From 65f8d369f445e7ffcd0a6995fe0b9a6528308b38 Mon Sep 17 00:00:00 2001 From: Sebastian Vater Date: Mon, 7 Jul 2025 15:47:48 +0200 Subject: [SERVER] iscsi: Initial commit, WIP --- src/server/CMakeLists.txt | 2 + src/server/iscsi.c | 1631 +++++++++++++++++++++++++ src/server/iscsi.h | 2956 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 4589 insertions(+) create mode 100644 src/server/iscsi.c create mode 100644 src/server/iscsi.h (limited to 'src') diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 9a1e1c4..c66ace4 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -49,6 +49,7 @@ set(DNBD3_SERVER_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/altservers.c ${CMAKE_CURRENT_SOURCE_DIR}/image.c ${CMAKE_CURRENT_SOURCE_DIR}/ini.c ${CMAKE_CURRENT_SOURCE_DIR}/integrity.c + ${CMAKE_CURRENT_SOURCE_DIR}/iscsi.c ${CMAKE_CURRENT_SOURCE_DIR}/locks.c ${CMAKE_CURRENT_SOURCE_DIR}/net.c ${CMAKE_CURRENT_SOURCE_DIR}/reference.c @@ -65,6 +66,7 @@ set(DNBD3_SERVER_HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/altservers.h ${CMAKE_CURRENT_SOURCE_DIR}/image.h ${CMAKE_CURRENT_SOURCE_DIR}/ini.h ${CMAKE_CURRENT_SOURCE_DIR}/integrity.h + ${CMAKE_CURRENT_SOURCE_DIR}/iscsi.h ${CMAKE_CURRENT_SOURCE_DIR}/locks.h ${CMAKE_CURRENT_SOURCE_DIR}/net.h ${CMAKE_CURRENT_SOURCE_DIR}/reference.h diff --git a/src/server/iscsi.c b/src/server/iscsi.c new file mode 100644 index 0000000..2dc85cc --- /dev/null +++ b/src/server/iscsi.c @@ -0,0 +1,1631 @@ + /* + * This file is part of the Distributed Network Block Device 3 + * + * Copyright(c) 2025 Sebastian Vater + * + * This file may be licensed under the terms of the + * GNU General Public License Version 2 (the ``GPL''). + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the GPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the GPL along with this + * program. If not, go to http://www.gnu.org/licenses/gpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "iscsi.h" + +/** + * @file iscsi.c + * @author Sebastian Vater + * @date 16 Jul 2025 + * @brief iSCSI implementation for DNBD3. + * + * This file contains the iSCSI implementation according to + * RFC7143 for dnbd3-server. + * All server-side network sending and client-side network + * receiving code is done here. + * @see https://www.rfc-editor.org/rfc/rfc7143 + */ + +/** + * Merges multiple strings using printf style formatting + * and allocates memory for holding the result. + * + * @param[in] buf Buffer to merge into. + * @param[in] format printf style format string. + * @param[in] args Values to fill in the format with. + * @return New buffer which holds the final result. + */ +uint8_t *iscsi_vsprintf_append_realloc(char *buf, const char *format, va_list args) +{ + va_list args_copy; + uint orig_size = 0; + + if ( buf != NULL ) + orig_size = strlen( (char *) buf ); + + va_copy( args_copy, args ); + uint new_size = vsnprintf( NULL, 0, format, args_copy ); + va_end( args_copy ); + + new_size += orig_size + 1; + + uint8_t *new_buf = realloc( buf, new_size ); + + if ( new_buf == NULL ) { + logadd( LOG_ERROR, "iscsi_vsprintf_append_realloc: Out of memory while allocating string buffer" ); + + return NULL; + } + + vsnprintf( (char *) new_buf + orig_size, (new_size - orig_size), format, args ); + + return new_buf; +} + +/** + * Merges strings using printf style formatting and allocates + * memory for holding the result. + * + * @param[in] buf Buffer to merge into. + * @param[in] format printf style format string. + * @param[in] ... Values to fill in the format string with. + * @return New buffer which holds the final result. + */ +uint8_t *iscsi_sprintf_append_realloc(char *buf, const char *format, ...) +{ + va_list args; + + va_start( args, format ); + uint8_t *ret_buf = iscsi_vsprintf_append_realloc( buf, format, args ); + va_end( args ); + + return ret_buf; +} + +/** + * Merges strings using printf style formatting and allocates + * memory for holding the result. + * + * @param[in] format printf style format string. + * @param[in] args Values to fill in the format with. + * @return New buffer which holds the final result. + */ +uint8_t *iscsi_vsprintf_alloc(const char *format, va_list args) +{ + return iscsi_vsprintf_append_realloc( NULL, format, args ); +} + +/** + * Allocates a buffer large enough to hold printf style + * string concatenation and fills it using vspnrintf. + * + * @param[in] format Format string to allocate and fill. + * @param[in] ... Values to fill in the format string with. + * @return New buffer containing the formatted string. + */ +uint8_t *iscsi_sprintf_alloc(const char *format, ... ) +{ + va_list args; + + va_start( args, format ); + uint8_t *buf = iscsi_vsprintf_alloc( format, args ); + va_end( args ); + + return buf; +} + +/** + * Creates a ultra hardcore speed optimized empty hash map and + * allocates enough buckets to hold default capacity elements. + * 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. + * 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. + * @return A pointer to the hash map structure or NULL in case of an error. + */ +iscsi_hashmap *iscsi_hashmap_create(const uint capacity) +{ + iscsi_hashmap *map = (iscsi_hashmap *) malloc( sizeof(iscsi_hashmap) ); + + if ( map == NULL ) { + logadd( LOG_ERROR, "iscsi_hashmap_create: Out of memory while allocating iSCSI hash map" ); + + return map; + } + + if ( capacity > 0UL ) { + uint new_capacity = (capacity + 1); // 1UL << (lg(capacity - 1) + 1) + + 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); + + map->capacity = ++new_capacity; // Round up actual new capacity to nearest power of two + } else { + map->capacity = ISCSI_HASHMAP_DEFAULT_CAPACITY; + } + + map->buckets = (iscsi_hashmap_bucket *) calloc( map->capacity, sizeof(struct iscsi_hashmap_bucket) ); + + if ( map->buckets == NULL ) { + free( map ); + + logadd( LOG_ERROR, "iscsi_hashmap_create: Out of memory while allocating iSCSI hash map buckets" ); + + return NULL; + } + + map->cap_load = (map->capacity * 3UL) >> 2UL; // 75% of capacity + map->count = 0; + map->removed_count = 0; + map->first = NULL; + map->last = (iscsi_hashmap_bucket *) &map->first; + + return map; +} + +/** + * 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. + */ +void iscsi_hashmap_destroy(iscsi_hashmap *map) +{ + if ( map != NULL ) { + if ( map->buckets != NULL ) + free( map->buckets ); + + free( map ); + } +} + +/** + * Puts an old bucket into a resized hash map. + * + * @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. + */ +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 - 1); + + for ( ;; ) { + iscsi_hashmap_bucket *entry = &map->buckets[index]; + + if ( entry->key == NULL ) { + *entry = *old_entry; + + return entry; + } + + index = (index + 1) & (map->capacity - 1); + } +} + +/** + * 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. + * + * @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. + */ +static int iscsi_hashmap_resize(iscsi_hashmap *map) +{ + 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; + + return -1; + } + + map->cap_load = (map->capacity * 3UL) >> 2UL; // 75% of capacity + map->last = (iscsi_hashmap_bucket *) &map->first; + map->count -= map->removed_count; + map->removed_count = 0; + + do { + iscsi_hashmap_bucket *current = map->last->next; + + if ( current->key == NULL ) { + map->last->next = current->next; + + continue; + } + + map->last->next = iscsi_hashmap_resize_entry(map, map->last->next); + map->last = map->last->next; + } while ( map->last->next != NULL ); + + free( old_buckets ); + + return 0; +} + +/** + * Calculates the hash code of data with a specified length. + * + * @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. + */ +static inline uint32_t iscsi_hashmap_hash_data(const uint8_t *data, const size_t len) +{ + 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; + } while ( --num_blocks > 0UL ); + + return (uint32_t) (hash ^ hash >> 32ULL); +} + +/** + * Finds a bucket by key of a specified hash map by + * key, key size and hash code. This function may + * only be called if the bucket is guaranteed to + * be found, otherwise this function hangs, so be + * careful. + * + * @param[in] map Pointer to hash map where the key to be + * searched for is located, may NOT be NULL, so be careful. + * @param[in] key Pointer to key. NULL is invalid, so be + * careful. + * @param[in] key_size Number of bytes for the key. + * @param[in] hash Hash of the key to be searched for. + * @return Pointer to found bucket. + */ +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); + + 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)) ) + return entry; + + index = (index + 1) & (map->capacity - 1); + } +} + +/** + * Creates a key from data and size and ensures + * its requirements for usage in hash map buckets. + * Currently keys to be used in a hash map bucket + * require a size of multiple by 8 bytes with + * the zero padding. + * + * @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). + */ +uint8_t *iscsi_hashmap_key_create(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; + } + + memcpy( key, data, len ); + memset( key + len, 0, (key_size - len) ); // Zero pad additional bytes in case length is not a multiple of 8 + + return 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 ); +} + +/** + * 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, MUST + * be a multiple of 8 bytes which is NOT checked, so + * be careful. + * @param[in] value Value of the key, NULL is allowed. + * @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 ); + + iscsi_hashmap_key_destroy( key ); + + return 0L; +} + +/** + * Adds a key / value pair to a specified hash map + * bucket list, if it doesn't exist already. The + * buckets are resized automatically if required. + * This function neither does make a copy of the key, + * 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. + * + * @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] value Value of the key to add, NULL is + * allowed. + * @retval -1 Adding key / value pair would have required + * hash map resizing which failed (probably due to + * memory exhaustion). + * @retval 0 Key / value pair was added successfully. + */ +int iscsi_hashmap_put(iscsi_hashmap *map, const uint8_t *key, const size_t key_size, uint8_t *value) +{ + if ( ((map->count + 1) > map->cap_load) && (iscsi_hashmap_resize( map ) < 0) ) + return -1; + + const uint32_t hash = iscsi_hashmap_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++; + + entry->key = key; + entry->key_size = key_size; + entry->hash = hash; + } + + entry->value = value; + + return 0; +} + +/** + * Adds a key / value pair if it doesn't exist + * using the value of `*out_in_val`. If the pair + * does exist, value will be set in `*out_in`, + * meaning the value of the pair will be in + * '*out_in` regardless of whether or not it it + * existed in the first place. + * The buckets are resized automatically if required. + * This function neither does make a copy of the key, + * 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. + * + * @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,out] out_in_value Value of the key to add, + * NULL is allowed. + * @retval -1 Adding key / value pair would have required + * hash map resizing which failed (probably due to + * memory exhaustion). + * @retval 0 Key / value pair was added successfully. + * @retval 1 Key already existed. + */ +int iscsi_hashmap_get_put(iscsi_hashmap *map, const 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) ) + return -1; + + const uint32_t hash = iscsi_hashmap_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++; + + entry->value = *out_in_value; + entry->key = key; + entry->key_size = key_size; + entry->hash = hash; + + return 0; + } + + *out_in_value = entry->value; + + return 1; +} + +/** + * Adds a key / value pair to a specified hash map + * bucket list. If the key already exists, it will + * be overwritten and a callback function will be + * invoked in order to allow, e.g. deallocation of + * resources, if necessary. The buckets are resized + * automatically if required. This function neither + * does make a copy of the key, 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. + * + * @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] value Value of the key to add, NULL is + * allowed. + * @param[in] callback Callback function which allows, + * for example, a dallocation of resources for the + * overwritten key and value pair. The function is + * invoked just before overwriting the old values. + * This may NOT be NULL, so take caution. + * @param[in] user_data Pointer to user specific data + * passed to the callback function in case more + * information is needed. + * @return -1 in case adding key / value pair would + * have required hash map resizing which failed + * (probably due to memory exhaustion), 0 if the + * Key / value pair was added successfully and + * the callback function also returned 0, otherwise + * the return value got by the callbuck function. + */ +int iscsi_hashmap_put_free(iscsi_hashmap *map, const 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) ) + return -1; + + const uint32_t hash = iscsi_hashmap_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++; + + entry->key = key; + entry->key_size = key_size; + entry->hash = hash; + entry->value = value; + + return 0; + } + + int err = callback( entry->key, key_size, entry->value, user_data ); + + entry->key = key; + entry->value = value; + + return err; +} + +/** + * 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 + * 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. + */ +int 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 ); + iscsi_hashmap_bucket *entry = iscsi_hashmap_find_entry( map, key, key_size, hash ); + + return entry->key != NULL; +} + +/** + * 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. + * @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[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 + * in the 'out_value' parameter. + * @retval FALSE 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 ); + iscsi_hashmap_bucket *entry = iscsi_hashmap_find_entry( map, key, key_size, hash ); + + *out_value = entry->value; + + return entry->key != 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. + * + * @param[in] map Pointer to the hash map to remove from + * 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. + */ +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 ); + iscsi_hashmap_bucket *entry = iscsi_hashmap_find_entry( map, key, key_size, hash ); + + if ( entry->key != NULL ) { + entry->key = NULL; + entry->value = NULL; + + map->removed_count++; + } +} + +/** + * 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. 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. + * @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] 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. + * @param[in] user_data Pointer to user specific data + * 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 ); + iscsi_hashmap_bucket *entry = iscsi_hashmap_find_entry( map, key, key_size, hash ); + + if ( entry->key != NULL ) { + callback( entry->key, entry->key_size, entry->value, user_data ); + + entry->key = NULL; + entry->value = NULL; + + map->removed_count++; + } +} + +/** + * Returns the number of elements stored in the specified + * hash map. Elements marked for removal are not included. + * + * @param[in] map Pointer to the hash map to count the + * 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. + */ +int iscsi_hashmap_size(iscsi_hashmap *map) +{ + return (map->count - map->removed_count); +} + +/** + * 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. + * + * @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. + * @param[in] 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. + */ +int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, uint8_t *user_data) +{ + iscsi_hashmap_bucket *current = map->first; + int err = 0; + + while ( current != NULL ) { + if ( current->key != NULL ) { + err = callback( current->key, current->key_size, current->value, user_data ); + + if ( err < 0 ) + break; + } + + current = current->next; + } + + return err; +} + +/** + * 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" ); + + return bhs_pkt; + } + + 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) ); + + return bhs_pkt; +} + +/** + * Deallocates all aquired resources by iscsi_create_packet. + * + * @param[in] packet_data Pointer to packet data to deallocate. If this is + * NULL, this function does nothing. + */ +void iscsi_destroy_packet(iscsi_bhs_packet *packet_data) +{ + if (packet_data != NULL) + free( packet_data ); +} + +/** + * 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! + * 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 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. + */ +iscsi_bhs_packet *iscsi_append_ahs_packet(iscsi_bhs_packet *packet_data, const uint32_t ahs_len) +{ + if ( packet_data == NULL ) { + packet_data = iscsi_create_packet(); + + if ( packet_data == NULL ) + return packet_data; + } + + 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 + iscsi_align(ahs_len, ISCSI_ALIGN_SIZE); + + if ( new_pkt_size > (sizeof(struct iscsi_bhs_packet) + 1020UL) ) { + logadd( LOG_ERROR, "iscsi_append_ahs_packet: Total numer of AHS packets exceeds 255 DWORDs" ); + + return NULL; + } + + packet_data = (iscsi_bhs_packet *) realloc( packet_data, new_pkt_size ); + + if ( packet_data == NULL ) { + logadd( LOG_ERROR, "iscsi_append_ahs_packet: Out of memory while allocating iSCSI AHS packet data for appending" ); + + return packet_data; + } + + iscsi_ahs_packet *ahs_pkt = (iscsi_ahs_packet *) ((uint8_t *) packet_data + old_pkt_size); + ahs_pkt->len = iscsi_get_be16(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 += (ahs_len + (ISCSI_ALIGN_SIZE - 1)) >> 2UL; + + return packet_data; +} + +/** + * Gets the total number of AHS packets. + * + * @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. + */ +int iscsi_get_ahs_packets(const iscsi_bhs_packet *packet_data) +{ + if ( packet_data == NULL ) + return -1; + else if ( packet_data->total_ahs_len == 0 ) + return 0; + + 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; + + 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); + + ahs_len -= len; + ahs_pkt = ((uint8_t *) ahs_pkt) + (len - offsetof(struct iscsi_ahs_packet, data)); // Advance pointer to next AHS packet + count++; + } + + return count; +} + +/** + * Gets the pointer of an AHS packet by specified index. + * + * @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. + */ +iscsi_ahs_packet *iscsi_get_ahs_packet(const iscsi_bhs_packet *packet_data, const int index) +{ + if ( packet_data == NULL || (packet_data->total_ahs_len == 0) ) + return NULL; + + 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; + + while ( (int32_t) ahs_len > 0L ) { + if ( count-- < 0L ) + return ahs_pkt; + + 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); + + ahs_len -= len; + ahs_pkt = ((uint8_t *) ahs_pkt) + (len - offsetof(struct iscsi_ahs_packet, data)); // Advance pointer to next AHS packet + } + + logadd( LOG_ERROR, "iscsi_get_ahs_packet: Specified index for AHS packet does not exist" ); + + return NULL; +} + +/** + * Constructs and appends DataSegment (DS) 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! + * This function currently erases an already available DataSegment and + * throws away any data beyond DS. + * + * @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; + + if ( packet_data == NULL ) { + packet_data = iscsi_create_packet(); + + if ( packet_data == NULL ) + return packet_data; + } + + 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 = old_pkt_size + header_digest_size + iscsi_align(ds_len, ISCSI_ALIGN_SIZE) + data_digest_size; + + packet_data = (iscsi_bhs_packet *) realloc( packet_data, new_pkt_size ); + + if ( packet_data == NULL ) { + logadd( LOG_ERROR, "iscsi_append_ds_packet: Out of memory while allocating iSCSI DS packet data for appending" ); + + return packet_data; + } + + iscsi_put_be24( packet_data->ds_len, ds_len ); + memset( ((uint8_t *) packet_data) + old_pkt_size, 0, (new_pkt_size - old_pkt_size) ); + + return packet_data; +} + +static const uint32_t crc32c_lut[] = { // Created with a polynomial reflect value of 0x82F63B78 + 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}; + +/** + * Calculates CRC32C with 0x82F63B78 polynomial reflect according to iSCSI specs + * 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; +} + +/** + * 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; + + iscsi_put_be32( hdr_digest, crc32c ); +} + +/** + * 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; + + return iscsi_get_be32(pkt_crc32c) == crc32c; +} + +/** + * Calculates data digest (CRC32) with 0x82F63B78 polynomial reflect + * of a whole DataSegment (CRC32C) according to the iSCSI specs. + * 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; + + iscsi_put_be32( data_digest, crc32c ); +} + +/** + * 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; + + return iscsi_get_be32(pkt_crc32c) == crc32c; +} + +/** + * Checks whether packet data is an iSCSI packet or not. + * 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. + * + * @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. + */ +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) +{ + 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; + + 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 ( 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; + + const uint8_t opcode = ISCSI_GET_OPCODE(packet_data->opcode); + + 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 + + const iscsi_nop_out_packet *nop_out_pkt = (const iscsi_nop_out_packet *) packet_data; + + if ( (*(uint16_t *) nop_out_pkt->reserved != 0) || (nop_out_pkt->reserved[2] != 0) || (nop_out_pkt->reserved2[0] != 0) || (nop_out_pkt->reserved2[1] != 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_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 + + const iscsi_scsi_cmd_packet *scsi_cmd_pkt = (const iscsi_scsi_cmd_packet *) packet_data; + + 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 + + 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 + + const iscsi_task_mgmt_func_req_packet *task_mgmt_func_req_pkt = (const iscsi_task_mgmt_func_req_packet *) packet_data; + + if ( (task_mgmt_func_req_pkt->reserved != 0) || (task_mgmt_func_req_pkt->reserved2 != 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_CLIENT_LOGIN_REQ : { + if ( (packet_data->opcode != (opcode | 0x40)) || (ahs_len != 0) || (ds_len != 0) ) + return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bit 6 always MUST be set, AHS and DataSegment 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; + + 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 ( (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; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + + break; + } + case ISCSI_CLIENT_TEXT_REQ : { + if ( ((int8_t) packet_data->opcode < 0) || (ds_len == 0) ) + return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // MSB always MUST be cleared for this opcode and DataSegment is mandatory -> invalid iSCSI packet data + + const iscsi_text_req_packet *text_req_pkt = (const iscsi_text_req_packet *) packet_data; + + if ( (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; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + + break; + } + case ISCSI_CLIENT_SCSI_DATA_OUT : { + 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 + + const iscsi_scsi_data_out_req_packet *scsi_data_out_req_pkt = (const iscsi_scsi_data_out_req_packet *) packet_data; + + 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_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 + + const iscsi_logout_req_packet *logout_req_pkt = (const iscsi_logout_req_packet *) packet_data; + + if ( (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; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + + 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 + + const iscsi_snack_req_packet *snack_req_pkt = (const iscsi_snack_req_packet *) packet_data; + + if ( (snack_req_pkt->reserved != 0) || (snack_req_pkt->reserved2 != 0) || (snack_req_pkt->reserved3 != 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_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 + + const iscsi_nop_in_packet *nop_in_pkt = (const iscsi_nop_in_packet *) packet_data; + + if ( (*(uint16_t *) nop_in_pkt->reserved != 0) || (nop_in_pkt->reserved[2] != 0) || (nop_in_pkt->reserved2[0] != 0) || (nop_in_pkt->reserved2[1] != 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_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_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 + + const iscsi_task_mgmt_func_response_packet *task_mgmt_func_response_pkt = (const iscsi_task_mgmt_func_response_packet *) packet_data; + + if ( (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; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + + break; + } + case ISCSI_SERVER_LOGIN_RES : { + 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 -> invalid iSCSI packet data and DataSegment is mandatory + + const iscsi_login_response_packet *login_response_pkt = (const iscsi_login_response_packet *) packet_data; + + 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; + + if ( (login_response_pkt->reserved != 0) || (login_response_pkt->reserved2 != 0) || (login_response_pkt->reserved3 != 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_SERVER_TEXT_RES : { + 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 + + const iscsi_text_response_packet *text_response_pkt = (const iscsi_text_response_packet *) packet_data; + + if ( (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; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + + break; + } + case ISCSI_SERVER_SCSI_DATA_IN : { + 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 + + const iscsi_scsi_data_in_response_packet *scsi_data_in_pkt = (const iscsi_scsi_data_in_response_packet *) packet_data; + + 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_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 + + const iscsi_logout_response_packet *logout_response_pkt = (const iscsi_logout_response_packet *) packet_data; + + if ( (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; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + + 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 + + const iscsi_r2t_packet *r2t_pkt = (const iscsi_r2t_packet *) packet_data; + + if ( r2t_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_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 + + const iscsi_async_msg_packet *async_msg_pkt = (const iscsi_async_msg_packet *) packet_data; + + if ( (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; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + + break; + } + case ISCSI_SERVER_VENDOR_CODE1 : + case ISCSI_SERVER_VENDOR_CODE2 : + case ISCSI_SERVER_VENDOR_CODE3 : { + 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 + + const iscsi_reject_packet *reject_pkt = (const iscsi_reject_packet *) packet_data; + + if ( (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; // Reserved fields need all to be zero, but are NOT, tag always MUST be 0xFFFFFFFFUL -> invalid iSCSI packet data + + break; + } + default : { + return ISCSI_VALIDATE_PACKET_RESULT_ERROR_INVALID_OPCODE; + + 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 +} + +/** + * Parses and extracts a specific key and value pair out of an iSCSI packet + * 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. + * 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. + * @param[in] len Length of the remaining packet data. + * @return Number of bytes used by the extracted 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. + */ +static int iscsi_parse_text_key_value_pair(iscsi_hashmap *pairs, const uint8_t *packet_data, const uint32_t len) +{ + const uint key_val_len = strnlen( packet_data, len ); + const uint8_t *key_end = memchr( packet_data, '=', key_val_len ); + + if ( key_end == NULL ) { + logadd( LOG_ERROR, "iscsi_parse_text_key_value_pair: Key / value separator '=' not found" ); + + return -1L; + } + + const uint key_len = (key_end - packet_data); + + if ( key_len == 0 ) { + logadd( LOG_ERROR, "iscsi_parse_text_key_value_pair: Empty key found which is NOT allowed according to iSCSI specs" ); + + return -1L; + } + + 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; + } + + const uint hash_key_len = (key_len + 1UL); + uint8_t *hash_key = iscsi_hashmap_key_create( packet_data, hash_key_len ); + + if ( hash_key == NULL ) + return -1L; + + hash_key[key_len] = '\0'; + + if ( iscsi_hashmap_contains( 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; + } + + const uint val_len = strnlen( key_end + 1UL, key_val_len - key_len - 1UL ); + const uint max_len = (strcmp( hash_key, "CHAP_C" ) == 0) || (strcmp( 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; + } + + const uint8_t *hash_val = (uint8_t *) malloc( val_len + 1UL ); + + 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; + } + + memcpy( hash_val, key_end + 1, val_len ); + + const int rc = iscsi_hashmap_put( pairs, hash_key, hash_key_len, hash_val ); + + if ( rc < 0 ) + return -1L; + + return hash_key_len + val_len + 1UL; // Number of bytes for processed key / value pair (+1 for '=' and NUL terminator) +} + +/** + * Parses and extracts all key and value pairs out of iSCSI packet + * 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 + * 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. + * @param[in] len Length of the remaining packet data. + * @param[in] cbit Non-zero value of C bit was set in previously. + * @param[in] partial_pairs Array of partial pair pointers in + * case C bit was set (multiple iSCSI packets for text data). + * @retval -1 An error occured during parsing key. + * @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) +{ + if ( len == 0 ) + return 0L; // 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; + + for (key_val_pair_len = 0; (key_val_pair_len < len) && packet_data[key_val_pair_len] != '\0'; key_val_pair_len++) { + } + + const uint8_t *tmp_partial_buf = iscsi_sprintf_alloc( "%s%s", *partial_pairs, (const char *) packet_data ); + + if ( tmp_partial_buf == NULL ) + return -1L; + + const int rc = iscsi_parse_text_key_value_pair( pairs, tmp_partial_buf, (key_val_pair_len + strlen( *partial_pairs )) ); + free( tmp_partial_buf ); + + if ( rc < 0 ) + return -1L; + + free( *partial_pairs ); + *partial_pairs = NULL; + + packet_data += (key_val_pair_len + 1); + len -= (key_val_pair_len + 1); + } + + if ( c_bit ) { // Strip partial parameters in case C bit was enabled previousley + if ( partial_pairs == NULL ) { + logadd( LOG_ERROR, "iscsi_parse_key_value_pairs: C bit set but missing partial parameter" ); + + return -1L; + } + + 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--) { + } + + if ( key_val_pair_len != 0 ) + key_val_pair_len++; // NUL char found, don't copy to target buffer' + + *partial_pairs = (uint8_t *) malloc ( (len - key_val_pair_len) + 1 ); + + if ( *partial_pairs == NULL ) { + logadd( LOG_ERROR, "iscsi_parse_key_value_pairs: Out of memory allocating partial parameter" ); + + return -1L; + } + + 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; + else + return 0L; + } + + int offset = 0L; + + while ( (offset < len) && (packet_data[offset] != '\0') ) { + const int rc = iscsi_parse_text_key_value_pair( pairs, (packet_data + offset), (len - offset) ); + + if ( rc < 0 ) + return -1L; + + offset += rc; + } + + return 0L; +} + +/** + * Creates a data structure for an incoming iSCSI connection request + * from iSCSI packet data. + * + * @param[in] login_req_pkt Pointer to iSCSI packet data to construct iSCSI + * connection request from. May be NULL in which case this function does + * nothing. + * @return Pointer to initialized iSCSI connection structure or NULL in + * case of an error (invalid iSCSI packet data or memory exhaustion). + */ +iscsi_connection *iscsi_connection_create(const iscsi_login_req_packet *login_req_pkt) +{ + iscsi_connection *conn = (iscsi_connection *) malloc( sizeof(struct iscsi_connection) ); + + if ( conn == NULL ) { + logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI connection" ); + + return NULL; + } + + conn->key_value_pairs = iscsi_hashmap_create( 0UL ); + + if ( conn->key_value_pairs == NULL ) { + logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI text key / value pair hash map" ); + + free( conn ); + + return NULL; + } + + conn->header_digest = 0L; + conn->data_digest = 0L; + + conn->isid.a = login_req_pkt->isid.a; + conn->isid.b = iscsi_get_be16(login_req_pkt->isid.b); + conn->isid.c = login_req_pkt->isid.c; + conn->isid.d = iscsi_get_be16(login_req_pkt->isid.d); + + conn->tsih = login_req_pkt->tsih; // Identifier, no need for endianess conversion + conn->init_task_tag = login_req_pkt->init_task_tag; // Identifier, no need for endianess conversion + conn->cid = login_req_pkt->cid; // Identifier, no need for endianess conversion + conn->cmd_sn = iscsi_get_be32(login_req_pkt->cmd_sn); + conn->exp_stat_sn = iscsi_get_be32(login_req_pkt->exp_stat_sn); + + return conn; +} + +/** + * Deallocates a data structure of an iSCSI connection + * request and all allocated hash maps which don't + * require closing of external resources like closing + * TCP/IP socket connections. + * + * @param[in] conn Pointer to iSCSI connection structure to be + * deallocated, TCP/IP connections are NOT closed by this + * function, use iscsi_connection_close for this. This may be + * NULL in which case this function does nothing. + */ +void iscsi_connection_destroy(iscsi_connection *conn) +{ + if ( conn != 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 ); + + conn->key_value_pairs = NULL; + } + + free( conn ); + } +} + +/** + * Callback function for deallocation of an iSCSI + * connection stored in the hash map managing all + * iSCSI connections. + * + * @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. + * @return Always returns 0a as this function cannot fail. + */ +int iscsi_connection_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) +{ + iscsi_connection_destroy( (iscsi_connection *) value ); + iscsi_hashmap_key_destroy( key ); + + return 0L; +} diff --git a/src/server/iscsi.h b/src/server/iscsi.h new file mode 100644 index 0000000..f013a8f --- /dev/null +++ b/src/server/iscsi.h @@ -0,0 +1,2956 @@ +/* + * This file is part of the Distributed Network Block Device 3 + * + * Copyright(c) 2011-2012 Johann Latocha + * + * This file may be licensed under the terms of the + * GNU General Public License Version 2 (the ``GPL''). + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the GPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the GPL along with this + * program. If not, go to http://www.gnu.org/licenses/gpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef DNBD3_ISCSI_H_ +#define DNBD3_ISCSI_H_ + +#include + +#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) +#define iscsi_get_be24(x) ((x) & 0xFFFFFFUL) +#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) +{ + (*(uint16_t *) data) = val; +} + +static inline void iscsi_put_be24(uint8_t *data, const uint32_t val) +{ + (*(uint16_t *) data) = (uint16_t) (val >> 8U); + data[2] = (uint8_t) val; +} + +static inline void iscsi_put_be32(uint8_t *data, const uint32_t val) +{ + (*(uint32_t *) *data) = val; +} + +static inline void iscsi_put_be64(uint8_t *data, const uint64_t val) +{ + (*(uint64_t *) data) = val; +} +#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__) +// GCC or CLang +#define iscsi_get_be16(x) (__builtin_bswap16(x)) +#define iscsi_get_be24(x) (iscsi_get_be32(x) & 0xFFFFFFUL) +#define iscsi_get_be32(x) (__builtin_bswap32(x)) +#define iscsi_get_be64(x) (__builtin_bswap64(x)) +#elif defined(_MSC_VER) +#include +// MVSC +#define iscsi_get_be16(x) (_byteswap_ushort(x)) +#define iscsi_get_be32(x) (_byteswap_ulong(x)) +#define iscsi_get_be64(x) (_byteswap_uint64(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)) +#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) +{ + data[0] = (uint8_t) (val >> 8U); + data[1] = (uint8_t) val; +} + +static inline void iscsi_put_be24(uint8_t *data, const uint32_t val) +{ + data[0] = (uint8_t) (val >> 16UL); + data[1] = (uint8_t) (val >> 8UL); + data[2] = (uint8_t) val; +} + +static inline void iscsi_put_be32(uint8_t *data, const uint32_t val) +{ + data[0] = (uint8_t) (val >> 24UL); + data[1] = (uint8_t) (val >> 16UL); + data[2] = (uint8_t) (val >> 8UL); + data[3] = (uint8_t) val; +} + +static inline void iscsi_put_be64(uint8_t *data, const uint64_t val) +{ + 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; +} +#else +#error "Unknown CPU endianness" +#endif + +// Align a value so that it's evenly divisable by n +#define iscsi_align(x, n) (((x) + (n) - 1) & ~((n) - 1)) + +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 + +#define ISCSI_HASHMAP_DEFAULT_CAPACITY_SHIFT 5UL // Shift factor for default capacity +#define ISCSI_HASHMAP_DEFAULT_CAPACITY (1UL << (ISCSI_HASHMAP_DEFAULT_CAPACITY_SHIFT)) // Default capacity is 32 buckets +#define ISCSI_HASHMAP_RESIZE_SHIFT 1UL // Number of bits to shift left when resizing +#define ISCSI_HASHMAP_KEY_ALIGN_SHIFT 3UL // Key data shift value for alignment enforcement +#define ISCSI_HASHMAP_KEY_ALIGN (1UL << (ISCSI_HASHMAP_KEY_ALIGN_SHIFT)) // Key data size must be multiple of 8 bytes by now +#define ISCSI_HASHMAP_HASH_INITIAL 0x811C9DC5UL // Initial hash code +#define ISCSI_HASHMAP_HASH_MUL 0xBF58476D1CE4E5B9ULL // Value to multiply hash code with + +typedef struct iscsi_hashmap_bucket { + struct iscsi_hashmap_bucket *next; // Must be first element + + uint8_t *key; // Data used as key, zero padding + size_t key_size; // Size of key, must be a multiple of 8 bytes + uint32_t hash; // Hash code + uint8_t *value; // Value +} iscsi_hashmap_bucket; + +typedef struct iscsi_hashmap { + iscsi_hashmap_bucket *buckets; // Hashmap buckets + uint capacity; // Current capacity in elements + uint cap_load; // Capacity load threshold before next resize + uint count; // Number of buckets + uint removed_count; // Number of removed buckets + iscsi_hashmap_bucket *first; // First bucket of linked list + iscsi_hashmap_bucket *last; // Last bucket, allows faster traversion +} iscsi_hashmap; + +/** + * Callback function. This is a pointer to a + * function for various purposes like iterating + * through a hash map. It is also used for replacing + * already existing keys or for key removal. + * + * @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. + * @return A negative result indicates as fatal error, + * 0 means successful operation and a positive value + * indicates a non-fatal error or a warning. + */ +typedef int (*iscsi_hashmap_callback)(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // A Callback for iterating over map, freeing and removing entries. + // user_data is free for personal use + +iscsi_hashmap *hashmap_create(const uint capacity); // Creates an empty hash map with default capacity specified as above +void iscsi_hashmap_destroy(iscsi_hashmap *map); // Deallocates the hash map objects and buckets, not elements. + // 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_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, const 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, const 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, const 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_get(iscsi_hashmap *map, const uint8_t *key, const size_t key_size, uint8_t **out_val); // 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. +int iscsi_hashmap_size(iscsi_hashmap *map); // Retrieves the number of elements of the hash map, ignoring elements marked for removal +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). */ + +#define ISCSI_BHS_SIZE 48UL // iSCSI Basic Header Segment size +#define ISCSI_DIGEST_SIZE 4UL // iSCSI header and data digest size (CRC32C) +#define ISCSI_ALIGN_SIZE 4UL // iSCSI packet data alignment (BHS, AHS and DS) + +#define ISCSI_VERSION_MIN 0 // Current minimum iSCSI protocol version supported by this implementation +#define ISCSI_VERSION_MAX 0 // Current maximum iSCSI protocol version supported by this implementation + +// CRC32C constants for header and data digest +#define ISCSI_CRC32C_INITIAL 0xFFFFFFFFUL +#define ISCSI_CRC32C_XOR 0xFFFFFFFFUL + +// iSCSI initiator (client) command opcodes +#define ISCSI_CLIENT_NOP_OUT 0x00 // NOP-Out +#define ISCSI_CLIENT_SCSI_CMD 0x01 // SCSI Command (encapsulates a SCSI Command Descriptor Block) +#define ISCSI_CLIENT_TASK_FUNC_REQ 0x02 // SCSI Task Management Function Request +#define ISCSI_CLIENT_LOGIN_REQ 0x03 // Login Request +#define ISCSI_CLIENT_TEXT_REQ 0x04 // Text Request +#define ISCSI_CLIENT_SCSI_DATA_OUT 0x05 // SCSI Data-Out (for write operations) +#define ISCSI_CLIENT_LOGOUT_REQ 0x06 // Logout Request +#define ISCSI_CLIENT_SNACK_REQ 0x10 // SNACK Request +#define ISCSI_CLIENT_VENDOR_CODE1 0x1C // Vendor-specific code #1 +#define ISCSI_CLIENT_VENDOR_CODE2 0x1D // Vendor-specific code #2 +#define ISCSI_CLIENT_VENDOR_CODE3 0x1E // Vendor-specific code #3 + +#define ISCSI_CLIENT_FIRST_OPCODE 0x00 // First client opcode value +#define ISCSI_CLIENT_LAST_OPCODE 0x1F // Last client opcode value + +// iSCSI target (server) command opcodes +#define ISCSI_SERVER_NOP_IN 0x20 // NOP-In +#define ISCSI_SERVER_SCSI_RESPONSE 0x21 // SCSI Response - contains SCSI status and possibly sense information or other response information +#define ISCSI_SERVER_TASK_FUNC_RES 0x22 // SCSI Task Management Function Response +#define ISCSI_SERVER_LOGIN_RES 0x23 // Login Response +#define ISCSI_SERVER_TEXT_RES 0x24 // Text Response +#define ISCSI_SERVER_SCSI_DATA_IN 0x25 // SCSI Data-In (for read operations) +#define ISCSI_SERVER_LOGOUT_RES 0x26 // Logout Response +#define ISCSI_SERVER_READY_XFER 0x31 // Ready To Transfer (R2T) - sent by target when it is ready to receive data +#define ISCSI_SERVER_ASYNC_MSG 0x32 // Asynchronous Message - sent by target to indicate certain special conditions +#define ISCSI_SERVER_VENDOR_CODE1 0x3C // Vendor-specific code #1 +#define ISCSI_SERVER_VENDOR_CODE2 0x3D // Vendor-specific code #2 +#define ISCSI_SERVER_VENDOR_CODE3 0x3E // Vendor-specific code #3 +#define ISCSI_SERVER_REJECT 0x3F // Reject + +#define ISCSI_SERVER_FIRST_OPCODE 0x20 // First client opcode value +#define ISCSI_SERVER_LAST_OPCODE 0x3F // Last client opcode value + +#define ISCSI_OPCODE_MASK 0x3F // ISCSI opcode bit mask (bits 0-5 used) +#define ISCSI_GET_OPCODE(x) ((x) & ISCSI_OPCODE_MASK) // Funky macro to get iSCSI packet opcode + +typedef struct __attribute__((packed)) iscsi_bhs_packet { + uint8_t opcode; // Command opcode (see above) + uint8_t opcode_fields[3]; // Opcode-specific fields + uint8_t total_ahs_len; // Total AHS length + uint8_t ds_len[3]; // Data segment length + union { + uint64_t lun; // LUN bitmask + uint8_t opcode_spec[8]; // Opcode-specific fields + } lun_opcode; + uint32_t init_task_tag; // Initiator task tag + uint8_t opcode_spec_fields[28]; +} iscsi_bhs_packet; + +#define ISCSI_AHS_TYPE_EXT_CDB_PACKET 0x01 // Command Descriptor Block (CDB) +#define ISCSI_AHS_TYPE_BIDI_READ_EXP_XFER_AHS_PACKET 0x02 + +typedef struct __attribute__((packed)) iscsi_ahs_packet { + uint16_t len; // AHSLength + uint8_t type; // AHSType + uint8_t specific; // AHS-Specific + uint8_t data[0]; // AHS-Specific data +} iscsi_ahs_packet; + +/* 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; + +/* This type of AHS MUST NOT be used if the CDBLength is less than 17. + The length includes the reserved byte 3. +*/ +typedef struct __attribute__((packed)) iscsi_ext_cdb_ahs_packet { + uint16_t len; // AHSLength - (CDBLength - 15) + uint8_t type; // Identifier (always 0x01 according to specs) + uint8_t reserved; // Reserved for future usage + uint8_t data[0]; // ExtendedCDB +} iscsi_ext_cdb_ahs_packet; + +typedef struct __attribute__((packed)) iscsi_bidi_read_exp_xfer_ahs_packet { + uint16_t len; // Always 0x0005 according to specs + uint8_t type; // Identifier (always 0x02 according to specs) + uint8_t reserved; // Reserved for future usage + uint32_t bidi_read_exp_xfer_len; // Bidirectional Read Expected Data Transfer Length +} iscsi_bidi_read_exp_xfer_ahs_packet; + +/* Certain iSCSI conditions result in the command being terminated at + the target (response code of Command Completed at Target) with a SCSI + CHECK CONDITION Status as outlined in the following definitions + (Sense key: Aborted Command 0x0B): +*/ +#define ISCSI_DS_ERROR_UNEXPECTED_UNSOLICITED_DATA_ASC 0x0C // Unexpected unsolicited data +#define ISCSI_DS_ERROR_UNEXPECTED_UNSOLICITED_DATA_ASCQ 0x0C + +#define ISCSI_DS_ERROR_INCORRECT_AMOUNT_OF_DATA_ASC 0x0C // Incorrect amount of data +#define ISCSI_DS_ERROR_INCORRECT_AMOUNT_OF_DATA_ASCQ 0x0D + +#define ISCSI_DS_ERROR_PROTOCOL_SERVICE_CRC_ERROR_ASC 0x47 // Protocol Service CRC error +#define ISCSI_DS_ERROR_PROTOCOL_SERVICE_CRC_ERROR_ASCQ 0x05 + +#define ISCSI_DS_ERROR_SNACK_REJECTED_ASC 0x11 // SNACK rejected +#define ISCSI_DS_ERROR_SNACK_REJECTED_ASCQ 0x13 + +/* Optional header and data digests protect the integrity of the header + and data, respectively. The digests, if present, are located, + respectively, after the header and PDU-specific data and cover, + respectively, the header and the PDU data, each including the padding + bytes, if any. + + The existence and type of digests are negotiated during the Login + Phase. +*/ +typedef struct __attribute__((packed)) iscsi_header_digest { + uint32_t crc32c; // Header digest is a CRC32C for ensuring integrity +} iscsi_header_digest; + +typedef struct __attribute__((packed)) iscsi_data_digest { + uint32_t crc32c; // Data digest is a CRC32C for ensuring integrity +} iscsi_data_digest; + +/* iSCSI targets MUST support and enable Autosense. If Status is CHECK + CONDITION (0x02), then the data segment MUST contain sense data for + the failed command. + + For some iSCSI responses, the response data segment MAY contain some + 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 { + uint16_t len; // SenseLength - This field indicates the length of Sense Data. + uint8_t sense_data[0]; // The Sense Data contains detailed information about a CHECK CONDITION. + // SPC3 specifies the format and content of the Sense Data. + uint8_t res_data[0]; // Response Data +} iscsi_ds_cmd_data; + +// SCSI command opcodes (embedded in iSCSI protocol) +#define SCSI_OPCODE_TESTUNITREADY 0x00 // TEST UNIT READY +#define SCSI_OPCODE_READ6 0x08 // READ(6) +#define SCSI_OPCODE_INQUIRY 0x12 // INQUIRY +#define SCSI_OPCODE_MODESELECT6 0x15 // MODE SELECT(6) +#define SCSI_OPCODE_RESERVE6 0x16 // RESERVE(6) +#define SCSI_OPCODE_RELEASE6 0x17 // RELEASE(6) +#define SCSI_OPCODE_MODESENSE6 0x1A // MODE SENSE(6) +#define SCSI_OPCODE_STARTSTOPUNIT 0x1B // START STOP UNIT +#define SCSI_OPCODE_PREVENTALLOW 0x1E // PREVENT ALLOW MEDIUM REMOVAL +#define SCSI_OPCODE_READCAPACITY10 0x25 // READ CAPACITY(10) +#define SCSI_OPCODE_READ10 0x28 // READ(10) +#define SCSI_OPCODE_WRITE10 0x2A // WRITE(10) +#define SCSI_OPCODE_WRITE_VERIFY10 0x2E // WRITE AND VERIFY(10) +#define SCSI_OPCODE_VERIFY10 0x2F // VERIFY(10) +#define SCSI_OPCODE_PREFETCH10 0x34 // PRE-FETCH(10) +#define SCSI_OPCODE_SYNCHRONIZECACHE10 0x35 // SYNCHRONIZE CACHE(10) +#define SCSI_OPCODE_READ_DEFECT_DATA10 0x37 // READ DEFECT DATA(10) +#define SCSI_OPCODE_WRITE_SAME10 0x41 // WRITE SAME(10) +#define SCSI_OPCODE_UNMAP 0x42 // UNMAP +#define SCSI_OPCODE_READTOC 0x43 // READ TOC/PMA/ATIP +#define SCSI_OPCODE_SANITIZE 0x48 // SANITIZE +#define SCSI_OPCODE_MODESELECT10 0x55 // MODE SELECT(10) +#define SCSI_OPCODE_MODESENSE10 0x5A // MODE SENSE(10) +#define SCSI_OPCODE_PERSISTENT_RESERVE_IN 0x5E // PERSISTENT RESERVE IN +#define SCSI_OPCODE_PERSISTENT_RESERVE_OUT 0x5F // PERSISTENT RESERVE OUT +#define SCSI_OPCODE_EXTENDED_COPY 0x83 // Third-party Copy OUT +#define SCSI_OPCODE_RECEIVE_COPY_RESULTS 0x84 // Third-party Copy IN +#define SCSI_OPCODE_READ16 0x88 // READ(16) +#define SCSI_OPCODE_COMPARE_AND_WRITE 0x89 // COMPARE AND WRITE +#define SCSI_OPCODE_WRITE16 0x8A // WRITE(16) +#define SCSI_OPCODE_ORWRITE 0x8B // ORWRITE +#define SCSI_OPCODE_WRITE_VERIFY16 0x8E // WRITE AND VERIFY(16) +#define SCSI_OPCODE_VERIFY16 0x8F // VERIFY(16) +#define SCSI_OPCODE_PREFETCH16 0x90 // PRE-FETCH(16) +#define SCSI_OPCODE_SYNCHRONIZECACHE16 0x91 // SYNCHRONIZE CACHE(16) +#define SCSI_OPCODE_WRITE_SAME16 0x93 // WRITE SAME(16) +#define SCSI_OPCODE_WRITE_ATOMIC16 0x9C // WRITE ATOMIC(16) +#define SCSI_OPCODE_SERVICE_ACTION_IN 0x9E // SERVICE ACTION IN(16) +#define SCSI_OPCODE_REPORTLUNS 0xA0 // REPORT LUNS +#define SCSI_OPCODE_MAINTENANCE_IN 0xA3 // MAINTENANCE IN +#define SCSI_OPCODE_READ12 0xA8 // READ(12) +#define SCSI_OPCODE_WRITE12 0xAA // WRITE(12) +#define SCSI_OPCODE_WRITE_VERIFY12 0xAE // WRITE AND VERIFY(12) +#define SCSI_OPCODE_VERIFY12 0xAF // VERIFY(12) +#define SCSI_OPCODE_READ_DEFECT_DATA12 0xB7 // READ DEFECT DATA(12) + +#define ISCSI_SCSI_CMD_FLAGS_TASK_NO_UNSOLICITED_DATA (1 << 7) // (F) is set to 1 when no unsolicited SCSI Data-Out PDUs + // follow this PDU. When F = 1 for a write and if Expected + // Data Transfer Length is larger than the + // DataSegmentLength, the target may solicit additional data + // through R2T. +#define ISCSI_SCSI_CMD_FLAGS_TASK_READ (1 << 6) // (R) is set to 1 when the command is expected to input data +#define ISCSI_SCSI_CMD_FLAGS_TASK_WRITE (1 << 5) // (W) is set to 1 when the command is expected to output data + +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_UNTAGGED 0x0 // Untagged task attribute +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_SIMPLE 0x1 // Simple task attribute +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_ORDERED 0x2 // Ordered task attribute +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_HEAD_QUEUE 0x3 // Head of queue task attribute +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_ACA 0x4 // ACA task attribute +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_RESERVED_1 0x5 // ACA task attribute +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_RESERVED_2 0x6 // ACA task attribute +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_RESERVED_3 0x7 // ACA task attribute + +/* Flags and Task Attributes: + At least one of the W and F bits MUST be set to 1. + Either or both of R and W MAY be 1 when the Expected Data Transfer + Length and/or the Bidirectional Read Expected Data Transfer Length + are 0, but they MUST NOT both be 0 when the Expected Data Transfer + Length and/or Bidirectional Read Expected Data Transfer Length are + not 0 (i.e., when some data transfer is expected, the transfer + direction is indicated by the R and/or W bit). +*/ + +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_MASK 0x7 // Task Attributes (ATTR) are encoded in the first three LSBs' + +typedef struct __attribute__((packed)) iscsi_scsi_cmd_packet { + uint8_t opcode; // Always 0x01 according to specification (see above) + int8_t flags_task; // Flags and Task Attributes + uint16_t reserved; // Reserved for future usage + uint8_t total_ahs_len; // Total AHS length + uint8_t ds_len[3]; // Data segment length + uint64_t lun; // LUN bitmask + uint32_t init_task_tag; // Initiator task tag + uint32_t exp_xfer_len; // Expected Data Transfer Length + // For unidirectional operations, the Expected Data Transfer Length + // field contains the number of bytes of data involved in this SCSI + // operation. For a unidirectional write operation (W flag set to 1 and + // R flag set to 0), the initiator uses this field to specify the number + // of bytes of data it expects to transfer for this operation. For a + // unidirectional read operation (W flag set to 0 and R flag set to 1), + // the initiator uses this field to specify the number of bytes of data + // it expects the target to transfer to the initiator. It corresponds + // to the SAM-2 byte count. + // For bidirectional operations (both R and W flags are set to 1), this + // field contains the number of data bytes involved in the write + // transfer. For bidirectional operations, an additional header segment + // MUST be present in the header sequence that indicates the + // Bidirectional Read Expected Data Transfer Length. The Expected Data + // Transfer Length field and the Bidirectional Read Expected Data + // Transfer Length field correspond to the SAM-2 byte count. + // If the Expected Data Transfer Length for a write and the length of + // the immediate data part that follows the command (if any) are the + // same, then no more data PDUs are expected to follow. In this case, + // the F bit MUST be set to 1. + // If the Expected Data Transfer Length is higher than the + // FirstBurstLength (the negotiated maximum amount of unsolicited data + // the target will accept), the initiator MUST send the maximum amount + // of unsolicited data OR ONLY the immediate data, if any. + // Upon completion of a data transfer, the target informs the initiator + // (through residual counts) of how many bytes were actually processed + // (sent and/or received) by the target. + uint32_t cmd_sn; // The CmdSN enables ordered delivery across multiple connections in a single session + uint32_t exp_stat_sn; // Command responses up to ExpStatSN - 1 (modulo 2**32) have been + // received (acknowledges status) on the connection. + struct iscsi_cdb scsi_cdb; // SCSI Command Descriptor Block (CDB) + // 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. + struct iscsi_ahs_packet ahs; // Optional AHS packet data + struct iscsi_header_digest hdr_digest; // Optional header digest + struct iscsi_ds_cmd_data ds_cmd_data; // Optional data segment, command data + struct iscsi_data_digest data_digest; // Optional data digest +} iscsi_scsi_cmd_packet; + +#define ISCSI_SCSI_RESPONSE_FLAGS_RES_UNDERFLOW (1 << 1) // (U) set for Residual Underflow. In this case, the Residual + // Count indicates the number of bytes that were not + // transferred out of the number of bytes that were expected + // to be transferred. For a bidirectional operation, the + // Residual Count contains the residual for the write + // operation. + +#define ISCSI_SCSI_RESPONSE_FLAGS_RES_OVERFLOW (1 << 2) // (O) set for Residual Overflow. In this case, the Residual + // Count indicates the number of bytes that were not + // transferred because the initiator's Expected Data + // Transfer Length was not sufficient. For a bidirectional + // operation, the Residual Count contains the residual for + // the write operation. + +#define ISCSI_SCSI_RESPONSE_FLAGS_BIDI_READ_RES_UNDERFLOW (1 << 3) // (u) set for Bidirectional Read Residual Underflow. In this + // case, the Bidirectional Read Residual Count indicates the + // number of bytes that were not transferred to the + // initiator out of the number of bytes expected to be + // transferred. + +#define ISCSI_SCSI_RESPONSE_FLAGS_BIDI_READ_RES_OVERFLOW (1 << 4) // (o) set for Bidirectional Read Residual Overflow. In this + // case, the Bidirectional Read Residual Count indicates the + // number of bytes that were not transferred to the + // initiator because the initiator's Bidirectional Read + // Expected Data Transfer Length was not sufficient. + +/* Bits O and U and bits o and u are mutually exclusive (i.e., having + both o and u or O and U set to 1 is a protocol error). + + For a response other than "Command Completed at Target", bits 3-6 + MUST be 0. +*/ + +#define ISCSI_SCSI_RESPONSE_STATUS_GOOD 0x00 +#define ISCSI_SCSI_RESPONSE_STATUS_CHECK_COND 0x02 +#define ISCSI_SCSI_RESPONSE_STATUS_BUSY 0x08 +#define ISCSI_SCSI_RESPONSE_STATUS_RES_CONFLICT 0x18 +#define ISCSI_SCSI_RESPONSE_STATUS_TASK_SET_FULL 0x28 +#define ISCSI_SCSI_RESPONSE_STATUS_ACA_ACTIVE 0x30 +#define ISCSI_SCSI_RESPONSE_STATUS_TASK_ABORTED 0x40 + +/* The Status field is used to report the SCSI status of the command (as + specified in SAM2) and is only valid if the response code is + Command Completed at Target. + + If a SCSI device error is detected while data from the initiator is + still expected (the command PDU did not contain all the data and the + target has not received a data PDU with the Final bit set), the + target MUST wait until it receives a data PDU with the F bit set in + the last expected sequence before sending the Response PDU. +*/ + +#define ISCSI_SCSI_RESPONSE_CODE_OK 0x00 // Command Completed at Target +#define ISCSI_SCSI_RESPONSE_CODE_FAIL 0x01 // Target Failure +#define ISCSI_SCSI_RESPONSE_CODE_VENDOR_FIRST 0x80 // First vendor specific response code +#define ISCSI_SCSI_RESPONSE_CODE_VENDOR_LAST 0xFF // Last vendor specific response code + +/* The Response field is used to report a service response. The mapping + of the response code into a SCSI service response code value, if + needed, is outside the scope of this document. However, in symbolic + terms, response value 0x00 maps to the SCSI service response (see +*/ +typedef struct __attribute__((packed)) iscsi_scsi_response_packet { + uint8_t opcode; // Always 0x21 according to specification (see above) + int8_t flags; // Flags (see above) + uint8_t response; // This field contains the iSCSI service response. + uint8_t status; // The Status field is used to report the SCSI status of the command (as + // specified in SAM2) and is only valid if the response code is + // Command Completed at Target. See above for codes. + uint8_t total_ahs_len; // Total AHS length + uint8_t ds_len[3]; // Data segment length + uint64_t reserved; // Reserved for future usage + uint32_t init_task_tag; // Initiator task tag + uint32_t snack_tag; // This field contains a copy of the SNACK Tag of the last SNACK Tag + // accepted by the target on the same connection and for the command for + // which the response is issued. Otherwise, it is reserved and should + // be set to 0. + // After issuing a R-Data SNACK, the initiator must discard any SCSI + // status unless contained in a SCSI Response PDU carrying the same + // SNACK Tag as the last issued R-Data SNACK for the SCSI command on the + // current connection. + uint32_t stat_sn; // StatSN - Status Sequence Number + // The StatSN is a sequence number that the target iSCSI layer generates + // per connection and that in turn enables the initiator to acknowledge + // status reception. The StatSN is incremented by 1 for every + // response/status sent on a connection, except for responses sent as a + // result of a retry or SNACK. In the case of responses sent due to a + // retransmission request, the StatSN MUST be the same as the first time + // the PDU was sent, unless the connection has since been restarted. + uint32_t exp_cmd_sn; // ExpCmdSN - Next Expected CmdSN from This Initiator + // The ExpCmdSN is a sequence number that the target iSCSI returns to + // the initiator to acknowledge command reception. It is used to update + // a local variable with the same name. An ExpCmdSN equal to + // MaxCmdSN + 1 indicates that the target cannot accept new commands. + uint32_t max_cmd_sn; // MaxCmdSN - Maximum CmdSN from This Initiator + // The MaxCmdSN is a sequence number that the target iSCSI returns to + // the initiator to indicate the maximum CmdSN the initiator can send. + // It is used to update a local variable with the same name. If the + // MaxCmdSN is equal to ExpCmdSN - 1, this indicates to the initiator + // that the target cannot receive any additional commands. When the + // MaxCmdSN changes at the target while the target has no pending PDUs + // to convey this information to the initiator, it MUST generate a + // NOP-In to carry the new MaxCmdSN. + uint32_t exp_data_sn; // ExpDataSN or Reserved + // This field indicates the number of Data-In (read) PDUs the target has + // sent for the command. + // This field MUST be 0 if the response code is not Command Completed at + // Target or the target sent no Data-In PDUs for the command. + uint32_t bidi_read_res_cnt; // Bidirectional Read Residual Count or Reserved + // The Bidirectional Read Residual Count field MUST be valid in the case + // where either the u bit or the o bit is set. If neither bit is set, + // the Bidirectional Read Residual Count field is reserved. Targets may + // set the Bidirectional Read Residual Count, and initiators may use it + // when the response code is Command Completed at Target. If the o bit + // is set, the Bidirectional Read Residual Count indicates the number of + // bytes that were not transferred to the initiator because the + // initiator's Bidirectional Read Expected Data Transfer Length was not + // sufficient. If the u bit is set, the Bidirectional Read Residual + // Count indicates the number of bytes that were not transferred to the + // initiator out of the number of bytes expected to be transferred. + uint32_t res_cnt; // Residual Count or Reserved + // The Residual Count field MUST be valid in the case where either the U + // bit or the O bit is set. If neither bit is set, the Residual Count + // field MUST be ignored on reception and SHOULD be set to 0 when + // sending. Targets may set the residual count, and initiators may use + // it when the response code is Command Completed at Target (even if the + // status returned is not GOOD). If the O bit is set, the Residual + // Count indicates the number of bytes that were not transferred because + // the initiator's Expected Data Transfer Length was not sufficient. If + // the U bit is set, the Residual Count indicates the number of bytes + // that were not transferred out of the number of bytes expected to be + // transferred. + struct iscsi_header_digest hdr_digest; // Optional header digest + struct iscsi_ds_cmd_data ds_cmd_data; // Optional data segment, command data + struct iscsi_data_digest data_digest; // Optional data digest +} iscsi_scsi_response_packet; + +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_ABORT_TASK 0x01 // ABORT TASK - aborts the task identified by the Referenced Task Tag field +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_ABORT_TASK_SET 0x02 // ABORT TASK SET - aborts all tasks issued via this session on the LU +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_CLEAR_ACA 0x03 // CLEAR ACA - clears the Auto Contingent Allegiance condition +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_CLEAR_TASK_SET 0x04 // CLEAR TASK SET - aborts all tasks in the appropriate task set + // as defined by the TST field in the Control mode page + // (see SPC3) +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_LOGICAL_UNIT_RESET 0x05 // LOGICAL UNIT RESET +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_TARGET_WARM_RESET 0x06 // TARGET WARM RESET +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_TARGET_COLD_RESET 0x07 // TARGET COLD RESET +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_TASK_REASSIGN 0x08 // TASK REASSIGN - reassigns connection allegiance for the task + // identified by the Initiator Task Tag field to this connection, + // thus resuming the iSCSI exchanges for the task + +typedef struct __attribute__((packed)) iscsi_task_mgmt_func_req_packet { + uint8_t opcode; // Always 0x02 according to specification (see above) + uint8_t func; // Function. + // The task management functions provide an initiator with a way to + // explicitly control the execution of one or more tasks (SCSI and iSCSI + // tasks). The task management function codes are listed below. For a + // more detailed description of SCSI task management, see SAM2. + uint16_t reserved; // Reserved for future usage + uint8_t total_ahs_len; // TotalAHSLength (MUST be 0 for this PDU) + uint8_t ds_len[3]; // DataSegmentLength (MUST be 0 for this PDU) + uint64_t lun; // Logical Unit Number (LUN) or Reserved + // This field is required for functions that address a specific LU + // (ABORT TASK, CLEAR TASK SET, ABORT TASK SET, CLEAR ACA, LOGICAL UNIT + // RESET) and is reserved in all others + uint32_t init_task_tag; // Initiator task tag + // This is the Initiator Task Tag of the task to be aborted for the + // ABORT TASK function or reassigned for the TASK REASSIGN function. + // For all the other functions, this field MUST be set to the reserved + // value 0xFFFFFFFF + uint32_t ref_task_tag; // Referenced task tag or 0xFFFFFFFF + uint32_t cmd_sn; // CmdSN + uint32_t exp_stat_sn; // ExpStatSN + uint32_t ref_cmd_sn; // RefCmdSN or Reserved + // If an ABORT TASK is issued for a task created by an immediate + // command, then the RefCmdSN MUST be that of the task management + // request itself (i.e., the CmdSN and RefCmdSN are equal). + // For an ABORT TASK of a task created by a non-immediate command, the + // RefCmdSN MUST be set to the CmdSN of the task identified by the + // Referenced Task Tag field. Targets must use this field when the task + // identified by the Referenced Task Tag field is not with the target. + // Otherwise, this field is reserved + uint32_t exp_data_sn; // ExpDataSN or Reserved + // For recovery purposes, the iSCSI target and initiator maintain a data + // acknowledgment reference number - the first input DataSN number + // unacknowledged by the initiator. When issuing a new command, this + // number is set to 0. If the function is TASK REASSIGN, which + // establishes a new connection allegiance for a previously issued read + // or bidirectional command, the ExpDataSN will contain an updated data + // acknowledgment reference number or the value 0; the latter indicates + // that the data acknowledgment reference number is unchanged. The + // initiator MUST discard any data PDUs from the previous execution that + // it did not acknowledge, and the target MUST transmit all Data-In PDUs + // (if any) starting with the data acknowledgment reference number. The + // number of retransmitted PDUs may or may not be the same as the + // original transmission, depending on if there was a change in + // MaxRecvDataSegmentLength in the reassignment. The target MAY also + // send no more Data-In PDUs if all data has been acknowledged. + // The value of ExpDataSN MUST be 0 or higher than the DataSN of the + // last acknowledged Data-In PDU, but not larger than DataSN + 1 of the + // last Data-IN PDU sent by the target. Any other value MUST be ignored + // by the target. + // For other functions, this field is reserved + uint64_t reserved2; // Reserved for future usage + struct iscsi_header_digest hdr_digest; // Optional header digest +} iscsi_task_mgmt_func_req_packet; + +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_COMPLETE 0x00 // Function complete +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_NO_EXIST 0x01 // Task does not exist +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_LUN_NO_EXIST 0x02 // LUN does not exist +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_ALLEGIANT 0x03 // Task still allegiant +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_UNSUPPORTED_ALLEGIANCE 0x04 // Task allegiance reassignment not supported +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_UNSUPPORTED_MGMT 0x05 // Task management function not supported +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_AUTH_FAILED 0x06 // Function authorization failed +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_REJECTED 0xFF // Function rejected + +/* For the functions ABORT TASK, ABORT TASK SET, CLEAR ACA, CLEAR TASK + SET, LOGICAL UNIT RESET, TARGET COLD RESET, TARGET WARM RESET, and + TASK REASSIGN, the target performs the requested task management + function and sends a task management response back to the initiator. + For TASK REASSIGN, the new connection allegiance MUST ONLY become + effective at the target after the target issues the task management + response. +*/ +typedef struct __attribute__((packed)) iscsi_task_mgmt_func_response_packet { + uint8_t opcode; // Always 0x22 according to specification (see above) + uint8_t flags; // Reserved for future usage + uint8_t response; // Function response (see above) + // For the TARGET COLD RESET and TARGET WARM RESET functions, the target + // cancels all pending operations across all LUs known to the issuing + // initiator. For the TARGET COLD RESET function, the target MUST then + // close all of its TCP connections to all initiators (terminates all + // sessions). + // The mapping of the response code into a SCSI service response code + // value, if needed, is outside the scope of this document. However, in + // symbolic terms, Response values 0 and 1 map to the SCSI service + // response of FUNCTION COMPLETE. Response value 2 maps to the SCSI + // service response of INCORRECT LOGICAL UNIT NUMBER. All other + // Response values map to the SCSI service response of FUNCTION + // REJECTED. If a Task Management Function Response PDU does not arrive + // before the session is terminated, the SCSI service response is + // SERVICE DELIVERY OR TARGET FAILURE. + // The response to ABORT TASK SET and CLEAR TASK SET MUST only be issued + // by the target after all of the commands affected have been received + // by the target, the corresponding task management functions have been + // executed by the SCSI target, and the delivery of all responses + // delivered until the task management function completion has been + // confirmed (acknowledged through the ExpStatSN) by the initiator on + // all connections of this session. + // For the ABORT TASK function, + // a) if the Referenced Task Tag identifies a valid task leading to a + // successful termination, then targets must return the "Function + // complete" response. + // b) if the Referenced Task Tag does not identify an existing task + // but the CmdSN indicated by the RefCmdSN field in the Task + // Management Function Request is within the valid CmdSN window + // and less than the CmdSN of the Task Management Function Request + // itself, then targets must consider the CmdSN as received and + // return the "Function complete" response. + // c) if the Referenced Task Tag does not identify an existing task + // and the CmdSN indicated by the RefCmdSN field in the Task + // Management Function Request is outside the valid CmdSN window, + // then targets must return the "Task does not exist" response + uint8_t reserved; // Reserved for future usage + uint8_t total_ahs_len; // TotalAHSLength (MUST be 0 for this PDU) + uint8_t ds_len[3]; // DataSegmentLength (MUST be 0 for this PDU) + uint64_t reserved2; // Reserved for future usage + uint32_t init_task_tag; // Initiator task tag + uint32_t reserved3; // Reserved for future usage + uint32_t stat_sn; // StatSN + uint32_t exp_cmd_sn; // ExpCmdSN + uint32_t max_cmd_sn; // MaxCmdSN + uint32_t reserved4; // Reserved for future usage + uint64_t reserved5; // Reserved for future usage + struct iscsi_header_digest hdr_digest; // Optional header digest +} iscsi_task_mgmt_func_response_packet; + +#define ISCSI_SCSI_DATA_OUT_DATA_IN_FLAGS_IMMEDIATE (1 << 7) // Immediately process transfer + +typedef struct __attribute__((packed)) iscsi_scsi_data_out_req_packet { + uint8_t opcode; // Always 0x02 according to specification (see above) + int8_t flags; // Flags (see above) + uint16_t reserved; // Reserved for future usage + uint8_t total_ahs_len; // TotalAHSLength (MUST be 0 for this PDU) + uint8_t ds_len[3]; // DataSegmentLength (MUST be 0 for this PDU) + uint64_t lun; // Logical Unit Number (LUN) or Reserved + uint32_t init_task_tag; // Initiator task tag + uint32_t target_xfer_tag; // Target transfer tag or 0xFFFFFFFF + uint32_t reserved2; // Reserved for future usage + uint32_t exp_stat_sn; // ExpStatSN + uint32_t reserved3; // Reserved for future usage + uint32_t data_sn; // DataSN + uint32_t buf_offset; // Buffer offset + uint32_t reserved4; // Reserved for future usage + struct iscsi_header_digest hdr_digest; // Optional header digest + struct iscsi_data_digest data_digest; // Optional data digest +} iscsi_scsi_data_out_req_packet; + +#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS (1 << 0) // (S) set to indicate that the Command Status field + // contains status. If this bit is set to 1, the + // F bit MUST also be set to 1 + +#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_UNDERFLOW (1 << 1) // (U) set for Residual Underflow. In this case, the Residual + // Count indicates the number of bytes that were not + // transferred out of the number of bytes that were expected + // to be transferred. For a bidirectional operation, the + // Residual Count contains the residual for the write + // operation. + +#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_OVERFLOW (1 << 2) // (O) set for Residual Overflow. In this case, the Residual + // Count indicates the number of bytes that were not + // transferred because the initiator's Expected Data + // Transfer Length was not sufficient. For a bidirectional + // operation, the Residual Count contains the residual for + // the write operation. + +#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_ACK (1 << 6) // (A) for sessions with ErrorRecoveryLevel=1 or higher, the target sets + // this bit to 1 to indicate that it requests a positive acknowledgment + // from the initiator for the data received. The target should use the + // A bit moderately; it MAY only set the A bit to 1 once every + // MaxBurstLength bytes, or on the last Data-In PDU that concludes the + // entire requested read data transfer for the task from the target's + // perspective, and it MUST NOT do so more frequently. The target MUST + // NOT set to 1 the A bit for sessions with ErrorRecoveryLevel=0. The + // initiator MUST ignore the A bit set to 1 for sessions with + // ErrorRecoveryLevel=0. + // On receiving a Data-In PDU with the A bit set to 1 on a session with + // ErrorRecoveryLevel greater than 0, if there are no holes in the read + // data until that Data-In PDU, the initiator MUST issue a SNACK of type + // DataACK, except when it is able to acknowledge the status for the + // task immediately via the ExpStatSN on other outbound PDUs if the + // status for the task is also received. In the latter case + // (acknowledgment through the ExpStatSN), sending a SNACK of type + // DataACK in response to the A bit is OPTIONAL, but if it is done, it + // must not be sent after the status acknowledgment through the + // ExpStatSN. If the initiator has detected holes in the read data + // prior to that Data-In PDU, it MUST postpone issuing the SNACK of type + // DataACK until the holes are filled. An initiator also MUST NOT + // acknowledge the status for the task before those holes are filled. A + // status acknowledgment for a task that generated the Data-In PDUs is + // considered by the target as an implicit acknowledgment of the Data-In + // PDUs if such an acknowledgment was requested by the target + +#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_FINAL (1 << 7) // (F) for outgoing data, this bit is 1 for the last PDU of unsolicited + // data or the last PDU of a sequence that answers an R2T. + // For incoming data, this bit is 1 for the last input (read) data PDU + // of a sequence. Input can be split into several sequences, each + // having its own F bit. Splitting the data stream into sequences does + // not affect DataSN counting on Data-In PDUs. It MAY be used as a + // "change direction" indication for bidirectional operations that need + // such a change. + // DataSegmentLength MUST NOT exceed MaxRecvDataSegmentLength for the + // direction it is sent, and the total of all the DataSegmentLength of + // all PDUs in a sequence MUST NOT exceed MaxBurstLength (or + // FirstBurstLength for unsolicited data). However, the number of + // individual PDUs in a sequence (or in total) may be higher than the + // ratio of MaxBurstLength (or FirstBurstLength) to + // MaxRecvDataSegmentLength (as PDUs may be limited in length by the + // capabilities of the sender). Using a DataSegmentLength of 0 may + // increase beyond what is reasonable for the number of PDUs and should + // therefore be avoided. + // For bidirectional operations, the F bit is 1 for both the end of the + // input sequences and the end of the output sequences + +typedef struct __attribute__((packed)) iscsi_scsi_data_in_response_packet { + uint8_t opcode; // Always 0x25 according to specification (see above) + int8_t flags; // Incoming data flags (see above) + // The fields StatSN, Status, and Residual Count only have meaningful + // content if the S bit is set to 1 + uint8_t reserved; // Rserved for future usage + uint8_t status; // Status or Reserved + // Status can accompany the last Data-In PDU if the command did not end + // with an exception (i.e., the status is "good status" - GOOD, + // CONDITION MET, or INTERMEDIATE-CONDITION MET). The presence of + // status (and of a residual count) is signaled via the S flag bit. + // Although targets MAY choose to send even non-exception status in + // separate responses, initiators MUST support non-exception status in + // Data-In PDUs + uint8_t total_ahs_len; // TotalAHSLength + uint8_t ds_len[3]; // DataSegmentLength + // This is the data payload length of a SCSI Data-In or SCSI Data-Out + // PDU. The sending of 0-length data segments should be avoided, but + // initiators and targets MUST be able to properly receive 0-length data + // segments. + // The data segments of Data-In and Data-Out PDUs SHOULD be filled to + // the integer number of 4-byte words (real payload), unless the F bit + // is set to 1 + uint64_t lun; // Logical Unit Number (LUN) or Reserved + // If the Target Transfer Tag isprovided, then the LUN field MUST hold a + // valid value and be consistent with whatever was specified with the command; + // otherwise, the LUN field is reserved + uint32_t init_task_tag; // Initiator task tag + uint32_t target_xfer_tag; // On outgoing data, the Target Transfer Tag is provided to the target + // if the transfer is honoring an R2T. In this case, the Target + // Transfer Tag field is a replica of the Target Transfer Tag provided + // with the R2T. + // On incoming data, the Target Transfer Tag and LUN MUST be provided by + // the target if the A bit is set to 1; otherwise, they are reserved. + // The Target Transfer Tag and LUN are copied by the initiator into the + // SNACK of type DataACK that it issues as a result of receiving a SCSI + // Data-In PDU with the A bit set to 1. + // The Target Transfer Tag values are not specified by this protocol, + // except that the value 0xFFFFFFFF is reserved and means that the + // Target Transfer Tag is not supplied + uint32_t stat_sn; // StatSN + uint32_t exp_cmd_sn; // ExpCmdSN + uint32_t max_cmd_sn; // MaxCmdSN + uint32_t data_sn; // DataSN + // For input (read) or bidirectional Data-In PDUs, the DataSN is the + // input PDU number within the data transfer for the command identified + // by the Initiator Task Tag. + // R2T and Data-In PDUs, in the context of bidirectional commands, share + // the numbering sequence. + // For output (write) data PDUs, the DataSN is the Data-Out PDU number + // within the current output sequence. Either the current output + // sequence is identified by the Initiator Task Tag (for unsolicited + // data) or it is a data sequence generated for one R2T (for data + // solicited through R2T) + uint32_t buf_offset; // Buffer Offset + // The Buffer Offset field contains the offset of this PDU payload data + // within the complete data transfer. The sum of the buffer offset and + // length should not exceed the expected transfer length for the + // command. + // The order of data PDUs within a sequence is determined by + // DataPDUInOrder. When set to Yes, it means that PDUs have to be in + // increasing buffer offset order and overlays are forbidden. + // The ordering between sequences is determined by DataSequenceInOrder. + // When set to Yes, it means that sequences have to be in increasing + // buffer offset order and overlays are forbidden + uint32_t res_cnt; // Residual Count or Reserved + struct iscsi_header_digest hdr_digest; // Optional header digest + struct iscsi_ds_cmd_data ds_cmd_data; // Data segment + struct iscsi_data_digest data_digest; // Optional data digest +} iscsi_scsi_data_in_response_packet; + +/* When an initiator has submitted a SCSI command with data that passes + from the initiator to the target (write), the target may specify + which blocks of data it is ready to receive. The target may request + that the data blocks be delivered in whichever order is convenient + for the target at that particular instant. This information is + passed from the target to the initiator in the Ready To Transfer + (R2T) PDU. + + In order to allow write operations without an explicit initial R2T, + the initiator and target MUST have negotiated the key InitialR2T to + No during login. + + An R2T MAY be answered with one or more SCSI Data-Out PDUs with a + matching Target Transfer Tag. If an R2T is answered with a single + Data-Out PDU, the buffer offset in the data PDU MUST be the same as + the one specified by the R2T, and the data length of the data PDU + MUST be the same as the Desired Data Transfer Length specified in the + R2T. If the R2T is answered with a sequence of data PDUs, the buffer + offset and length MUST be within the range of those specified by the + R2T, and the last PDU MUST have the F bit set to 1. If the last PDU + (marked with the F bit) is received before the Desired Data Transfer + Length is transferred, a target MAY choose to reject that PDU with + the "Protocol Error" reason code. DataPDUInOrder governs the + Data-Out PDU ordering. If DataPDUInOrder is set to Yes, the buffer + offsets and lengths for consecutive PDUs MUST form a continuous + non-overlapping range, and the PDUs MUST be sent in increasing offset + order. + + The target may send several R2T PDUs. It therefore can have a number + of pending data transfers. The number of outstanding R2T PDUs is + limited by the value of the negotiated key MaxOutstandingR2T. Within + a task, outstanding R2Ts MUST be fulfilled by the initiator in the + order in which they were received. + + R2T PDUs MAY also be used to recover Data-Out PDUs. Such an R2T + (Recovery-R2T) is generated by a target upon detecting the loss of + one or more Data-Out PDUs due to: + + - Digest error + + - Sequence error + + - Sequence reception timeout + + A Recovery-R2T carries the next unused R2TSN but requests part of or + the entire data burst that an earlier R2T (with a lower R2TSN) had + already requested. + + DataSequenceInOrder governs the buffer offset ordering in consecutive + R2Ts. If DataSequenceInOrder is Yes, then consecutive R2Ts MUST + refer to continuous non-overlapping ranges, except for Recovery-R2Ts. +*/ +typedef struct __attribute__((packed)) iscsi_r2t_packet { + uint8_t opcode; // Always 0x31 according to specification (see above) + uint8_t flags; // Reserved for future usage + uint16_t reserved; // Reserved for future usage + uint8_t total_ahs_len; // TotalAHSLength, MUST be 0 for this PDU + uint8_t ds_len[3]; // DataSegmentLength, MUST be 0 0 for this PDU + uint64_t lun; // Logical Unit Number (LUN) or Reserved + uint32_t init_task_tag; // Initiator task tag + uint32_t target_xfer_tag; // Target transfer tag + uint32_t stat_sn; // The StatSN field will contain the next StatSN. The StatSN for this + // connection is not advanced after this PDU is sent + uint32_t exp_cmd_sn; // ExpCmdSN + uint32_t max_cmd_sn; // MaxCmdSN + uint32_t data_sn; // DataSN + uint32_t r2t_sn; // R2TSN is the R2T PDU input PDU number within the command identified + // by the Initiator Task Tag. + // For bidirectional commands, R2T and Data-In PDUs share the input PDU + // numbering sequence + uint32_t buf_offset; // Buffer Offset + // The target therefore also specifies a buffer offset that + // indicates the point at which the data transfer should begin, relative + // to the beginning of the total data transfer + uint32_t des_data_xfer_len; // Desired Data Transfer Length + // The target specifies how many bytes it wants the initiator to send + // because of this R2T PDU. The target may request the data from the + // initiator in several chunks, not necessarily in the original order of + // the data. The Desired Data Transfer Length MUST NOT be 0 and MUST NOT + // exceed MaxBurstLength + struct iscsi_header_digest hdr_digest; // Optional header digest +} iscsi_r2t_packet; + +#define ISCSI_ASYNC_MSG_EVENT_SCSI_ASYNC_EVENT 0x00 // (SCSI Async Event) - a SCSI asynchronous event is reported in + // the sense data. Sense Data that accompanies the report, in + // the data segment, identifies the condition. The sending of a + // SCSI event ("asynchronous event reporting" in SCSI + // terminology) is dependent on the target support for SCSI + // asynchronous event reporting as indicated in the + // standard INQUIRY data. Its use may be enabled by + // parameters in the SCSI Control mode page +#define ISCSI_ASYNC_MSG_EVENT_LOGOUT_REQUEST 0x01 // (Logout Request) - the target requests Logout. This Async + // Message MUST be sent on the same connection as the one + // requesting to be logged out. The initiator MUST honor this + // request by issuing a Logout as early as possible but no later + // than Parameter3 seconds. The initiator MUST send a Logout + // with a reason code of "close the connection" OR "close the + // session" to close all the connections. Once this message is + // received, the initiator SHOULD NOT issue new iSCSI commands on + // the connection to be logged out. The target MAY reject any + // new I/O requests that it receives after this message with the + // reason code "Waiting for Logout". If the initiator does not + // log out in Parameter3 seconds, the target should send an Async + // PDU with iSCSI event code "Dropped the connection" if possible + // or simply terminate the transport connection. Parameter1 and + // Parameter2 are reserved +#define ISCSI_ASYNC_MSG_EVENT_CONNECT_DROP_NOTIFY 0x02 // (Connection Drop Notification) - the target indicates that it + // will drop the connection. + // The Parameter1 field indicates the CID of the connection that + // is going to be dropped. + // The Parameter2 field (Time2Wait) indicates, in seconds, the + // minimum time to wait before attempting to reconnect or + // reassign. + // The Parameter3 field (Time2Retain) indicates the maximum time + // allowed to reassign commands after the initial wait (in + // Parameter2). + // If the initiator does not attempt to reconnect and/or reassign + // the outstanding commands within the time specified by + // Parameter3, or if Parameter3 is 0, the target will terminate + // all outstanding commands on this connection. In this case, no + // other responses should be expected from the target for the + // outstanding commands on this connection. + // A value of 0 for Parameter2 indicates that reconnect can be + // attempted immediately +#define ISCSI_ASYNC_MSG_EVENT_SESSION_DROP_NOTIFY 0x03 // (Session Drop Notification) - the target indicates that it + // will drop all the connections of this session. + // The Parameter1 field is reserved. + // The Parameter2 field (Time2Wait) indicates, in seconds, the + // minimum time to wait before attempting to reconnect. + // The Parameter3 field (Time2Retain) indicates the maximum time + // allowed to reassign commands after the initial wait (in + // Parameter2). + // If the initiator does not attempt to reconnect and/or reassign + // the outstanding commands within the time specified by + // Parameter3, or if Parameter3 is 0, the session is terminated. + // In this case, the target will terminate all outstanding + // commands in this session; no other responses should be + // expected from the target for the outstanding commands in this + // session. A value of 0 for Parameter2 indicates that reconnect + // can be attempted immediately +#define ISCSI_ASYNC_MSG_EVENT_NEGOTIATION_REQUEST 0x04 // (Negotiation Request) - the target requests parameter + // negotiation on this connection. The initiator MUST honor this + // request by issuing a Text Request (that can be empty) on the + // same connection as early as possible, but no later than + // Parameter3 seconds, unless a Text Request is already pending + // on the connection, or by issuing a Logout Request. If the + // initiator does not issue a Text Request, the target may + // reissue the Asynchronous Message requesting parameter + // negotiation +#define ISCSI_ASYNC_MSG_EVENT_TASK_TERMINATION 0x05 // (Task Termination) - all active tasks for a LU with a matching + // LUN field in the Async Message PDU are being terminated. The + // receiving initiator iSCSI layer MUST respond to this message + // by taking the following steps, in order: + // - Stop Data-Out transfers on that connection for all active + // TTTs for the affected LUN quoted in the Async Message PDU. + // - Acknowledge the StatSN of the Async Message PDU via a + // NOP-Out PDU with ITT=0xFFFFFFFF (i.e., non-ping flavor), + // while copying the LUN field from the Async Message to + // NOP-Out. + // This value of AsyncEvent, however, MUST NOT be used on an + // iSCSI session unless the new TaskReporting text key was + // negotiated to FastAbort on the session +#define ISCSI_ASYNC_MSG_EVENT_VENDOR_FIRST 0xF8 // First vendor-specific iSCSI event. The AsyncVCode details the + // vendor code, and data MAY accompany the report +#define ISCSI_ASYNC_MSG_EVENT_VENDOR_LAST 0xFF // Last vendor-specific iSCSI event. The AsyncVCode details the + // vendor code, and data MAY accompany the report + +/* An Asynchronous Message may be sent from the target to the initiator + without corresponding to a particular command. The target specifies + the reason for the event and sense data. + Some Asynchronous Messages are strictly related to iSCSI, while + others are related to SCSI +*/ +typedef struct __attribute__((packed)) iscsi_async_msg_packet { + uint8_t opcode; // Always 0x32 according to specification (see above + uint8_t flags; // Reserved for future usage + uint16_t reserved; // Reserved for future usage + uint8_t total_ahs_len; // TotalAHSLength, MUST be 0 for this PDU + uint8_t ds_len[3]; // DataSegmentLength, MUST be 0 0 for this PDU + uint64_t lun; // The LUN field MUST be valid if AsyncEvent is 0. Otherwise, this + // field is reserved + uint32_t tag; // Tag (always 0xFFFFFFFF for now) + uint32_t reserved2; // Reserved for future usage + uint32_t stat_sn; // StatSN + // The StatSN counts this PDU as an acknowledgeable event (the StatSN is + // advanced), which allows for initiator and target state synchronization. + uint32_t exp_cmd_sn; // ExpCmdSN + uint32_t max_cmd_sn; // MaxCmdSN + uint8_t async_event; // AsyncEvent + uint8_t async_vcode; // AsyncVCode is a vendor-specific detail code that is only valid if the + // AsyncEvent field indicates a vendor-specific event. Otherwise, it is + // reserved + uint16_t param_1; // Parameter1 or Reserved + uint16_t param_2; // Parameter2 or Reserved + uint16_t param_3; // Parameter3 or Reserved + uint32_t reserved3; // Reserved for future usage + struct iscsi_header_digest hdr_digest; // Optional header digest + struct iscsi_ds_cmd_data ds_cmd_data; // Data segment + struct iscsi_data_digest data_digest; // Optional data digest +} iscsi_async_msg_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 { + uint16_t sense_len; // 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_data[0]; // Sense Data + uint16_t event_data[0]; // iSCSI Event Data +} iscsi_sense_event_data_packet; + +#define ISCSI_TEXT_REQ_FLAGS_CONTINUE (1 << 6) // (C) When set to 1, this bit indicates that the text (set of key=value + // pairs) in this Text Request is not complete (it will be continued on + // subsequent Text Requests); otherwise, it indicates that this Text + // Request ends a set of key=value pairs. A Text Request with the C bit + // set to 1 MUST have the F bit set to 0. +#define ISCSI_TEXT_REQ_FLAGS_FINAL (1 << 7) // (F) When set to 1, this bit indicates that this is the last or only Text + // Request in a sequence of Text Requests; otherwise, it indicates that + // more Text Requests will follow. + +/* The Text Request is provided to allow for the exchange of information + and for future extensions. It permits the initiator to inform a + target of its capabilities or request some special operations. + + An initiator MUST NOT have more than one outstanding Text Request on + a connection at any given time. + + On a connection failure, an initiator must either explicitly abort + any active allegiant text negotiation task or cause such a task to be + implicitly terminated by the target. +*/ +typedef struct __attribute__((packed)) iscsi_text_req_packet { + uint8_t opcode; // Always 0x04 according to specification (see above) + int8_t flags; // Text request flags (see above) + uint16_t reserved; // Reserved for future usage + uint8_t total_ahs_len; // TotalAHSLength + uint8_t ds_len[3]; // DataSegmentLength + uint64_t lun; // Logical Unit Number (LUN) or Reserved + uint32_t init_task_tag; // This is the initiator-assigned identifier for this Text Request. If + // the command is sent as part of a sequence of Text Requests and + // responses, the Initiator Task Tag MUST be the same for all the + // requests within the sequence (similar to linked SCSI commands). The + // I bit for all requests in a sequence also MUST be the same + uint32_t target_xfer_tag; // When the Target Transfer Tag is set to the reserved value 0xFFFFFFFF, + // it tells the target that this is a new request, and the target resets + // any internal state associated with the Initiator Task Tag (resets the + // current negotiation state). + // The target sets the Target Transfer Tag in a Text Response to a value + // other than the reserved value 0xFFFFFFFF whenever it indicates that + // it has more data to send or more operations to perform that are + // associated with the specified Initiator Task Tag. It MUST do so + // whenever it sets the F bit to 0 in the response. By copying the + // Target Transfer Tag from the response to the next Text Request, the + // initiator tells the target to continue the operation for the specific + // Initiator Task Tag. The initiator MUST ignore the Target Transfer + // Tag in the Text Response when the F bit is set to 1. + // This mechanism allows the initiator and target to transfer a large + // amount of textual data over a sequence of text-command/text-response + // exchanges or to perform extended negotiation sequences. + // If the Target Transfer Tag is not 0xFFFFFFFF, the LUN field MUST be + // sent by the target in the Text Response. + // A target MAY reset its internal negotiation state if an exchange is + // stalled by the initiator for a long time or if it is running out of + // resources. + // Long Text Responses are handled as shown in the following example: + // I->T Text SendTargets=All (F = 1, TTT = 0xFFFFFFFF) + // T->I Text (F = 0, TTT = 0x12345678) + // I->T Text (F = 1, TTT = 0x12345678) + // T->I Text (F = 0, TTT = 0x12345678) + // I->T Text (F = 1, TTT = 0x12345678) + // ... + // T->I Text (F = 1, TTT = 0xFFFFFFFF) + uint32_t cmd_sn; // CmdSN + uint32_t exp_stat_sn; // ExpStatSN + uint64_t reserved2[2]; // Reserved for future usage + struct iscsi_header_digest hdr_digest; // Optional header digest + struct iscsi_ds_cmd_data ds_cmd_data; // Data segment + // The data lengths of a Text Request MUST NOT exceed the iSCSI target + // MaxRecvDataSegmentLength (a parameter that is negotiated per + // connection and per direction). + // A key=value pair can span Text Request or Text Response boundaries. + // A key=value pair can start in one PDU and continue on the next. In + // other words, the end of a PDU does not necessarily signal the end of + // a key=value pair. + // The target responds by sending its response back to the initiator. + // The response text format is similar to the request text format. The + // Text Response MAY refer to key=value pairs presented in an earlier + // Text Request, and the text in the request may refer to earlier + // responses. + // Text operations are usually meant for parameter setting/negotiations + // but can also be used to perform some long-lasting operations + struct iscsi_data_digest data_digest; // Optional data digest +} iscsi_text_req_packet; + +#define ISCSI_TEXT_RESPONSE_FLAGS_CONTINUE (1 << 6) // (C) When set to 1, this bit indicates that the text (set of key=value + // pairs) in this Text Response is not complete (it will be continued on + // subsequent Text Responses); otherwise, it indicates that this Text + // Response ends a set of key=value pairs. A Text Response with the + // C bit set to 1 MUST have the F bit set to 0 +#define ISCSI_TEXT_RESPONSE_FLAGS_FINAL (1 << 7) // (F) When set to 1, in response to a Text Request with the Final bit set + // to 1, the F bit indicates that the target has finished the whole + // operation. Otherwise, if set to 0 in response to a Text Request with + // the Final Bit set to 1, it indicates that the target has more work to + // do (invites a follow-on Text Request). A Text Response with the + // F bit set to 1 in response to a Text Request with the F bit set to 0 + // is a protocol error. + // A Text Response with the F bit set to 1 MUST NOT contain key=value + // pairs that may require additional answers from the initiator. + // A Text Response with the F bit set to 1 MUST have a Target Transfer + // Tag field set to the reserved value 0xFFFFFFFF. + // A Text Response with the F bit set to 0 MUST have a Target Transfer + // Tag field set to a value other than the reserved value 0xFFFFFFFF + +/* The Text Response PDU contains the target's responses to the + initiator's Text Request. The format of the Text field matches that + of the Text Request. +*/ +typedef struct __attribute__((packed)) iscsi_text_response_packet { + uint8_t opcode; // Always 0x24 according to specification (see above) + int8_t flags; // Text response flags (see above) + uint16_t reserved; // Reserved for future usage + uint8_t total_ahs_len; // TotalAHSLength + uint8_t ds_len[3]; // DataSegmentLength + uint64_t lun; // Logical Unit Number (LUN) or Reserved + uint32_t init_task_tag; // The Initiator Task Tag matches the tag used in the initial Text Request + uint32_t target_xfer_tag; // When a target has more work to do (e.g., cannot transfer all the + // remaining text data in a single Text Response or has to continue the + // negotiation) and has enough resources to proceed, it MUST set the + // Target Transfer Tag to a value other than the reserved value + // 0xFFFFFFFF. Otherwise, the Target Transfer Tag MUST be set to + // 0xFFFFFFFF. + // When the Target Transfer Tag is not 0xFFFFFFFF, the LUN field may be + // significant. + // The initiator MUST copy the Target Transfer Tag and LUN in its next + // request to indicate that it wants the rest of the data. + // When the target receives a Text Request with the Target Transfer Tag + // set to the reserved value 0xFFFFFFFF, it resets its internal + // information (resets state) associated with the given Initiator Task + // Tag (restarts the negotiation). + // When a target cannot finish the operation in a single Text Response + // and does not have enough resources to continue, it rejects the Text + // Request with the appropriate Reject code. + // A target may reset its internal state associated with an Initiator + // Task Tag (the current negotiation state) as expressed through the + // Target Transfer Tag if the initiator fails to continue the exchange + // for some time. The target may reject subsequent Text Requests with + // the Target Transfer Tag set to the "stale" value + uint32_t stat_sn; // StatSN. The target StatSN variable is advanced by each Text Response sent + uint32_t exp_cmd_sn; // ExpCmdSN + uint32_t max_cmd_sn; // MaxCmdSN + uint64_t reserved2[2]; // Reserved for future usage + struct iscsi_header_digest hdr_digest; // Optional header digest + struct iscsi_ds_cmd_data ds_cmd_data; // Data segment + // The data lengths of a Text Response MUST NOT exceed the iSCSI + // initiator MaxRecvDataSegmentLength (a parameter that is negotiated + // per connection and per direction). + // The text in the Text Response Data is governed by the same rules as + // the text in the Text Request Data. + // Although the initiator is the requesting party and controls the + // request-response initiation and termination, the target can offer + // key=value pairs of its own as part of a sequence and not only in + // response to the initiator + struct iscsi_data_digest data_digest; // Optional data digest +} iscsi_text_response_packet; + +#define ISCSI_ISID_TYPE_BITS (1 << 6) // Two bits - The T field identifies the format and usage of A, B, C, and D as + // indicated below: +#define ISCSI_ISID_TYPE_FORMAT_OUI 0x0 // 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_EN 0x1 // EN: Format (IANA Enterprise Number) + // A: Reserved + // B and C: EN (IANA Enterprise Number) + // D: Qualifier +#define ISCSI_ISID_TYPE_FORMAT_RANDOM 0x2 // Random + // A: Reserved + // B and C: Random + // D: Qualifier + +/* Only the following keys are used during the SecurityNegotiation stage + of the Login Phase (other keys MUST NOT be used): +*/ +#define ISCSI_LOGIN_AUTH_TEXT_KEY_SESSION_TYPE "SessionType" // Use: LO, Declarative, Any-Stage + // Senders: Initiator + // Scope: SW + // SessionType= + // Default is Normal. + // The initiator indicates the type of session it wants to create. The + // target can either accept it or reject it. + // A Discovery session indicates to the target that the only purpose of + // this session is discovery. The only requests a target accepts in + // this type of session are a Text Request with a SendTargets key and a + // Logout Request with reason "close the session". + // The Discovery session implies MaxConnections = 1 and overrides both + // the default and an explicit setting. ErrorRecoveryLevel MUST be 0 + // (zero) for Discovery sessions. + // Depending on the type of session, a target may decide on resources to + // allocate, the security to enforce, etc., for the session. If the + // 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_TEXT_KEY_INITIATOR_NAME "InitiatorName" // Use: IO, Declarative, Any-Stage + // Senders: Initiator + // Scope: SW + // InitiatorName= + // Examples: + // InitiatorName=iqn.1992-04.de.uni-freiburg.bwlehrpool:qcow2.5003 + // InitiatorName=iqn.2001-02.de.uni-freiburg.matrix:basty.eduroam + // InitiatorName=naa.52004567BA64678D + // The initiator of the TCP connection MUST provide this key to the + // remote endpoint at the first login of the Login Phase for every + // connection. The InitiatorName key enables the initiator to identify + // itself to the remote endpoint. + // The InitiatorName MUST NOT be redeclared within the Login Phase +#define ISCSI_LOGIN_AUTH_TEXT_KEY_TARGET_NAME "TargetName" // Use: IO by initiator, FFPO by target - only as response to a + // SendTargets, Declarative, Any-Stage + // Senders: Initiator and target + // Scope: SW + // TargetName= + // Examples: + // TargetName=iqn.1993-11.de.uni-freiburg:diskarrays.sn.5003 + // TargetName=eui.020000023B040506 + // TargetName=naa.62004567BA64678D0123456789ABCDEF + // The initiator of the TCP connection MUST provide this key to the + // remote endpoint in the first Login Request if the initiator is not + // establishing a Discovery session. The iSCSI Target Name specifies + // the worldwide unique name of the target. + // The TargetName key may also be returned by the SendTargets Text + // Request (which is its only use when issued by a target). + // The TargetName MUST NOT be redeclared within the Login Phase +#define ISCSI_LOGIN_AUTH_TEXT_KEY_TARGET_ADDRESS "TargetAddress" // Use: ALL, Declarative, Any-Stage + // Senders: Target + // Scope: SW + // TargetAddress=domainname[:port][,portal-group-tag] + // The domainname can be specified as either a DNS host name, a dotted- + // decimal IPv4 address, or a bracketed IPv6 address as specified in + // RFC3986. + // If the TCP port is not specified, it is assumed to be the IANA- + // assigned default port for iSCSI. + // If the TargetAddress is returned as the result of a redirect status + // in a Login Response, the comma and portal-group-tag MUST be omitted. + // If the TargetAddress is returned within a SendTargets response, the + // portal-group-tag MUST be included. + // Examples: + // TargetAddress=10.0.0.1:5003,1 + // TargetAddress=[1080:0:0:0:8:800:200C:417A],65 + // TargetAddress=[1080::8:800:200C:417A]:5003,1 + // TargetAddress=gitlab.uni-freiburg.de,443 + // The formats for the port and portal-group-tag are the same as the one + // specified in TargetPortalGroupTag +#define ISCSI_LOGIN_AUTH_TEXT_KEY_INITIATOR_ALIAS "InitiatorAlias" // Use: ALL, Declarative, Any-Stage + // Senders: Initiator + // Scope: SW + // InitiatorAlias= + // Examples: + // InitiatorAlias=Web Server 5 + // InitiatorAlias=matrix.uni-freiburg.de + // InitiatorAlias=Matrix Server + // If an initiator has been configured with a human-readable name or + // description, it SHOULD be communicated to the target during a Login + // Request PDU. If not, the host name can be used instead. This string + // is not used as an identifier, nor is it meant to be used for + // authentication or authorization decisions. It can be displayed by + // the target's user interface in a list of initiators to which it is + // connected +#define ISCSI_LOGIN_AUTH_TEXT_KEY_TARGET_ALIAS "TargetAlias" // Use: ALL, Declarative, Any-Stage + // Senders: Target + // Scope: SW + // TargetAlias= + // Examples: + // TargetAlias=Bob-s Disk + // TargetAlias=Database Server 1 Log Disk + // TargetAlias=Web Server 3 Disk 20 + // If a target has been configured with a human-readable name or + // description, this name SHOULD be communicated to the initiator during + // a Login Response PDU if SessionType=Normal. This string is not used + // as an identifier, nor is it meant to be used for authentication or + // 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_TEXT_KEY_TARGET_PORTAL_GROUP_TAG "TargetPortalGroupTag" // Use: IO by target, Declarative, Any-Stage + // Senders: Target + // Scope: SW + // TargetPortalGroupTag=<16-bit-binary-value> + // Example: + // TargetPortalGroupTag=1 + // The TargetPortalGroupTag key is a 16-bit binary-value that uniquely + // identifies a portal group within an iSCSI target node. This key + // carries the value of the tag of the portal group that is servicing + // the Login Request. The iSCSI target returns this key to the + // initiator in the Login Response PDU to the first Login Request PDU + // that has the C bit set to 0 when TargetName is given by the + // initiator. + // SAM2 notes in its informative text that the TPGT value should be + // non-zero; note that this is incorrect. A zero value is allowed as a + // legal value for the TPGT. This discrepancy currently stands + // corrected in SAM4 +#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD "AuthMethod" // Use: During Login - Security Negotiation + // Senders: Initiator and target + // Scope: connection + // AuthMethod = + // The main item of security negotiation is the authentication method + // (AuthMethod). + // The authentication methods that can be used (appear in the list-of- + // values) are either vendor-unique methods or those listed in the + // following table: + // +--------------------------------------------------------------+ + // | Name | Description | + // +--------------------------------------------------------------+ + // | KRB5 | Kerberos V5 - defined in RFC4120 | + // +--------------------------------------------------------------+ + // | SRP | Secure Remote Password - | + // | | defined in RFC2945 | + // +--------------------------------------------------------------+ + // | CHAP | Challenge Handshake Authentication Protocol - | + // | | defined in RFC1994 | + // +--------------------------------------------------------------+ + // | None | No authentication | + // +--------------------------------------------------------------+ + // The AuthMethod selection is followed by an "authentication exchange" + // specific to the authentication method selected. + // The authentication method proposal may be made by either the + // initiator or the target. However, the initiator MUST make the first + // step specific to the selected authentication method as soon as it is + // selected. It follows that if the target makes the authentication + // method proposal, the initiator sends the first key(s) of the exchange + // together with its authentication method selection. + // The authentication exchange authenticates the initiator to the target + // and, optionally, the target to the initiator. Authentication is + // OPTIONAL to use but MUST be supported by the target and initiator. + // The initiator and target MUST implement CHAP. All other + // authentication methods are OPTIONAL. + // Private or public extension algorithms MAY also be negotiated for + // authentication methods. Whenever a private or public extension + // algorithm is part of the default offer (the offer made in the absence + // of explicit administrative action), the implementer MUST ensure that + // CHAP is listed as an alternative in the default offer and "None" is + // not part of the default offer. + // Extension authentication methods MUST be named using one of the + // following two formats: + // 1) Z-reversed.vendor.dns_name.do_something= + // 2) New public key with no name prefix constraints + // Authentication methods named using the Z- format are used as private + // extensions. New public keys must be registered with IANA using the + // IETF Review process RFC5226. New public extensions for + // authentication methods MUST NOT use the Z# name prefix. + // For all of the public or private extension authentication methods, + // the method-specific keys MUST conform to the format specified for + // standard-label. + // To identify the vendor for private extension authentication methods, + // we suggest using the reversed DNS-name as a prefix to the proper + // digest names. + // The part of digest-name following Z- MUST conform to the format for + // standard-label. + // Support for public or private extension authentication methods is + // OPTIONAL + +/* Kerberos V5 (KRB5) related authentication keys: */ +#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_KRB_AP_REQ "KRB_AP_REQ" // For KRB5 (Kerberos V5) (see RFC4120 and RFC1964), the initiator MUST use: + // KRB_AP_REQ= + // where KRB_AP_REQ is the client message as defined in RFC4120. + // The default principal name assumed by an iSCSI initiator or target + // (prior to any administrative configuration action) MUST be the iSCSI + // Initiator Name or iSCSI Target Name, respectively, prefixed by the + // string "iscsi/". + // If the initiator authentication fails, the target MUST respond with a + // Login reject with "Authentication Failure" status. Otherwise, if the + // initiator has selected the mutual authentication option (by setting + // MUTUAL-REQUIRED in the ap-options field of the KRB_AP_REQ), the + // target MUST reply with: +#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_KRB_AP_REP "KRB_AP_REP" // KRB_AP_REP= + // where KRB_AP_REP is the server's response message as defined in + // RFC4120. + // If mutual authentication was selected and target authentication + // fails, the initiator MUST close the connection. + // KRB_AP_REQ and KRB_AP_REP are binary-values, and their binary length + // (not the length of the character string that represents them in + // encoded form) MUST NOT exceed 65536 bytes. Hex or Base64 encoding + // may be used for KRB_AP_REQ and KRB_AP_REP + +/* Secure Remote Password (SRP) related authentication keys: */ +#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_SRP_SRP_U "SRP_U" // For SRP RFC2945, the initiator MUST use: + // SRP_U= TargetAuth=Yes or TargetAuth=No + // The target MUST answer with a Login reject with the "Authorization + // Failure" status or reply with: +#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_SRP_SRP_GROUP "SRP_GROUP" // SRP_GROUP= SRP_s= + // where G1,G2... are proposed groups, in order of preference. +#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_SRP_SRP_A "SRP_A" // The initiator MUST either close the connection or continue with: + // SRP_A= + // SRP_GROUP= + // where G is one of G1,G2... that were proposed by the target. + // The target MUST answer with a Login reject with the "Authentication + // Failure" status or reply with: +#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_SRP_SRP_B "SRP_B" // SRP_B= + // The initiator MUST close the connection or continue with: +#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_SRP_SRP_M "SRP_M" // SRP_M= + // If the initiator authentication fails, the target MUST answer with a + // Login reject with "Authentication Failure" status. Otherwise, if the + // initiator sent TargetAuth=Yes in the first message (requiring target + // authentication), the target MUST reply with: +#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_SRP_SRP_HM "SRP_HM" // SRP_HM= + // If the target authentication fails, the initiator MUST close the + // connection: + // where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using + // the SHA1 hash function, such as SRP-SHA1) and + // G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups + // specified in RFC3723. + // G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are + // binary-values. The length of s,A,B,M and H(A | M | K) in binary form + // (not the length of the character string that represents them in + // encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may + // be used for s,A,B,M and H(A | M | K). + // For the SRP_GROUP, all the groups specified in RFC3723 up to + // 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be + // supported by initiators and targets. To guarantee interoperability, + // targets MUST always offer "SRP-1536" as one of the proposed groups + +/* Challenge Handshake Authentication Protocol (CHAP) related authentication keys: */ +#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_A "CHAP_A" // For CHAP RFC1994, the initiator MUST use: + // CHAP_A= + // where A1,A2... are proposed algorithms, in order of preference. + // The target MUST answer with a Login reject with the "Authentication + // Failure" status or reply with: + // CHAP_A= +#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_I "CHAP_I" // CHAP_I= +#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_C "CHAP_C" // CHAP_C= + // where A is one of A1,A2... that were proposed by the initiator. + // The initiator MUST continue with: +#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_N "CHAP_N" // CHAP_N= +#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_R "CHAP_R" // CHAP_R= + // or, if it requires target authentication, with: + // CHAP_N= + // CHAP_R= + // CHAP_I= + // CHAP_C= + // If the initiator authentication fails, the target MUST answer with a + // Login reject with "Authentication Failure" status. Otherwise, if the + // initiator required target authentication, the target MUST either + // answer with a Login reject with "Authentication Failure" or reply + // with: + // CHAP_N= + // CHAP_R= + // If the target authentication fails, the initiator MUST close the + // connection: + // where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, + // Algorithm, Identifier, Challenge, and Response as defined in + // RFC1994. + // N is a text string; A,A1,A2, and I are numbers; C and R are + // binary-values. Their binary length (not the length of the character + // string that represents them in encoded form) MUST NOT exceed + // 1024 bytes. Hex or Base64 encoding may be used for C and R. + // For the Algorithm, as stated in [RFC1994], one value is required to + // be implemented: + // 5 (CHAP with MD5) + // To guarantee interoperability, initiators MUST always offer it as one + // of the proposed algorithms + +/* Login/Text Operational Text Keys + + Some session-specific parameters MUST only be carried on the leading + connection and cannot be changed after the leading connection login + (e.g., MaxConnections - the maximum number of connections). This + holds for a single connection session with regard to connection + restart. The keys that fall into this category have the "use: LO" + (Leading Only). + + Keys that can only be used during login have the "use: IO" + (Initialize Only), while those that can be used in both the Login + Phase and Full Feature Phase have the "use: ALL". + + Keys that can only be used during the Full Feature Phase use FFPO + (Full Feature Phase Only). + + Keys marked as Any-Stage may also appear in the SecurityNegotiation + stage, while all other keys described in this section are + operational keys. + + Keys that do not require an answer are marked as Declarative. + + Key scope is indicated as session-wide (SW) or connection-only (CO). + + "Result function", wherever mentioned, states the function that can + be applied to check the validity of the responder selection. + "Minimum" means that the selected value cannot exceed the offered + value. "Maximum" means that the selected value cannot be lower than + the offered value. "AND" means that the selected value must be a + possible result of a Boolean "and" function with an arbitrary Boolean + value (e.g., if the offered value is No the selected value must be + No). "OR" means that the selected value must be a possible result of + a Boolean "or" function with an arbitrary Boolean value (e.g., if the + offered value is Yes the selected value must be Yes). +*/ +#define ISCSI_LOGIN_AUTH_TEXT_KEY_HEADER_DIGEST "HeaderDigest" // Use: IO +#define ISCSI_LOGIN_AUTH_TEXT_KEY_DATA_DIGEST "DataDigest" // Senders: Initiator and target + // Scope: CO + // HeaderDigest = + // DataDigest = + // Default is None for both HeaderDigest and DataDigest. + // Digests enable the checking of end-to-end, non-cryptographic data + // integrity beyond the integrity checks provided by the link layers and + // the covering of the whole communication path, including all elements + // that may change the network-level PDUs, such as routers, switches, + // and proxies. + // The following table lists cyclic integrity checksums that can be + // negotiated for the digests and MUST be implemented by every iSCSI + // initiator and target. These digest options only have error detection + // significance. + // +---------------------------------------------+ + // | Name | Description | Generator | + // +---------------------------------------------+ + // | CRC32C | 32-bit CRC |0x11edc6f41| + // +---------------------------------------------+ + // | None | no digest | + // +---------------------------------------------+ + // The generator polynomial G(x) for this digest is given in hexadecimal + // notation (e.g. "0x3b" stands for 0011 1011, and the polynomial is + // x**5 + x**4 + x**3 + x + 1). + // When the initiator and target agree on a digest, this digest MUST be + // used for every PDU in the Full Feature Phase. + // Padding bytes, when present in a segment covered by a CRC, SHOULD be + // set to 0 and are included in the CRC. + // The CRC MUST be calculated by a method that produces the same results + // as the following process: + // - The PDU bits are considered as the coefficients of a polynomial + // M(x) of degree n - 1; bit 7 of the lowest numbered byte is + // considered the most significant bit (x**n - 1), followed by bit 6 + // of the lowest numbered byte through bit 0 of the highest numbered + // byte (x**0). + // - The most significant 32 bits are complemented. + // - The polynomial is multiplied by x**32, then divided by G(x). The + // generator polynomial produces a remainder R(x) of degree <= 31. + // - The coefficients of R(x) are formed into a 32-bit sequence. + // - The bit sequence is complemented, and the result is the CRC. + // - The CRC bits are mapped into the digest word. The x**31 + // coefficient is mapped to bit 7 of the lowest numbered byte of the + // digest, and the mapping continues with successive coefficients and + // bits so that the x**24 coefficient is mapped to bit 0 of the lowest + // numbered byte. The mapping continues further with the x**23 + // coefficient mapped to bit 7 of the next byte in the digest until + // the x**0 coefficient is mapped to bit 0 of the highest numbered + // byte of the digest. + // - Computing the CRC over any segment (data or header) extended to + // include the CRC built using the generator 0x11edc6f41 will always + // get the value 0x1c2d19ed as its final remainder (R(x)). This value + // is given here in its polynomial form (i.e., not mapped as the + // digest word). + // For a discussion about selection criteria for the CRC, see RFC3385. + // For a detailed analysis of the iSCSI polynomial, see Castagnoli93. + // Private or public extension algorithms MAY also be negotiated for + // digests. Whenever a private or public digest extension algorithm is + // part of the default offer (the offer made in the absence of explicit + // administrative action), the implementer MUST ensure that CRC32C is + // listed as an alternative in the default offer and "None" is not part + // of the default offer. + // Extension digest algorithms MUST be named using one of the following + // two formats: + // 1) Y-reversed.vendor.dns_name.do_something= + // 2) New public key with no name prefix constraints + // Digests named using the Y- format are used for private purposes + // (unregistered). New public keys must be registered with IANA using + // the IETF Review process (RFC5226). New public extensions for + // digests MUST NOT use the Y# name prefix. + // For private extension digests, to identify the vendor we suggest + // using the reversed DNS-name as a prefix to the proper digest names. + // The part of digest-name following Y- MUST conform to the format for + // standard-label specified. + // Support for public or private extension digests is OPTIONA +#define ISCSI_LOGIN_AUTH_TEXT_KEY_MAX_CONNECTIONS "MaxConnections" // Use: LO + // Senders: Initiator and target + // Scope: SW + // Irrelevant when: SessionType=Discovery + // MaxConnections= + // Default is 1. + // Result function is Minimum. + // The initiator and target negotiate the maximum number of connections + // requested/acceptable +#define ISCSI_LOGIN_AUTH_TEXT_KEY_SEND_TARGETS "SendTargets" // Use: FFPO + // Senders: Initiator + // Scope: SW + // The text in this appendix is a normative part of this document. + // To reduce the amount of configuration required on an initiator, iSCSI + // provides the SendTargets Text Request. The initiator uses the + // SendTargets request to get a list of targets to which it may have + // access, as well as the list of addresses (IP address and TCP port) on + // which these targets may be accessed. + // To make use of SendTargets, an initiator must first establish one of + // two types of sessions. If the initiator establishes the session + // using the key "SessionType=Discovery", the session is a Discovery + // session, and a target name does not need to be specified. Otherwise, + // the session is a Normal operational session. The SendTargets command + // MUST only be sent during the Full Feature Phase of a Normal or + // Discovery session. + // A system that contains targets MUST support Discovery sessions on + // each of its iSCSI IP address-port pairs and MUST support the + // SendTargets command on the Discovery session. In a Discovery + // session, a target MUST return all path information (IP address-port + // pairs and Target Portal Group Tags) for the targets on the target + // Network Entity that the requesting initiator is authorized to access. + // A target MUST support the SendTargets command on operational + // sessions; these will only return path information about the target to + // which the session is connected and do not need to return information + // about other target names that may be defined in the responding + // system. + // An initiator MAY make use of the SendTargets command as it sees fit. + // A SendTargets command consists of a single Text Request PDU. This + // PDU contains exactly one text key and value. The text key MUST be + // SendTargets. The expected response depends upon the value, as well + // as whether the session is a Discovery session or an operational + // session. + // The value must be one of: + // All + // The initiator is requesting that information on all relevant + // targets known to the implementation be returned. This value + // MUST be supported on a Discovery session and MUST NOT be + // supported on an operational session. + // + // If an iSCSI Target Name is specified, the session should + // respond with addresses for only the named target, if possible. + // This value MUST be supported on Discovery sessions. A + // Discovery session MUST be capable of returning addresses for + // those targets that would have been returned had value=All been + // designated. + // + // The session should only respond with addresses for the target + // to which the session is logged in. This MUST be supported on + // operational sessions and MUST NOT return targets other than the + // one to which the session is logged in. + // The response to this command is a Text Response that contains a list + // of zero or more targets and, optionally, their addresses. Each + // target is returned as a target record. A target record begins with + // the TargetName text key, followed by a list of TargetAddress text + // keys, and bounded by the end of the Text Response or the next + // TargetName key, which begins a new record. No text keys other than + // TargetName and TargetAddress are permitted within a SendTargets + // response. + // A Discovery session MAY respond to a SendTargets request with its + // complete list of targets, or with a list of targets that is based on + // the name of the initiator logged in to the session. + // A SendTargets response MUST NOT contain target names if there are no + // targets for the requesting initiator to access. + // Each target record returned includes zero or more TargetAddress + // fields. + // Each target record starts with one text key of the form: + // TargetName= + // followed by zero or more address keys of the form: + // TargetAddress=[:], + // + // The hostname-or-ipaddress contains a domain name, IPv4 address, or + // IPv6 address (RFC4291), as specified for the TargetAddress key. + // A hostname-or-ipaddress duplicated in TargetAddress responses for a + // given node (the port is absent or equal) would probably indicate that + // multiple address families are in use at once (IPv6 and IPv4). + // Each TargetAddress belongs to a portal group, identified by its + // numeric Target Portal Group Tag. The iSCSI Target + // Name, together with this tag, constitutes the SCSI port identifier; + // the tag only needs to be unique within a given target's name list of + // addresses. + // Multiple-connection sessions can span iSCSI addresses that belong to + // the same portal group. + // Multiple-connection sessions cannot span iSCSI addresses that belong + // to different portal groups. + // If a SendTargets response reports an iSCSI address for a target, it + // SHOULD also report all other addresses in its portal group in the + // same response. + // A SendTargets Text Response can be longer than a single Text Response + // PDU and makes use of the long Text Responses as specified. + // After obtaining a list of targets from the Discovery session, an + // iSCSI initiator may initiate new sessions to log in to the discovered + // targets for full operation. The initiator MAY keep the Discovery + // session open and MAY send subsequent SendTargets commands to discover + // new targets. + // Examples: + // This example is the SendTargets response from a single target that + // has no other interface ports. + // The initiator sends a Text Request that contains: + // SendTargets=All + // The target sends a Text Response that contains: + // TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.8675309 + // All the target had to return in this simple case was the target name. + // It is assumed by the initiator that the IP address and TCP port for + // this target are the same as those used on the current connection to + // the default iSCSI target. + // The next example has two internal iSCSI targets, each accessible via + // two different ports with different IP addresses. The following is + // the Text Response: + // TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.8675309 + // TargetAddress=10.1.0.45:5300,1 + // TargetAddress=10.1.1.45:5300,2 + // TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.1234567 + // TargetAddress=10.1.0.45:5300,1 + // TargetAddress=10.1.1.45:5300,2 + // Both targets share both addresses; the multiple addresses are likely + // used to provide multi-path support. The initiator may connect to + // either target name on either address. Each of the addresses has its + // own Target Portal Group Tag; they do not support spanning multiple- + // connection sessions with each other. Keep in mind that the Target + // Portal Group Tags for the two named targets are independent of one + // another; portal group "1" on the first target is not necessarily the + // same as portal group "1" on the second target. + // In the above example, a DNS host name or an IPv6 address could have + // been returned instead of an IPv4 address. + // The next Text Response shows a target that supports spanning sessions + // across multiple addresses and further illustrates the use of the + // Target Portal Group Tags: + // TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.8675309 + // TargetAddress=10.1.0.45:5300,1 + // TargetAddress=10.1.1.46:5300,1 + // TargetAddress=10.1.0.47:5300,2 + // TargetAddress=10.1.1.48:5300,2 + // TargetAddress=10.1.1.49:5300,3 + // In this example, any of the target addresses can be used to reach the + // same target. A single-connection session can be established to any + // of these TCP addresses. A multiple-connection session could span + // addresses .45 and .46 or .47 and .48 but cannot span any other + // combination. A TargetAddress with its own tag (.49) cannot be + // combined with any other address within the same session. + // This SendTargets response does not indicate whether .49 supports + // multiple connections per session; it is communicated via the + // MaxConnections text key upon login to the target +#define ISCSI_LOGIN_AUTH_TEXT_KEY_INITIAL_R2T "InitialR2T" // Use: LO + // Senders: Initiator and target + // Scope: SW + // Irrelevant when: SessionType=Discovery + // InitialR2T= + // Examples: + // I->InitialR2T=No + // T->InitialR2T=No + // Default is Yes. + // Result function is OR. + // The InitialR2T key is used to turn off the default use of R2T for + // unidirectional operations and the output part of bidirectional + // commands, thus allowing an initiator to start sending data to a + // target as if it has received an initial R2T with Buffer + // Offset=Immediate Data Length and Desired Data Transfer + // Length=(min(FirstBurstLength, Expected Data Transfer Length) - + // Received Immediate Data Length). + // The default action is that R2T is required, unless both the initiator + // and the target send this key-pair attribute specifying InitialR2T=No. + // 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_TEXT_KEY_IMMEDIATE_DATA "ImmediateData" // Use: LO + // Senders: Initiator and target + // Scope: SW + // Irrelevant when: SessionType=Discovery + // ImmediateData= + // Default is Yes. + // Result function is AND. + // The initiator and target negotiate support for immediate dataTo + // 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. + // If ImmediateData is set to Yes and InitialR2T is set to Yes + // (default), then only immediate data are accepted in the first burst. + // If ImmediateData is set to No and InitialR2T is set to Yes, then the + // initiator MUST NOT send unsolicited data and the target MUST reject + // unsolicited data with the corresponding response code. + // If ImmediateData is set to No and InitialR2T is set to No, then the + // initiator MUST NOT send unsolicited immediate data but MAY send one + // unsolicited burst of Data-OUT PDUs. + // If ImmediateData is set to Yes and InitialR2T is set to No, then the + // initiator MAY send unsolicited immediate data and/or one unsolicited + // burst of Data-OUT PDUs. + // The following table is a summary of unsolicited data options: + // +----------+-------------+------------------+-------------+ + // |InitialR2T|ImmediateData| Unsolicited |ImmediateData| + // | | | Data-Out PDUs | | + // +----------+-------------+------------------+-------------+ + // | No | No | Yes | No | + // +----------+-------------+------------------+-------------+ + // | No | Yes | Yes | Yes | + // +----------+-------------+------------------+-------------+ + // | Yes | No | No | No | + // +----------+-------------+------------------+-------------+ + // | Yes | Yes | No | Yes | + // +----------+-------------+------------------+-------------+ +#define ISCSI_LOGIN_AUTH_TEXT_KEY_MAX_RECV_DS_LEN "MaxRecvDataSegmentLength" // Use: ALL, Declarative + // Senders: Initiator and target + // Scope: CO + // MaxRecvDataSegmentLength= + // Default is 8192 bytes. + // The initiator or target declares the maximum data segment length in + // bytes it can receive in an iSCSI PDU. + // The transmitter (initiator or target) is required to send PDUs with a + // data segment that does not exceed MaxRecvDataSegmentLength of the + // receiver. + // A target receiver is additionally limited by MaxBurstLength for + // solicited data and FirstBurstLength for unsolicited dataAn + // initiator MUST NOT send solicited PDUs exceeding MaxBurstLength nor + // unsolicited PDUs exceeding FirstBurstLength (or FirstBurstLength- + // Immediate Data Length if immediate data were sent) +#define ISCSI_LOGIN_AUTH_TEXT_KEY_MAX_BURST_LEN "MaxBurstLength" // Use: LO + // Senders: Initiator and target + // Scope: SW + // Irrelevant when: SessionType=Discovery + // MaxBurstLength= + // Default is 262144 (256 KB). + // Result function is Minimum. + // The initiator and target negotiate the maximum SCSI data payload in + // bytes in a Data-In or a solicited Data-Out iSCSI sequence. A + // 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_TEXT_KEY_FIRST_BURST_LEN "FirstBurstLength" // Use: LO + // Senders: Initiator and target + // Scope: SW + // Irrelevant when: SessionType=Discovery + // Irrelevant when: ( InitialR2T=Yes and ImmediateData=No ) + // FirstBurstLength= + // Default is 65536 (64 KB). + // Result function is Minimum. + // The initiator and target negotiate the maximum amount in bytes of + // unsolicited data an iSCSI initiator may send to the target during the + // execution of a single SCSI command. This covers the immediate data + // (if any) and the sequence of unsolicited Data-Out PDUs (if any) that + // follow the command. + // FirstBurstLength MUST NOT exceed MaxBurstLength +#define ISCSI_LOGIN_AUTH_TEXT_KEY_DEFAULT_TIME_WAIT "DefaultTime2Wait" // Use: LO + // Senders: Initiator and target + // Scope: SW + // DefaultTime2Wait= + // Default is 2. + // Result function is Maximum. + // The initiator and target negotiate the minimum time, in seconds, to + // wait before attempting an explicit/implicit logout or an active task + // reassignment after an unexpected connection termination or a + // connection reset. + // A value of 0 indicates that logout or active task reassignment can be + // attempted immediately +#define ISCSI_LOGIN_AUTH_TEXT_KEY_DEFAULT_TIME_RETAIN "DefaultTime2Retain" // Use: LO + // Senders: Initiator and target + // Scope: SW + // DefaultTime2Retain= + // Default is 20. + // Result function is Minimum. + // The initiator and target negotiate the maximum time, in seconds, + // after an initial wait (Time2Wait), before which an active task + // reassignment is still possible after an unexpected connection + // termination or a connection reset. + // This value is also the session state timeout if the connection in + // question is the last LOGGED_IN connection in the session. + // A value of 0 indicates that connection/task state is immediately + // discarded by the target +#define ISCSI_LOGIN_AUTH_TEXT_KEY_MAX_OUTSTANDING_R2T "MaxOutstandingR2T" // Use: LO + // Senders: Initiator and target + // Scope: SW + // MaxOutstandingR2T= + // Irrelevant when: SessionType=Discovery + // Default is 1. + // Result function is Minimum. + // The initiator and target negotiate the maximum number of outstanding + // R2Ts per task, excluding any implied initial R2T that might be part + // of that task. An R2T is considered outstanding until the last data + // 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_TEXT_KEY_DATA_PDU_IN_ORDER "DataPDUInOrder" // Use: LO + // Senders: Initiator and target + // Scope: SW + // Irrelevant when: SessionType=Discovery + // DataPDUInOrder= + // Default is Yes. + // Result function is OR. + // "No" is used by iSCSI to indicate that the data PDUs within sequences + // can be in any order. "Yes" is used to indicate that data PDUs within + // sequences have to be at continuously increasing addresses and + // overlays are forbidden +#define ISCSI_LOGIN_AUTH_TEXT_KEY_DATA_SEQ_IN_ORDER "DataSequenceInOrder" // Use: LO + // Senders: Initiator and target + // Scope: SW + // Irrelevant when: SessionType=Discovery + // DataSequenceInOrder= + // Default is Yes. + // Result function is OR. + // A data sequence is a sequence of Data-In or Data-Out PDUs that end + // with a Data-In or Data-Out PDU with the F bit set to 1. A Data-Out + // sequence is sent either unsolicited or in response to an R2T. + // Sequences cover an offset-range. + // If DataSequenceInOrder is set to No, data PDU sequences may be + // transferred in any order. + // If DataSequenceInOrder is set to Yes, data sequences MUST be + // transferred using continuously non-decreasing sequence offsets (R2T + // buffer offset for writes, or the smallest SCSI Data-In buffer offset + // within a read data sequence). + // If DataSequenceInOrder is set to Yes, a target may retry at most the + // last R2T, and an initiator may at most request retransmission for the + // last read data sequence. For this reason, if ErrorRecoveryLevel is + // not 0 and DataSequenceInOrder is set to Yes, then MaxOutstandingR2T + // MUST be set to 1 +#define ISCSI_LOGIN_AUTH_TEXT_KEY_ERR_RECOVERY_LEVEL "ErrorRecoveryLevel" // Use: LO + // Senders: Initiator and target + // Scope: SW + // ErrorRecoveryLevel= + // Default is 0. + // Result function is Minimum. + // The initiator and target negotiate the recovery level supported. + // Recovery levels represent a combination of recovery capabilities. + // Each recovery level includes all the capabilities of the lower + // recovery levels and adds some new ones to them. + // In the description of recovery mechanisms, certain recovery classes + // are specified +#define ISCSI_LOGIN_AUTH_TEXT_KEY_PRIV_EXT_KEY_FMT "X-reversed.vendor" // Use: ALL + // Senders: Initiator and target + // Scope: specific key dependent + // X-reversed.vendor.dns_name.do_something= + // Keys with this format are used for private extension purposes. These + // keys always start with X- if unregistered with IANA (private). New + // public keys (if registered with IANA via an IETF Review RFC5226) no + // longer have an X# name prefix requirement; implementers may propose + // any intuitive unique name. + // For unregistered keys, to identify the vendor we suggest using the + // reversed DNS-name as a prefix to the key-proper. + // The part of key-name following X- MUST conform to the format for + // key-name. + // Vendor-specific keys MUST ONLY be used in Normal sessions. + // Support for public or private extension keys is OPTIONAL +#define ISCSI_LOGIN_AUTH_TEXT_KEY_TASK_REPORTING "TaskReporting" // Use: LO + // Senders: Initiator and target + // Scope: SW + // Irrelevant when: SessionType=Discovery + // TaskReporting= + // Default is RFC3720. + // This key is used to negotiate the task completion reporting semantics + // from the SCSI target. The following table describes the semantics + // that an iSCSI target MUST support for respective negotiated key + // values. Whenever this key is negotiated, at least the RFC3720 and + // ResponseFence values MUST be offered as options by the negotiation + // originator. + // +--------------+------------------------------------------+ + // | Name | Description | + // +--------------+------------------------------------------+ + // | RFC3720 | RFC 3720-compliant semantics. Response | + // | | fencing is not guaranteed, and fast | + // | | completion of multi-task aborting is not | + // | | supported. | + // +--------------+------------------------------------------+ + // | ResponseFence| Response Fence | + // | | semantics MUST be supported in reporting | + // | | task completions. | + // +--------------+------------------------------------------+ + // | FastAbort | Updated fast multi-task abort semantics | + // | | defined in MUST be supported. Support | + // | | for the Response. Fence is implied - | + // | | i.e., semantics MUST be supported as | + // | | well. | + // +--------------+------------------------------------------+ + // When TaskReporting is not negotiated to FastAbort, the + // standard multi-task abort semantics MUST be used +#define ISCSI_LOGIN_AUTH_TEXT_KEY_X_NODE_ARCH "X#NodeArchitecture" // Use: LO, Declarative + // Senders: Initiator and target + // Scope: SW + // X#NodeArchitecture= + // Default is None. + // Examples: + // X#NodeArchitecture=ExampleOS/v1234,ExampleInc_SW_Initiator/1.05a + // X#NodeArchitecture=ExampleInc_HW_Initiator/4010,Firmware/2.0.0.5 + // X#NodeArchitecture=ExampleInc_SW_Initiator/2.1,CPU_Arch/i686 + // This document does not define the structure or content of the list of + // values. + // The initiator or target declares the details of its iSCSI node + // architecture to the remote endpoint. These details may include, but + // are not limited to, iSCSI vendor software, firmware, or hardware + // versions; the OS version; or hardware architecture. This key may be + // declared on a Discovery session or a Normal session. + // The length of the key value (total length of the list-of-values) MUST + // NOT be greater than 255 bytes. + // X#NodeArchitecture MUST NOT be redeclared during the Login Phase. + // Functional behavior of the iSCSI node (this includes the iSCSI + // protocol logic - the SCSI, iSCSI, and TCP/IP protocols) MUST NOT + // depend on the presence, absence, or content of the X#NodeArchitecture + // key. The key MUST NOT be used by iSCSI nodes for interoperability or + // for exclusion of other nodes. To ensure proper use, key values + // SHOULD be set by the node itself, and there SHOULD NOT be provisions + // for the key values to contain user-defined text. + // Nodes implementing this key MUST choose one of the following + // implementation options: + // - only transmit the key, + // - only log the key values received from other nodes, or + // - both transmit and log the key values. + // Each node choosing to implement transmission of the key values MUST + // be prepared to handle the response of iSCSI nodes that do not + // understand the key. + // Nodes that implement transmission and/or logging of the key values + // may also implement administrative mechanisms that disable and/or + // change the logging and key transmission details. + // Thus, a valid behavior for this key may be that a node is completely + // 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_TEXT_KEY_IF_MARKER "IFMarker" // Obsoleted Keys +#define ISCSI_LOGIN_AUTH_TEXT_KEY_OF_MARKER "OFMarker" // This document obsoletes the following keys defined in RFC3720: +#define ISCSI_LOGIN_AUTH_TEXT_KEY_OF_MARK_INT "OFMarkInt" // IFMarker, OFMarker, OFMarkInt, and IFMarkInt. However, iSCSI +#define ISCSI_LOGIN_AUTH_TEXT_KEY_IF_MARK_INT "IFMarkInt" // implementations compliant to this document may still receive these + // obsoleted keys - i.e., in a responder role - in a text negotiation. + // When an IFMarker or OFMarker key is received, a compliant iSCSI + // implementation SHOULD respond with the constant "Reject" value. The + // implementation MAY alternatively respond with a "No" value. + // However, the implementation MUST NOT respond with a "NotUnderstood" + // value for either of these keys. + // When an IFMarkInt or OFMarkInt key is received, a compliant iSCSI + // implementation MUST respond with the constant "Reject" value. The + // implementation MUST NOT respond with a "NotUnderstood" value for + // either of these keys + +/* This is an initiator-defined component of the session identifier and + is structured as follows: + + For the T field values 00b and 01b, a combination of A and B (for + 00b) or B and C (for 01b) identifies the vendor or organization whose + component (software or hardware) generates this ISID. A vendor or + organization with one or more OUIs, or one or more Enterprise + Numbers, MUST use at least one of these numbers and select the + appropriate value for the T field when its components generate ISIDs. + An OUI or EN MUST be set in the corresponding fields in network byte + order (byte big-endian). + + If the T field is 10b, B and C are set to a random 24-bit unsigned + integer value in network byte order (byte big-endian). + + The Qualifier field is a 16-bit or 24-bit unsigned integer value that + provides a range of possible values for the ISID within the selected + namespace. It may be set to any value within the constraints + specified in the iSCSI protocol. + + If the ISID is derived from something assigned to a hardware adapter + or interface by a vendor as a preset default value, it MUST be + configurable to a value assigned according to the SCSI port behavior + desired by the system in which it is installed. The resultant ISID + MUST also be persistent over power cycles, reboot, card swap, etc. +*/ +typedef struct __attribute__((packed)) iscsi_isid { + uint8_t a; // Meaning depends on T bit (see above) + uint16_t b; // Meaning depends on T bit (see above) + uint8_t c; // Meaning depends on T bit (see above) + uint16_t d; // Meaning depends on T bit (see above) +} iscsi_isid; + +#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION 0x0 // SecurityNegotiation +#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 // LoginOperationalNegotiation +#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_FULL_FEATURE_PHASE 0x3 // FullFeaturePhase +#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE (1 << 0) // (NSG) - Two bits - the Login negotiation requests and responses are + // associated with a specific stage in the session (SecurityNegotiation, + // LoginOperationalNegotiation, FullFeaturePhase) and may indicate the + // 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_REQS_FLAGS_GET_NEXT_STAGE(x) ((x) & 3) + +#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_SECURITY_NEGOTIATION 0x0 // SecurityNegotiation +#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 // LoginOperationalNegotiation +#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_FULL_FEATURE_PHASE 0x3 // FullFeaturePhase +#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE (1 << 2) // (CSG) - 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_REQS_FLAGS_GET_CURRENT_STAGE(x) (((x) >> 2) & 3) + +#define ISCSI_LOGIN_REQ_FLAGS_CONTINUE (1 << 6) // (C) When set to 1, this bit indicates that the text (set of key=value + // pairs) in this Login Request is not complete (it will be continued on + // subsequent Login Requests); otherwise, it indicates that this Login + // Request ends a set of key=value pairs. A Login Request with the + // C bit set to 1 MUST have the T bit set to 0. +#define ISCSI_LOGIN_REQ_FLAGS_TRANSMIT (1 << 7) // (T) When set to 1, this bit indicates that the initiator is ready to + // transit to the next stage. + // If the T bit is set to 1 and the NSG is set to FullFeaturePhase, then + // this also indicates that the initiator is ready for the Login + // Final-Response. + +/* After establishing a TCP connection between an initiator and a + target, the initiator MUST start a Login Phase to gain further access + to the target's resources. + + The Login Phase consists of a sequence of Login Requests and Login + Responses that carry the same Initiator Task Tag. + + Login Requests are always considered as immediate. +*/ +typedef struct __attribute__((packed)) iscsi_login_req_packet { + uint8_t opcode; // Always 0x03 according to specification (see above) + int8_t flags; // Login request flags (see above) + uint8_t version_max; // Version-max indicates the maximum version number supported. + // All Login Requests within the Login Phase MUST carry the same + // Version-max. Currently, this is always 0 + // The target MUST use the value presented with the first Login Request. + uint8_t version_min; // All Login Requests within the Login Phase MUST carry the same + // Version-min. The target MUST use the value presented with the first + // Login Request. Always 0 for now + uint8_t total_ahs_len; // TotalAHSLength + uint8_t ds_len[3]; // DataSegmentLength + struct iscsi_isid isid; // ISID (see above for declaration) + uint16_t tsih; // The TSIH must be set in the first Login Request. The reserved value + // 0 MUST be used on the first connection for a new session. Otherwise, + // the TSIH sent by the target at the conclusion of the successful login + // of the first connection for this session MUST be used. The TSIH + // identifies to the target the associated existing session for this new + // connection. + // All Login Requests within a Login Phase MUST carry the same TSIH. + // The target MUST check the value presented with the first Login + // Request + uint32_t init_task_tag; // Initiator task tag + uint16_t cid; // Connection ID. The CID provides a unique ID for this connection within the session. + // All Login Requests within the Login Phase MUST carry the same CID. + // The target MUST use the value presented with the first Login Request. + // A Login Request with a non-zero TSIH and a CID equal to that of an + // existing connection implies a logout of the connection followed by a + // login + uint16_t reserved; // Reserved for future usage + uint32_t cmd_sn; // The CmdSN is either the initial command sequence number of a session + // (for the first Login Request of a session - the "leading" login) or + // the command sequence number in the command stream if the login is for + // a new connection in an existing session. + // Examples: + // - Login on a leading connection: If the leading login carries the + // CmdSN 123, all other Login Requests in the same Login Phase carry + // the CmdSN 123, and the first non-immediate command in the Full + // Feature Phase also carries the CmdSN 123. + // - Login on other than a leading connection: If the current CmdSN at + // the time the first login on the connection is issued is 500, then + // that PDU carries CmdSN=500. Subsequent Login Requests that are + // needed to complete this Login Phase may carry a CmdSN higher than + // 500 if non-immediate requests that were issued on other connections + // in the same session advance the CmdSN. + // If the Login Request is a leading Login Request, the target MUST use + // the value presented in the CmdSN as the target value for the + // ExpCmdSN + uint32_t exp_stat_sn; // For the first Login Request on a connection, this is the ExpStatSN + // for the old connection, and this field is only valid if the Login + // Request restarts a connection + // For subsequent Login Requests, it is used to acknowledge the Login + // Responses with their increasing StatSN values + uint64_t reserved2[2]; // Reserved for future usage + struct iscsi_ds_cmd_data ds_cmd_data; // Data segment - Login Parameters in Text Request Format + // The initiator MUST provide some basic parameters in order + // 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_login_req_packet; + +#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION 0x0 // SecurityNegotiation +#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 // LoginOperationalNegotiation +#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FULL_FEATURE_PHASE 0x3 // FullFeaturePhase +#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE (1 << 0) // (NSG) - Two bits - the Login negotiation requests and responses are + // associated with a specific stage in the session (SecurityNegotiation, + // LoginOperationalNegotiation, FullFeaturePhase) and may indicate the + // 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_GET_NEXT_STAGE(x) ((x) & 3) + +#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_SECURITY_NEGOTIATION 0x0 // SecurityNegotiation +#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 // LoginOperationalNegotiation +#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FULL_FEATURE_PHASE 0x3 // FullFeaturePhase +#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE (1 << 2) // (CSG) - 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_RESPONSE_FLAGS_GET_CURRENT_STAGE(x) (((x) >> 2) & 3) + +#define ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE (1 << 6) // (C) When set to 1, this bit indicates that the text (set of key=value + // pairs) in this Login Response is not complete (it will be continued + // on subsequent Login Responses); otherwise, it indicates that this + // Login Response ends a set of key=value pairs. A Login Response with + // the C bit set to 1 MUST have the T bit set to 0 +#define ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT (1 << 7) // (T) The T bit is set to 1 as an indicator of the end of the stage. If + // the T bit is set to 1 and the NSG is set to FullFeaturePhase, then + // this is also the Login Final-Response. A T bit of 0 indicates a + // "partial" response, which means "more negotiation needed". + // A Login Response with the T bit set to 1 MUST NOT contain key=value + // pairs that may require additional answers from the initiator within + // the same stage. + // If the Status-Class is 0, the T bit MUST NOT be set to 1 if the T bit + // in the request was set to 0 + +#define ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SUCCESS 0x00 // Success - indicates that the iSCSI target successfully + // received, understood, and accepted the request. The numbering + // fields (StatSN, ExpCmdSN, MaxCmdSN) are only valid if Status- + // Class is 0 +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SUCCESS 0x00 // Login is proceeding OK. If the response T bit is set to 1 in both the + // request and the matching response, and the NSG is set to + // FullFeaturePhase in both the request and the matching response, the + // Login Phase is finished, and the initiator may proceed to issue SCSI + // commands. + +#define ISCSI_LOGIN_RESPONSE_STATUS_CLASS_REDIRECT 0x01 // Redirection - indicates that the initiator must take further + // action to complete the request. This is usually due to the + // target moving to a different address. All of the redirection + // Status-Class responses MUST return one or more text key + // parameters of the type "TargetAddress", which indicates the + // target's new address. A redirection response MAY be issued by + // a target prior to or after completing a security negotiation if + // a security negotiation is required. A redirection SHOULD be + // accepted by an initiator, even without having the target + // complete a security negotiation if any security negotiation is + // required, and MUST be accepted by the initiator after the + // completion of the security negotiation if any security + // negotiation is required +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_REDIRECT_TEMP 0x01 // The requested iSCSI Target Name (ITN) has temporarily moved + // to the address provided +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_REDIRECT_PERM 0x02 // The requested ITN has permanently moved to the address provided + +#define ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR 0x02 // Initiator Error (not a format error) - indicates that the + // initiator most likely caused the error. This MAY be due to a + // request for a resource for which the initiator does not have + // permission. The request should not be tried again +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC 0x00 // Miscellaneous iSCSI initiator errors +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_ERR 0x01 // The initiator could not be successfully + // authenticated or target authentication is + // not supported +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_FAIL 0x02 // The initiator is not allowed access to the + // given target +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_NOT_FOUND 0x03 // The requested ITN does not exist at this + // address +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_TARGET_REMOVED 0x04 // The requested ITN has been removed, and + // no forwarding address is provided +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_WRONG_VERSION 0x05 // The requested iSCSI version range is not + // supported by the target +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_TOO_MANY_CONNECTIONS 0x06 // Too many connections on this SSID +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISSING_PARAMETER 0x07 // Missing parameters (e.g. iSCSI Initiator + // Name and/or Target Name) +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_NO_SESSION_SPANNING 0x08 // arget does not support session spanning + // to this connection (address) +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_SESSION_NO_SUPPORT 0x09 // Target does not support this type of + // session or not from this initiator +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_SESSION_NO_EXIST 0x0A // Attempt to add a connection to a non- + // existent session +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_INVALID_LOGIN_REQ_TYPE 0x0B // Invalid request type during login + +#define ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SERVER_ERR 0x03 // Target Error - indicates that the target sees no errors in the + // initiator's Login Request but is currently incapable of + // fulfilling the request. The initiator may retry the same Login + // Request later +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_TARGET_ERROR 0x00 // Target hardware or software error +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_SERVICE_UNAVAILABLE 0x01 // The iSCSI service or target is not + // currently operational +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_OUT_OF_RESOURCES 0x02 // The target has insufficient session, + // connection, or other resources + +/* The Login Response indicates the progress and/or end of the Login + Phase. +*/ +typedef struct __attribute__((packed)) iscsi_login_response_packet { + uint8_t opcode; // Always 0x23 according to specification (see above) + int8_t flags; // Login response flags (see above) + uint8_t version_max; // This is the highest version number supported by the target. + // All Login Responses within the Login Phase MUST carry the same + // Version-max + uint8_t version_active; // Version-active indicates the highest version supported by the target + // and initiator. If the target does not support a version within the + // range specified by the initiator, the target rejects the login and + // this field indicates the lowest version supported by the target. + // All Login Responses within the Login Phase MUST carry the same + // Version-active. + // The initiator MUST use the value presented as a response to the first + // Login Request + uint8_t total_ahs_len; // TotalAHSLength + uint8_t ds_len[3]; // DataSegmentLength + struct iscsi_isid isid; // ISID (see above for declaration) + uint16_t tsih; // The TSIH is the target-assigned session-identifying handle. Its + // internal format and content are not defined by this protocol, except + // for the value 0, which is reserved. With the exception of the Login + // Final-Response in a new session, this field should be set to the TSIH + // provided by the initiator in the Login Request. For a new session, + // the target MUST generate a non-zero TSIH and ONLY return it in the + // Login Final-Response + uint32_t init_task_tag; // Initiator task tag + uint32_t reserved; // Reserved for future usage + uint32_t stat_sn; // For the first Login Response (the response to the first Login + // Request), this is the starting status sequence number for the + // connection. The next response of any kind - including the next + // Login Response, if any, in the same Login Phase - will carry this + // number + 1. This field is only valid if the Status-Class is 0 + uint32_t exp_cmd_sn; // ExpCmdSN + uint32_t max_cmd_sn; // MaxCmdSN + uint8_t status_class; // Status-class (see above for details). If the Status-Class is + // not 0, the initiator and target MUST close the TCP connection + // If the target wishes to reject the Login Request for more than one + // reason, it should return the primary reason for the rejection + uint8_t status_detail; // Status-detail (see above for details) + uint16_t reserved2; // Reserved for future usage + uint64_t reserved3; // Reserved for future usage + struct iscsi_ds_cmd_data ds_cmd_data; // Data segment - Login Parameters in Text Request Format + // The target MUST provide some basic parameters in order to enable the + // initiator to determine if it is connected to the correct port and the + // initial text parameters for the security exchange. + // All the rules specified for Text Responses also hold for Login + // Responses +} iscsi_login_response_packet; + +#define ISCSI_LOGOUT_REQ_REASON_CODE_CLOSE_SESSION 0x00 // Close the session. All commands associated with the + // session (if any) are terminated +#define ISCSI_LOGOUT_REQ_REASON_CODE_CLOSE_CONNECTION 0x01 // Close the connection. All commands associated with the + // connection (if any) are terminated +#define ISCSI_LOGOUT_REQ_REASON_CODE_REMOVE_CONNECTION_RECOVERY 0x02 // Remove the connection for recovery. The connection is + // closed, and all commands associated with it, if any, are + // to be prepared for a new allegiance + +/* The entire logout discussion in this section is also applicable for + an implicit Logout realized by way of a connection reinstatement or + session reinstatement. When a Login Request performs an implicit + Logout, the implicit Logout is performed as if having the reason + codes specified below: +*/ +#define ISCSI_LOGOUT_REQ_REASON_CODE_IMPLICIT_SESSION_REINSTATEMENT 0x00 // Session reinstatement +#define ISCSI_LOGOUT_REQ_REASON_CODE_IMPLICIT_CONNECTION_REINSTATEMENT 0x01 // connection reinstatement when the operational + // ErrorRecoveryLevel < 2 +#define ISCSI_LOGOUT_REQ_REASON_CODE_IMPLICIT_CONNECTION_REINSTATEMENT_2 0x02 // connection reinstatement when the operational + // ErrorRecoveryLevel = 2 + +/* The Logout Request is used to perform a controlled closing of a + connection. + + An initiator MAY use a Logout Request to remove a connection from a + session or to close an entire session. + + After sending the Logout Request PDU, an initiator MUST NOT send any + new iSCSI requests on the closing connection. If the Logout Request + is intended to close the session, new iSCSI requests MUST NOT be sent + on any of the connections participating in the session. + + When receiving a Logout Request with the reason code "close the + connection" or "close the session", the target MUST terminate all + pending commands, whether acknowledged via the ExpCmdSN or not, on + that connection or session, respectively. + + When receiving a Logout Request with the reason code "remove the + connection for recovery", the target MUST discard all requests not + yet acknowledged via the ExpCmdSN that were issued on the specified + connection and suspend all data/status/R2T transfers on behalf of + pending commands on the specified connection. + + The target then issues the Logout Response and half-closes the TCP + connection (sends FIN). After receiving the Logout Response and + attempting to receive the FIN (if still possible), the initiator MUST + completely close the logging-out connection. For the terminated + commands, no additional responses should be expected. + + A Logout for a CID may be performed on a different transport + connection when the TCP connection for the CID has already been + terminated. In such a case, only a logical "closing" of the iSCSI + connection for the CID is implied with a Logout. + + All commands that were not terminated or not completed (with status) + and acknowledged when the connection is closed completely can be + reassigned to a new connection if the target supports connection + recovery. + + If an initiator intends to start recovery for a failing connection, + it MUST use the Logout Request to "clean up" the target end of a + failing connection and enable recovery to start, or use the Login + Request with a non-zero TSIH and the same CID on a new connection for + the same effect. In sessions with a single connection, the + connection can be closed and then a new connection reopened. A + connection reinstatement login can be used for recovery. + + A successful completion of a Logout Request with the reason code + "close the connection" or "remove the connection for recovery" + results at the target in the discarding of unacknowledged commands + received on the connection being logged out. These are commands that + have arrived on the connection being logged out but that have not + been delivered to SCSI because one or more commands with a smaller + CmdSN have not been received by iSCSI. The resulting holes in the + command sequence numbers will have to be handled by appropriate + recovery, unless the session is also closed. +*/ +typedef struct __attribute__((packed)) iscsi_logout_req_packet { + uint8_t opcode; // Always 0x06 according to specification (see above) + int8_t reason_code; // Reason Code + // A target implicitly terminates the active tasks due to the iSCSI + // protocol in the following cases: + // a) When a connection is implicitly or explicitly logged out with + // the reason code "close the connection" and there are active + // tasks allegiant to that connection. + // b) When a connection fails and eventually the connection state + // times out and there are active tasks allegiant to that + // connection + // c) When a successful recovery Logout is performed while there are + // active tasks allegiant to that connection and those tasks + // eventually time out after the Time2Wait and Time2Retain periods + // without allegiance reassignment + // d) When a connection is implicitly or explicitly logged out with + // the reason code "close the session" and there are active tasks + // in that session + // If the tasks terminated in any of the above cases are SCSI tasks, + // they must be internally terminated as if with CHECK CONDITION status. + // This status is only meaningful for appropriately handling the + // internal SCSI state and SCSI side effects with respect to ordering, + // because this status is never communicated back as a terminating + // status to the initiator. However, additional actions may have to be + // taken at the SCSI level, depending on the SCSI context as defined by + // the SCSI standards (e.g., queued commands and ACA; UA for the next + // command on the I_T nexus in cases a), b), and c) above). After the + // tasks are terminated, the target MUST report a Unit Attention condition + // on the next command processed on any connection for each affected + // I_T_L nexus with the status of CHECK CONDITION, the ASC/ASCQ value + // of 0x47 / 0x7F ("SOME COMMANDS CLEARED BY ISCSI PROTOCOL EVENT"), etc. + uint16_t reserved; // Reserved for future usage + uint8_t total_ahs_len; // TotalAHSLength (MUST be 0 for this PDU) + uint8_t ds_len[3]; // DataSegmentLength (MUST be 0 for this PDU) + uint64_t reserved2; // Reserved for future usage + uint32_t init_task_tag; // Initiator task tag + uint16_t cid; // This is the connection ID of the connection to be closed (including + // closing the TCP stream). This field is only valid if the reason code + // is not "close the session" + uint16_t reserved3; // Reserved for future usage + uint32_t cmd_sn; // CmdSN + uint32_t exp_stat_sn; // This is the last ExpStatSN value for the connection to be closed + uint64_t reserved4[2]; // Reserved for future usage + struct iscsi_header_digest hdr_digest; // Optional header digest +} iscsi_logout_req_packet; + +#define ISCSI_LOGOUT_RESPONSE_CLOSED_SUCCESSFULLY 0x00 // Connection or session closed successfully +#define ISCSI_LOGOUT_RESPONSE_CID_NOT_FOUND 0x01 // CID not found +#define ISCSI_LOGOUT_RESPONSE_CONNECTION_RECOVERY_NOT_SUPPORTED 0x02 // Connection recovery is not supported (i.e., the Logout reason + // code was "remove the connection for recovery" and the target + // does not support it as indicated by the operational + // ErrorRecoveryLevel) +#define ISCSI_LOGOUT_RESPONSE_CLEANUP_FAILED 0x03 // Cleanup failed for various reasons + +/* The Logout Response is used by the target to indicate if the cleanup + operation for the connection(s) has completed. + + After Logout, the TCP connection referred by the CID MUST be closed + at both ends (or all connections must be closed if the logout reason + was session close). +*/ +typedef struct __attribute__((packed)) iscsi_logout_response_packet { + uint8_t opcode; // Always 0x26 according to specification (see above) + uint8_t flags; // Reserved for future usage + uint8_t response; // Response + uint8_t reserved; // Reserved for future usage + uint8_t total_ahs_len; // TotalAHSLength (MUST be 0 for this PDU) + uint8_t ds_len[3]; // DataSegmentLength (MUST be 0 for this PDU) + uint64_t reserved2; // Reserved for future usage + uint32_t init_task_tag; // Initiator task tag + uint32_t reserved3; // Reserved for future usage + uint32_t stat_sn; // StatSN + uint32_t exp_cmd_sn; // ExpCmdSN + uint32_t max_cmd_sn; // MaxCmdSN + uint32_t reserved4; // Reserved for future usage + uint16_t time_wait; // Time2Wait + // If the Logout response code is 0 and the operational + // ErrorRecoveryLevel is 2, this is the minimum amount of time, in + // seconds, to wait before attempting task reassignment. If the Logout + // response code is 0 and the operational ErrorRecoveryLevel is less + // than 2, this field is to be ignored. + // This field is invalid if the Logout response code is 1. + // If the Logout response code is 2 or 3, this field specifies the + // minimum time to wait before attempting a new implicit or explicit + // logout. + // If Time2Wait is 0, the reassignment or a new Logout may be attempted + // immediately + uint16_t time_retain; // Time2Retain + // If the Logout response code is 0 and the operational + // ErrorRecoveryLevel is 2, this is the maximum amount of time, in + // seconds, after the initial wait (Time2Wait) that the target waits for + // the allegiance reassignment for any active task, after which the task + // state is discarded. If the Logout response code is 0 and the + // operational ErrorRecoveryLevel is less than 2, this field is to be + // ignored. + // This field is invalid if the Logout response code is 1. + // If the Logout response code is 2 or 3, this field specifies the + // maximum amount of time, in seconds, after the initial wait + // (Time2Wait) that the target waits for a new implicit or explicit + // logout. + // If it is the last connection of a session, the whole session state is + // discarded after Time2Retain. + // If Time2Retain is 0, the target has already discarded the connection + // (and possibly the session) state along with the task states. No + // reassignment or Logout is required in this case + uint32_t reserved5; // Reserved for future usage + struct iscsi_header_digest hdr_digest; // Optional header digest +} iscsi_logout_response_packet; + +#define ISCSI_SNACK_REQ_TYPE_DATA_R2T_SNACK 0x00 // Data/R2T SNACK: requesting retransmission of one or more + // Data-In or R2T PDUs +#define ISCSI_SNACK_REQ_TYPE_STATUS_SNACK 0x01 // Status SNACK: requesting retransmission of one or more + // numbered responses +#define ISCSI_SNACK_REQ_TYPE_DATA_ACK 0x02 // DataACK: positively acknowledges Data-In PDUs. + // If an initiator operates at ErrorRecoveryLevel 1 or higher, it MUST + // issue a SNACK of type DataACK after receiving a Data-In PDU with the + // A bit set to 1. However, if the initiator has detected holes in the + // input sequence, it MUST postpone issuing the SNACK of type DataACK + // until the holes are filled. An initiator MAY ignore the A bit if it + // deems that the bit is being set aggressively by the target (i.e., + // before the MaxBurstLength limit is reached). + // The DataACK is used to free resources at the target and not to + // request or imply data retransmission. + // An initiator MUST NOT request retransmission for any data it had + // already acknowledged +#define ISCSI_SNACK_REQ_TYPE_R_DATA_SNACK 0x03 // R-Data SNACK: requesting retransmission of Data-In PDUs with + // possible resegmentation and status tagging. + // If the initiator MaxRecvDataSegmentLength changed between the + // original transmission and the time the initiator requests + // retransmission, the initiator MUST issue a R-Data SNACK. + // With R-Data SNACK, the initiator indicates that it discards all the + // unacknowledged data and expects the target to resend it. It also + // expects resegmentation. In this case, the retransmitted Data-In PDUs + // MAY be different from the ones originally sent in order to reflect + // changes in MaxRecvDataSegmentLength. Their DataSN starts with the + // BegRun of the last DataACK received by the target if any was received; + // otherwise, it starts with 0 and is increased by 1 for each resent + // Data-In PDU. + // A target that has received a R-Data SNACK MUST return a SCSI Response + // that contains a copy of the SNACK Tag field from the R-Data SNACK in + // the SCSI Response SNACK Tag field as its last or only Response. For + // example, if it has already sent a response containing another value + // in the SNACK Tag field or had the status included in the last Data-In + // PDU, it must send a new SCSI Response PDU. If a target sends more + // than one SCSI Response PDU due to this rule, all SCSI Response PDUs + // must carry the same StatSN. If an initiator attempts to recover a lost + // SCSI Response when more than one response has been sent, the + // target will send the SCSI Response with the latest content known to + // the target, including the last SNACK Tag for the command. + // For considerations in allegiance reassignment of a task to a + // connection with a different MaxRecvDataSegmentLength. + +/* If the implementation supports ErrorRecoveryLevel greater than zero, + it MUST support all SNACK types. + + The SNACK is used by the initiator to request the retransmission of + numbered responses, data, or R2T PDUs from the target. The SNACK + Request indicates the numbered responses or data "runs" whose + retransmission is requested, where the run starts with the first + StatSN, DataSN, or R2TSN whose retransmission is requested and + indicates the number of Status, Data, or R2T PDUs requested, + including the first. 0 has special meaning when used as a starting + number and length: + + - When used in RunLength, it means all PDUs starting with the + initial. + + - When used in both BegRun and RunLength, it means all + unacknowledged PDUs. + + The numbered response(s) or R2T(s) requested by a SNACK MUST be + delivered as exact replicas of the ones that the target transmitted + originally, except for the fields ExpCmdSN, MaxCmdSN, and ExpDataSN, + which MUST carry the current values. R2T(s)requested by SNACK MUST + also carry the current value of the StatSN. + + The numbered Data-In PDUs requested by a Data SNACK MUST be delivered + as exact replicas of the ones that the target transmitted originally, + except for the fields ExpCmdSN and MaxCmdSN, which MUST carry the + current values; and except for resegmentation. + + Any SNACK that requests a numbered response, data, or R2T that was + not sent by the target or was already acknowledged by the initiator + MUST be rejected with a reason code of "Protocol Error". +*/ +typedef struct __attribute__((packed)) iscsi_snack_req_packet { + uint8_t opcode; // Always 0x10 according to specification (see above) + int8_t type; // Type + // Data/R2T SNACK, Status SNACK, or R-Data SNACK for a command MUST + // precede status acknowledgment for the given command + uint16_t reserved; // Reserved for future usage + uint8_t total_ahs_len; // TotalAHSLength + uint8_t ds_len[3]; // DataSegmentLength + uint64_t lun; // LUN or Reserved + uint32_t init_task_tag; // For a Status SNACK and DataACK, the Initiator Task Tag MUST be set to + // the reserved value 0xFFFFFFFF. In all other cases, the Initiator + // Task Tag field MUST be set to the Initiator Task Tag of the + // referenced command + uint32_t target_xfer_snack_tag; // For a R-Data SNACK, this field MUST contain a value that is different + // from 0 or 0xFFFFFFFF and is unique for the task (identified by the + // Initiator Task Tag). This value MUST be copied by the iSCSI target + // in the last or only SCSI Response PDU it issues for the command. + // For DataACK, the Target Transfer Tag MUST contain a copy of the + // Target Transfer Tag and LUN provided with the SCSI Data-In PDU with + // the A bit set to 1. + // In all other cases, the Target Transfer Tag field MUST be set to the + // reserved value 0xFFFFFFFF + uint32_t reserved2; // Reserved for future usage + uint32_t exp_stat_sn; // ExpStatSN + uint32_t reserved3; // Reserved for future usage + uint32_t beg_run; // BegRun + // This field indicates the DataSN, R2TSN, or StatSN of the first PDU + // whose retransmission is requested (Data/R2T and Status SNACK), or the + // next expected DataSN (DataACK SNACK). + // A BegRun of 0, when used in conjunction with a RunLength of 0, means + // "resend all unacknowledged Data-In, R2T or Response PDUs". + // BegRun MUST be 0 for a R-Data SNACK + uint32_t run_len; // RunLength + // This field indicates the number of PDUs whose retransmission is + // requested. + // A RunLength of 0 signals that all Data-In, R2T, or Response PDUs + // carrying the numbers equal to or greater than BegRun have to be + // resent. + // The RunLength MUST also be 0 for a DataACK SNACK in addition to a + // R-Data SNACK + struct iscsi_header_digest hdr_digest; // Optional header digest +} iscsi_snack_req_packet; + +#define ISCSI_REJECT_REASON_RESERVED 0x01 // Reserved, original PDU can't be resent +#define ISCSI_REJECT_REASON_DATA_DIGEST_ERR 0x02 // Data (payload) digest error, original + // PDU can be resent. + // For iSCSI, Data-Out PDU retransmission is only done if the + // target requests retransmission with a recovery R2T. However, + // if this is the data digest error on immediate data, the + // initiator may choose to retransmit the whole PDU, including + // the immediate data +#define ISCSI_REJECT_REASON_SNACK_REJECT 0x03 // SNACK Reject (original PDU can be resent) +#define ISCSI_REJECT_REASON_PROTOCOL_ERR 0x04 // Protocol Error (e.g., SNACK Request for a status that was + // already acknowledged). Original PDU can't be resent' +#define ISCSI_REJECT_REASON_COMMAND_NOT_SUPPORTED 0x05 // Command not supported (original PDU can't be resent) +#define ISCSI_REJECT_REASON_TOO_MANY_IMMEDIATE_COMMANDS 0x06 // Immediate command reject - too many immediate + // commands (original PDU can be resent) +#define ISCSI_REJECT_REASON_TASK_IN_PROGRESS 0x07 // Task in progress (original PDU can't be resent) +#define ISCSI_REJECT_REASON_INVALID_DATA_ACK 0x08 // Invalid data ack (original PDU can't be resent) +#define ISCSI_REJECT_REASON_INVALID_PDU_FIELD 0x09 // Invalid PDU field, original PDU can't be resent. + // A target should use this reason code for all invalid values + // of PDU fields that are meant to describe a task, a response, + // or a data transfer. Some examples are invalid TTT/ITT, + // buffer offset, LUN qualifying a TTT, and an invalid sequence + // number in a SNACK +#define ISCSI_REJECT_REASON_OUT_OF_RESOURCES 0x0A // Long op reject - Can't generate Target Transfer Tag - out of + // resources. Original PDU can be resent later +#define ISCSI_REJECT_REASON_DEPRECATED 0x0B // Deprecated; MUST NOT be used. Reason code 0x0B is deprecated + // and MUST NOT be used by implementations. An implementation + // receiving reason code 0x0B MUST treat it as a negotiation + // failure that terminates the Login Phase and the TCP connection +#define ISCSI_REJECT_REASON_WAITING_FOR_LOGOUT 0x0C // Waiting for Logout, original PDU can't be resent + +typedef struct __attribute__((packed)) iscsi_reject_packet { + uint8_t opcode; // Always 0x3F according to specification (see above) + uint8_t flags; // Reserved for future usage + uint8_t reason; // Reject reason (see above for definitions). + // In all the cases in which a pre-instantiated SCSI task is terminated + // because of the reject, the target MUST issue a proper SCSI command + // response with CHECK CONDITION. In these cases in which a status for + // the SCSI task was already sent before the reject, no additional + // status is required. If the error is detected while data from the + // initiator is still expected (i.e., the command PDU did not contain + // all the data and the target has not received a Data-Out PDU with the + // Final bit set to 1 for the unsolicited data, if any, and all + // outstanding R2Ts, if any), the target MUST wait until it receives + // the last expected Data-Out PDUs with the F bit set to 1 before + // sending the Response PDU + uint8_t reserved; // Reserved for future usage + uint8_t total_ahs_len; // TotalAHSLength + uint8_t ds_len[3]; // DataSegmentLength + uint64_t reserved2; // Reserved for future usage + uint32_t tag; // Always 0xFFFFFFFF for now + uint32_t reserved3; // Reserved for future usage + uint32_t stat_sn; // StatSN. This field carries its usual value and is not related to the + // rejected command. The StatSN is advanced after a Reject + uint32_t exp_cmd_sn; // ExpCmdSN. This field carries its usual value and is not related to the + // rejected command + uint32_t max_cmd_sn; // MaxCmdSN. This field carries its usual value and is not related to the + // rejected command + uint32_t data_r2tsn_sn; // DataSN/R2TSN or Reserved. + // This field is only valid if the rejected PDU is a Data/R2T SNACK and + // the Reject reason code is "Protocol Error". The DataSN/R2TSN is the + // next Data/R2T sequence number that the target would send for the + // task, if any + uint32_t reserved4[2]; // Reserved for future usage + struct iscsi_header_digest hdr_digest; // Optional header digest + struct iscsi_bhs_packet bad_pdu_hdr; // Complete Header of Bad PDU. The target returns the + // header (not including the digest) of the PDU in error + // as the data of the response + uint8_t vendor_data[0]; // Vendor-specific data (if any) + struct iscsi_data_digest data_digest; // Optional data digest +} iscsi_reject_packet; + +/* NOP-Out may be used by an initiator as a "ping request" to verify + that a connection/session is still active and all its components are + operational. The NOP-In response is the "ping echo". + + A NOP-Out is also sent by an initiator in response to a NOP-In. + + A NOP-Out may also be used to confirm a changed ExpStatSN if another + PDU will not be available for a long time. + + Upon receipt of a NOP-In with the Target Transfer Tag set to a valid + value (not the reserved value 0xffffffff), the initiator MUST respond + with a NOP-Out. In this case, the NOP-Out Target Transfer Tag MUST + contain a copy of the NOP-In Target Transfer Tag. The initiator + + SHOULD NOT send a NOP-Out in response to any other received NOP-In, + in order to avoid lengthy sequences of NOP-In and NOP-Out PDUs sent + in response to each other. +*/ +typedef struct __attribute__((packed)) iscsi_nop_out_packet { + uint8_t opcode; // Always 0x00 according to specification (see above) + uint8_t reserved[3]; // Reserved for future usage + uint8_t total_ahs_len; // TotalAHSLength + uint8_t ds_len[3]; // DataSegmentLength + uint64_t lun; // LUN or Reserved + uint32_t init_task_tag; // The NOP-Out MUST have the Initiator Task Tag set to a valid value + // only if a response in the form of a NOP-In is requested (i.e., the + // NOP-Out is used as a ping request). Otherwise, the Initiator Task + // Tag MUST be set to 0xFFFFFFFF. + // When a target receives the NOP-Out with a valid Initiator Task Tag, + // it MUST respond with a NOP-In Response. + // If the Initiator Task Tag contains 0xFFFFFFFF, the I bit MUST be set + // to 1, and the CmdSN is not advanced after this PDU is sent + uint32_t target_xfer_tag; // The Target Transfer Tag is a target-assigned identifier for the + // operation. + // The NOP-Out MUST only have the Target Transfer Tag set if it is + // issued in response to a NOP-In with a valid Target Transfer Tag. In + // this case, it copies the Target Transfer Tag from the NOP-In PDU. + // Otherwise, the Target Transfer Tag MUST be set to 0xFFFFFFFF. + // When the Target Transfer Tag is set to a value other than 0xFFFFFFFF, + // the LUN field MUST also be copied from the NOP-In + uint32_t cmd_sn; // CmdSN + uint32_t exp_stat_sn; // ExpStatSN + uint64_t reserved2[2]; // Reserved for future usage + struct iscsi_header_digest hdr_digest; // Optional header digest + struct iscsi_ds_cmd_data ds_ping_data; // DataSegment - Ping Data (optional) + // Ping data is reflected in the NOP-In Response. The length of the + // reflected data is limited to MaxRecvDataSegmentLength. The length of + // ping data is indicated by the DataSegmentLength. 0 is a valid value + // for the DataSegmentLength and indicates the absence of ping data + struct iscsi_data_digest data_digest; // Optional data digest +} iscsi_nop_out_packet; + +/* NOP-In is sent by a target as either a response to a NOP-Out, a + "ping" to an initiator, or a means to carry a changed ExpCmdSN and/or + MaxCmdSN if another PDU will not be available for a long time (as + determined by the target). + + When a target receives the NOP-Out with a valid Initiator Task Tag + (not the reserved value 0xFFFFFFFF), it MUST respond with a NOP-In + with the same Initiator Task Tag that was provided in the NOP-Out + request. It MUST also duplicate up to the first + MaxRecvDataSegmentLength bytes of the initiator-provided Ping Data. + For such a response, the Target Transfer Tag MUST be 0xFFFFFFFF. The + + target SHOULD NOT send a NOP-In in response to any other received + NOP-Out in order to avoid lengthy sequences of NOP-In and NOP-Out + PDUs sent in response to each other. + + Otherwise, when a target sends a NOP-In that is not a response to a + NOP-Out received from the initiator, the Initiator Task Tag MUST be + set to 0xFFFFFFFF, and the data segment MUST NOT contain any data + (DataSegmentLength MUST be 0). +*/ + +typedef struct __attribute__((packed)) iscsi_nop_in_packet { + uint8_t opcode; // Always 0x20 according to specification (see above) + uint8_t reserved[3]; // Reserved for future usage + uint8_t total_ahs_len; // TotalAHSLength + uint8_t ds_len[3]; // DataSegmentLength + uint64_t lun; // A LUN MUST be set to a correct value when the Target Transfer Tag is + // valid (not the reserved value 0xFFFFFFFF) + uint32_t init_task_tag; // Initiator task tag or 0xFFFFFFFF + uint32_t target_xfer_tag; // If the target is responding to a NOP-Out, this field is set to the + // reserved value 0xFFFFFFFF. + // If the target is sending a NOP-In as a ping (intending to receive a + // corresponding NOP-Out), this field is set to a valid value (not the + // reserved value 0xFFFFFFFF). + // If the target is initiating a NOP-In without wanting to receive a + // corresponding NOP-Out, this field MUST hold the reserved value + // 0xFFFFFFFF + uint32_t stat_sn; // The StatSN field will always contain the next StatSN. However, when + // the Initiator Task Tag is set to 0xFFFFFFFF, the StatSN for the + // connection is not advanced after this PDU is sent + uint32_t exp_cmd_sn; // ExpCmdSN + uint32_t max_cmd_sn; // MaxCmdSN + uint32_t reserved2[3]; // Reserved for future usage + struct iscsi_header_digest hdr_digest; // Optional header digest + struct iscsi_ds_cmd_data ds_ping_data; // DataSegment - Return Ping Data + struct iscsi_data_digest data_digest; // Optional data digest +} iscsi_nop_in_packet; + +#define ISCSI_VALIDATE_PACKET_RESULT_OK 0L // Validation successful -> iSCSI packet recognized and compliance to protocol specification +#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_NO_DATA -1L // Validation failed -> No packet data specified +#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_SIZE_TOO_SMALL -2L // Validation failed -> Packet size smaller than smallest possible iSCSI packet +#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_SIZE_MISMATCH -3L // Validation failed -> Packet size doesn't match calculated lengths from BHS +#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_UNSUPPORTED_VERSION -4L // Validation failed -> iSCSI protocol version not supported yet +#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS -5L // Validation failed -> Valid opcode but violates iSCSI protocol specification +#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_INVALID_OPCODE -6L // Validation failed -> Invalid opcode according to iSCSI protocol specification +#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_CRC32C_HDR_DIGEST -7L // Validation failed -> CRC32C check failed for header (BHS and/or AHS) +#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_CRC32C_DATA_DIGEST -8L // Validation failed -> CRC32C check failed for data segment + +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_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 + +#define ISCSI_TEXT_KEY_MAX_LEN 63UL // Maximum length of a key according to iSCSI specs + +#define ISCSI_TEXT_VALUE_MAX_SIMPLE_LEN 255UL // Maximum length of value for a simple key type +#define ISCSI_TEXT_VALUE_MAX_LEN 8192UL // Maximum length of value for a normal key + +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_INVALID -1L // Invalid +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_UNSPECIFIED 0L // Unspecified type +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST 1L // List +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN 2L // Numerical minimum +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MAX 3L // Numerical maximum +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_DECLARATIVE 4L // Numerical declarative +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE 5L // Declarative +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_OR 6L // Boolean OR +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_AND 7L // Boolean AND + +typedef struct iscsi_key_value_pair { + int type; // Type of pair (see above) + int state_index; + uint8_t *value; // Value of key +} iscsi_key_value_pair; + +static int iscsi_parse_text_key_value_pair(iscsi_hashmap *pairs, const uint8_t *packet_data, const uint32_t len); // Extracts a single text key / value pairs out of an iSCSI packet into a hash map +int iscsi_parse_key_value_pairs(iscsi_hashmap **pairs, const uint8_t *packet_data, uint len, int cbit, uint8_t **partial_pair); // Extracts all text key / value pairs out of an iSCSI packet into a hash map + +typedef struct iscsi_connection { + iscsi_hashmap *key_value_pairs; // Hash map containing text key / value pairs associated to this connection + int header_digest; // iSCSI connection contains a header digest (CRC32), must be 0 or 4 for now + int data_digest; // iSCSI connection contains a data digest (CRC32), must be 0 or 4 for now + uint8_t opcode; // Always 0x03 according to specification (see above) + int8_t flags; // Login request flags (see above) + uint8_t version_max; // Version-max indicates the maximum version number supported. + // All Login Requests within the Login Phase MUST carry the same + // Version-max. Currently, this is always 0 + // The target MUST use the value presented with the first Login Request. + uint8_t version_min; // All Login Requests within the Login Phase MUST carry the same + // Version-min. The target MUST use the value presented with the first + // Login Request. Always 0 for now + struct iscsi_isid isid; // ISID (see above for declaration) + uint16_t tsih; // The TSIH must be set in the first Login Request. The reserved value + // 0 MUST be used on the first connection for a new session. Otherwise, + // the TSIH sent by the target at the conclusion of the successful login + // of the first connection for this session MUST be used. The TSIH + // identifies to the target the associated existing session for this new + // connection. + // All Login Requests within a Login Phase MUST carry the same TSIH. + // The target MUST check the value presented with the first Login + // Request + uint32_t init_task_tag; // Initiator task tag + uint16_t cid; // Connection ID. The CID provides a unique ID for this connection within the session. + // All Login Requests within the Login Phase MUST carry the same CID. + // The target MUST use the value presented with the first Login Request. + // A Login Request with a non-zero TSIH and a CID equal to that of an + // existing connection implies a logout of the connection followed by a + // login + uint32_t cmd_sn; // The CmdSN is either the initial command sequence number of a session + // (for the first Login Request of a session - the "leading" login) or + // the command sequence number in the command stream if the login is for + // a new connection in an existing session. + // Examples: + // - Login on a leading connection: If the leading login carries the + // CmdSN 123, all other Login Requests in the same Login Phase carry + // the CmdSN 123, and the first non-immediate command in the Full + // Feature Phase also carries the CmdSN 123. + // - Login on other than a leading connection: If the current CmdSN at + // the time the first login on the connection is issued is 500, then + // that PDU carries CmdSN=500. Subsequent Login Requests that are + // needed to complete this Login Phase may carry a CmdSN higher than + // 500 if non-immediate requests that were issued on other connections + // in the same session advance the CmdSN. + // If the Login Request is a leading Login Request, the target MUST use + // the value presented in the CmdSN as the target value for the + // ExpCmdSN + uint32_t exp_stat_sn; // For the first Login Request on a connection, this is the ExpStatSN + // for the old connection, and this field is only valid if the Login + // Request restarts a connection + // For subsequent Login Requests, it is used to acknowledge the Login + // Responses with their increasing StatSN values +} iscsi_connection; + +iscsi_connection *iscsi_connection_create(const iscsi_login_req_packet *login_req_pkt); // Creates data structure for an iSCSI connection request +void iscsi_connection_destroy(iscsi_connection *conn); // Deallocates all resources acquired by iscsi_connection_create +int iscsi_connection_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // iSCSI connection destructor callback for hash map + +#endif /* DNBD3_ISCSI_H_ */ -- cgit v1.2.3-55-g7522 From 36292568e3720173e4ab279ad53714afaf7dec1c Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 29 Jul 2025 14:38:08 +0200 Subject: doxygen: Add initial config and gitlab runner job --- .gitlab-ci.yml | 27 ++++++++ Doxyfile | 54 +++++++++++++++ src/server/iscsi.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++++- src/server/iscsi.h | 9 +++ 4 files changed, 280 insertions(+), 2 deletions(-) create mode 100644 .gitlab-ci.yml create mode 100644 Doxyfile (limited to 'src') diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..69a5b1c --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,27 @@ +default: + image: debian:trixie-slim + +variables: + GIT_SUBMODULE_STRATEGY: recursive + +build: + stage: build + script: + - apt-get update + - apt-get install -y git doxygen graphviz gcc cmake make libjansson-dev libfuse-dev + - doxygen + artifacts: + paths: + - docs + +pages: + stage: deploy +# only: +# variables: +# - $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH + script: + - rm -rf -- public + - mv -- docs/html public + artifacts: + paths: + - public diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000..aa35fd5 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,54 @@ +# Doxyfile for dnbd3 project + +# Project Info +PROJECT_NAME = dnbd3 +PROJECT_BRIEF = "Documentation for dnbd3 C project" +OUTPUT_DIRECTORY = docs +CREATE_SUBDIRS = NO + +# HTML output +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_COLORSTYLE_HUE = 220 + +# Input +INPUT = src README.md +RECURSIVE = YES +FILE_PATTERNS = *.c *.h CMakeLists.txt *.md + +# Exclude unnecessary stuff (e.g., markdown or docs if not needed) +#EXCLUDE_PATTERNS = */cowDoc/* *.md + +# Source browsing +SOURCE_BROWSER = YES +INLINE_SOURCES = YES +STRIP_CODE_COMMENTS = NO + +# Preprocessor +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = NO +SKIP_FUNCTION_MACROS = YES + +# Warnings +WARN_IF_UNDOCUMENTED = YES +WARN_NO_PARAMDOC = YES + +# Dot (call graphs etc.) +HAVE_DOT = YES +CALL_GRAPH = YES +CALLER_GRAPH = YES +DOT_MULTI_TARGETS = YES + +# Other output (disabled) +GENERATE_LATEX = NO +GENERATE_MAN = NO +GENERATE_RTF = NO +GENERATE_XML = NO + +# Misc +EXTRACT_ALL = YES +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = YES +QUIET = NO diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 2dc85cc..7334bd6 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -387,6 +387,9 @@ void iscsi_hashmap_key_destroy(uint8_t *key) { * 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] 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) @@ -1092,6 +1095,72 @@ int iscsi_validate_data_digest(const iscsi_bhs_packet *packet_data, const int he return iscsi_get_be32(pkt_crc32c) == crc32c; } +/** + * Validates an iSCSI protocol key and value pair for compliance + * with the iSCSI specs. + * + * @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. + */ +static int iscsi_validate_text_key_value_pair(const uint8_t *packet_data, const uint32_t len) +{ + const uint key_val_len = strnlen( packet_data, len ); + const uint8_t *key_end = memchr( packet_data, '=', key_val_len ); + + if ( key_end == NULL ) + return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Missing separator '=' for key / value pair -> invalid iSCSI packet data + + const uint key_len = (key_end - packet_data); + + if ( key_len == 0 ) + return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Zero length is not allowed -> invalid iSCSI packet data + + if ( key_len > ISCSI_TEXT_KEY_MAX_LEN ) + return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; + + const uint val_len = strnlen( 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; + + if ( val_len > max_len ) + return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Value exceeds maximum length -> invalid iSCSI packet data + + return key_len + 1UL + val_len + 1UL; // Number of bytes for processed key / value pair (+1 for '=' and NUL terminator) +} + +/** + * Validates all iSCSI protocol key and value pairs for + * compliance with the iSCSI specs. + * + * @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. + */ +static int iscsi_validate_key_value_pairs(const uint8_t *packet_data, uint len) +{ + if ( len == 0 ) + return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Zero length is not allowed -> invalid iSCSI packet data + + int offset = 0L; + + while ( (offset < len) && (packet_data[offset] != '\0') ) { + const int rc = iscsi_validate_text_key_value_pair( (packet_data + offset), (len - offset) ); + + if ( rc < ISCSI_VALIDATE_PACKET_RESULT_OK ) + return rc; + + offset += rc; + } + + return (offset != len) ? ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS : ISCSI_VALIDATE_PACKET_RESULT_OK; +} + /** * Checks whether packet data is an iSCSI packet or not. * Since iSCSI doesn't have a magic identifier for its packets, a @@ -1180,6 +1249,8 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint if ( (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; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + return iscsi_validate_key_value_pairs( ((const uint8_t *) login_req_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len ); + break; } case ISCSI_CLIENT_TEXT_REQ : { @@ -1191,6 +1262,8 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint if ( (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; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + return iscsi_validate_key_value_pairs( ((const uint8_t *) text_req_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len ); + break; } case ISCSI_CLIENT_SCSI_DATA_OUT : { @@ -1271,6 +1344,8 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint if ( (login_response_pkt->reserved != 0) || (login_response_pkt->reserved2 != 0) || (login_response_pkt->reserved3 != 0) ) return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + return iscsi_validate_key_value_pairs( ((const uint8_t *) login_response_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len ); + break; } case ISCSI_SERVER_TEXT_RES : { @@ -1282,6 +1357,8 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint if ( (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; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + return iscsi_validate_key_value_pairs( ((const uint8_t *) text_response_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len ); + break; } case ISCSI_SERVER_SCSI_DATA_IN : { @@ -1456,7 +1533,7 @@ static int iscsi_parse_text_key_value_pair(iscsi_hashmap *pairs, const uint8_t * * @param[in] packet_data Pointer to first key and value pair to * be parsed. NULL is an illegal value here, so be careful. * @param[in] len Length of the remaining packet data. - * @param[in] cbit Non-zero value of C bit was set in previously. + * @param[in] c_bit Non-zero value of C bit was set in previously. * @param[in] partial_pairs Array of partial pair pointers in * case C bit was set (multiple iSCSI packets for text data). * @retval -1 An error occured during parsing key. @@ -1537,6 +1614,114 @@ int iscsi_parse_key_value_pairs(iscsi_hashmap **pairs, const uint8_t *packet_dat return 0L; } +/** + * Callback function for constructing an iSCSI packet data + * stream for a single key=value text pair. + * + * @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 creates an + * empty key assignment. + * @param[in] user_data Pointer to a data structure + * containing the memory buffer and length for the + * iSCSI packet data (iscsi_key_value_pair_packet + * structure), may NOT be NULL, so be careful. + * @retval 0 The NUL terminated key=value pair has been + * generated successfully. + * @retval -1 The NUL terminated key=value pair could + * NOT be created (probably due to memory exhaustion). + */ +int iscsi_create_key_value_pair_packet_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) +{ + iscsi_key_value_pair_packet *packet_data = (iscsi_key_value_pair_packet *) user_data; + const uint8_t *buf = iscsi_sprintf_alloc( "%s=%s", key, ((value != NULL) ? value : "") ); + + if ( buf == NULL ) { + logadd( LOG_ERROR, "iscsi_create_key_value_pair_callback: Out of memory generating text key / value pair DataSegment" ); + + return -1L; + } + + const uint len = strlen( buf ) + 1; + const uint new_len = packet_data->len + len; + + uint8_t *new_buf = realloc( packet_data->buf, new_len ); + + if ( new_buf == NULL ) { + logadd( LOG_ERROR, "iscsi_create_key_value_pair_callback: Out of memory generating text key / value pair DataSegment" ); + + free( buf ); + + return -1L; + } + + memcpy( (new_buf + packet_data->len), buf, len ); + + packet_data->buf = new_buf; + packet_data->len = new_len; + + return 0L; +} + +/** + * Creates a properly aligned iSCSI DataSegment + * containing NUL terminated key=value pairs + * out of an hash map. The result can directly + * be attached to a BHS/AHS packet and sent + * via TCP/IP. + * + * @param[in] pairs Pointer to hash map containing + * the key and value pairs to construct the + * DataSegment from, may NOT be NULL, so be careful. + * @return Pointer to iscsi_key_value_pair_packet + * structure containing the properly aligned + * DataSegment buffer and its unaligned length or + * NULL in case of an error (most likely due to + * memory exhaustion). + */ +iscsi_key_value_pair_packet *iscsi_create_key_value_pairs_packet(const iscsi_hashmap *pairs) +{ + iscsi_key_value_pair_packet *packet_data = (iscsi_key_value_pair_packet *) malloc( sizeof(struct iscsi_key_value_pair_packet) ); + + if ( packet_data == NULL ) { + logadd( LOG_ERROR, "iscsi_create_key_value_pairs: Out of memory generating text key / value pair DataSegment" ); + + return NULL; + } + + packet_data->buf = NULL; + packet_data->len = 0UL; + + if ( iscsi_hashmap_iterate( pairs, iscsi_create_key_value_pair_packet_callback, packet_data ) < 0L ) { + if ( packet_data->buf != NULL ) + free( packet_data->buf ); + + free( packet_data ); + + return NULL; + } + + if ( (packet_data->len & (ISCSI_ALIGN_SIZE - 1UL)) != 0 ) { + uint8_t *new_buf = realloc( packet_data->buf, iscsi_align(packet_data->len, ISCSI_ALIGN_SIZE) ); + + if ( new_buf == NULL ) { + logadd( LOG_ERROR, "iscsi_create_key_value_pairs: Out of memory generating text key / value pair DataSegment" ); + + free( packet_data->buf ); + free( packet_data ); + + return NULL; + } + + memset( (packet_data->buf + packet_data->len), 0, (packet_data->len & (ISCSI_ALIGN_SIZE - 1UL)) ); + } + + return packet_data; +} + /** * Creates a data structure for an incoming iSCSI connection request * from iSCSI packet data. @@ -1620,7 +1805,10 @@ void iscsi_connection_destroy(iscsi_connection *conn) * be a multiple of 8 bytes which is NOT checked, so * be careful. * @param[in] value Value of the key, NULL is allowed. - * @return Always returns 0a as this function cannot fail. + * @param[in] 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_connection_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) { diff --git a/src/server/iscsi.h b/src/server/iscsi.h index f013a8f..8035d94 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -2868,6 +2868,8 @@ void iscsi_calc_header_digest(const iscsi_bhs_packet *packet_data); // Calculate 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 +static int iscsi_validate_text_key_value_pair(const uint8_t *packet_data, const uint32_t len); // Validates a single text key / value pair according to iSCSI specs +static int iscsi_validate_key_value_pairs(const uint8_t *packet_data, uint len); // Validates all text key / value pairs according to iSCSI specs 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 #define ISCSI_TEXT_KEY_MAX_LEN 63UL // Maximum length of a key according to iSCSI specs @@ -2891,8 +2893,15 @@ typedef struct iscsi_key_value_pair { uint8_t *value; // Value of key } iscsi_key_value_pair; +typedef struct iscsi_key_value_pair_packet { + uint8_t *buf; // Current text buffer containing multiple zeroes + uint len; // Current length of buffer including final zero terminator +} iscsi_key_value_pair_packet; + static int iscsi_parse_text_key_value_pair(iscsi_hashmap *pairs, const uint8_t *packet_data, const uint32_t len); // Extracts a single text key / value pairs out of an iSCSI packet into a hash map int iscsi_parse_key_value_pairs(iscsi_hashmap **pairs, const uint8_t *packet_data, uint len, int cbit, uint8_t **partial_pair); // Extracts all text key / value pairs out of an iSCSI packet into a hash map +int iscsi_create_key_value_pair_packet_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Creates a single partial iSCSI packet stream out of a single text key and value pair +iscsi_key_value_pair_packet *iscsi_create_key_value_pairs_packet(const iscsi_hashmap *pairs); // Creates a properly aligned iSCSI packet DataSegment out of a hash map containing text key and value pairs typedef struct iscsi_connection { iscsi_hashmap *key_value_pairs; // Hash map containing text key / value pairs associated to this connection -- cgit v1.2.3-55-g7522 From 00a233cf13760f317de683f591dab633a331fdc4 Mon Sep 17 00:00:00 2001 From: Sebastian Vater Date: Wed, 30 Jul 2025 11:35:52 +0200 Subject: [SERVER] iscsi: Add a lot of Doxygen comments, some general additions --- Doxyfile | 5 +- src/server/iscsi.c | 226 +- src/server/iscsi.h | 8063 +++++++++++++++++++++++++++++++++++----------------- 3 files changed, 5551 insertions(+), 2743 deletions(-) (limited to 'src') diff --git a/Doxyfile b/Doxyfile index aa35fd5..75de69e 100644 --- a/Doxyfile +++ b/Doxyfile @@ -28,8 +28,9 @@ STRIP_CODE_COMMENTS = NO # Preprocessor ENABLE_PREPROCESSING = YES MACRO_EXPANSION = YES -EXPAND_ONLY_PREDEF = NO -SKIP_FUNCTION_MACROS = YES +EXPAND_ONLY_PREDEF = YES +SKIP_FUNCTION_MACROS = NO +PREDEFINED = __attribute__(x)= # Warnings WARN_IF_UNDOCUMENTED = YES diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 7334bd6..49e0f46 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -34,13 +34,15 @@ * @brief iSCSI implementation for DNBD3. * * This file contains the iSCSI implementation according to - * RFC7143 for dnbd3-server. + * RFC7143 for dnbd3-server.\n * All server-side network sending and client-side network - * receiving code is done here. + * receiving code is done here.\n * @see https://www.rfc-editor.org/rfc/rfc7143 */ /** + * @brief Allocates and appends a buffer and sprintf's it. + * * Merges multiple strings using printf style formatting * and allocates memory for holding the result. * @@ -77,6 +79,8 @@ uint8_t *iscsi_vsprintf_append_realloc(char *buf, const char *format, va_list ar } /** + * @brief Allocates and appends a buffer and sprintf's it. + * * Merges strings using printf style formatting and allocates * memory for holding the result. * @@ -97,6 +101,8 @@ uint8_t *iscsi_sprintf_append_realloc(char *buf, const char *format, ...) } /** + * @brief Allocates a buffer and sprintf's it. + * * Merges strings using printf style formatting and allocates * memory for holding the result. * @@ -110,6 +116,8 @@ uint8_t *iscsi_vsprintf_alloc(const char *format, va_list args) } /** + * @brief Allocates a buffer and sprintf's it. + * * Allocates a buffer large enough to hold printf style * string concatenation and fills it using vspnrintf. * @@ -129,11 +137,13 @@ uint8_t *iscsi_sprintf_alloc(const char *format, ... ) } /** + * @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. + * 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. + * 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. * @@ -186,6 +196,8 @@ 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 @@ -205,6 +217,8 @@ void iscsi_hashmap_destroy(iscsi_hashmap *map) } /** + * @brief Puts an old bucket into a resized hash map. + * * Puts an old bucket into a resized hash map. * * @param[in] map Pointer to resized hash map, may NOT be NULL, so @@ -231,6 +245,8 @@ static iscsi_hashmap_bucket *iscsi_hashmap_resize_entry(iscsi_hashmap *map, cons } /** + * @brief Resizes a hash map by doubling its bucket capacity and purges any removed buckets. + * * 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 @@ -281,6 +297,8 @@ static int iscsi_hashmap_resize(iscsi_hashmap *map) } /** + * @brief Calculates the hash code of data with a specified length. + * * Calculates the hash code of data with a specified length. * * @param[in] data Pointer to data to be hashed, NULL is NOT @@ -306,6 +324,8 @@ static inline uint32_t iscsi_hashmap_hash_data(const uint8_t *data, const size_t } /** + * @brief Finds a bucket by key of a specified hash map by key, key size and hash code. + * * Finds a bucket by key of a specified hash map by * key, key size and hash code. This function may * only be called if the bucket is guaranteed to @@ -335,8 +355,10 @@ static iscsi_hashmap_bucket *iscsi_hashmap_find_entry(iscsi_hashmap *map, const } /** + * @brief Creates a key suitable for hashmap usage (ensures 8-byte boundary and zero padding). + * * Creates a key from data and size and ensures - * its requirements for usage in hash map buckets. + * 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. @@ -366,6 +388,8 @@ uint8_t *iscsi_hashmap_key_create(const uint8_t *data, const size_t len) } /** + * @brief Deallocates all resources acquired by iscsi_hashmap_create_key. + * * Deallocates a key allocated with the function * iscsi_hashmap_key_create. * @@ -377,6 +401,8 @@ void iscsi_hashmap_key_destroy(uint8_t *key) { } /** + * @brief Deallocates all key / value pairs in a hash map by calling free (default destructor). + * * Default callback function for deallocation of * allocated hash map resources by simply calling * free. @@ -387,7 +413,7 @@ void iscsi_hashmap_key_destroy(uint8_t *key) { * 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] user_data This argument is not used by + * @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. @@ -403,9 +429,11 @@ int iscsi_hashmap_key_destroy_value_callback(uint8_t *key, const size_t key_size } /** + * @brief Assigns key / value pair to hash map without making copies. + * * Adds a key / value pair to a specified hash map * bucket list, if it doesn't exist already. The - * buckets are resized automatically if required. + * buckets are resized automatically if required.\n * This function neither does make a copy of the key, * nor of the value. Keys should be allocated using * the function iscsi_hashmap_key_create or freed by @@ -453,12 +481,14 @@ int iscsi_hashmap_put(iscsi_hashmap *map, const uint8_t *key, const size_t key_s } /** + * @brief Assigns key / value pair to hash map without making copies. + * * Adds a key / value pair if it doesn't exist * using the value of `*out_in_val`. If the pair * does exist, value will be set in `*out_in`, * meaning the value of the pair will be in * '*out_in` regardless of whether or not it it - * existed in the first place. + * existed in the first place.\n * The buckets are resized automatically if required. * This function neither does make a copy of the key, * nor of the value. Keys should be allocated using @@ -511,13 +541,15 @@ int iscsi_hashmap_get_put(iscsi_hashmap *map, const uint8_t *key, const size_t k } /** + * @brief Assigns key / value pair to hash map without making copies with callback function in case the key already exists. + * * Adds a key / value pair to a specified hash map * bucket list. If the key already exists, it will * be overwritten and a callback function will be * invoked in order to allow, e.g. deallocation of * resources, if necessary. The buckets are resized * automatically if required. This function neither - * does make a copy of the key, nor of the value. + * does make a copy of the key, nor of the value.\n * Keys should be allocated using the function * iscsi_hashmap_key_create or freed by using * iscsi_hashmap_key_destroy in order to ensure the @@ -538,7 +570,7 @@ int iscsi_hashmap_get_put(iscsi_hashmap *map, const uint8_t *key, const size_t k * overwritten key and value pair. The function is * invoked just before overwriting the old values. * This may NOT be NULL, so take caution. - * @param[in] user_data Pointer to user specific data + * @param[in,out] user_data Pointer to user specific data * passed to the callback function in case more * information is needed. * @return -1 in case adding key / value pair would @@ -580,6 +612,8 @@ int iscsi_hashmap_put_free(iscsi_hashmap *map, const uint8_t *key, const size_t } /** + * @brief Checks whether a specified key exists. + * * Checks whether a specified key exists in a hash map. * * @param[in] map Pointer to the hash map to be searched @@ -602,6 +636,8 @@ int iscsi_hashmap_contains(iscsi_hashmap *map, const uint8_t *key, const size_t } /** + * @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. * @@ -633,6 +669,8 @@ int iscsi_hashmap_get(iscsi_hashmap *map, const uint8_t *key, const size_t key_s } /** + * @brief Marks an element for removal 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 @@ -661,7 +699,9 @@ void iscsi_hashmap_remove(iscsi_hashmap *map, const uint8_t *key, const size_t k } /** - * Removes an element from the bucket list of the hash map. + * @brief Marks an element for removal by setting key and value both to NULL, but invokes a callback function before actual marking for 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 @@ -681,7 +721,7 @@ void iscsi_hashmap_remove(iscsi_hashmap *map, const uint8_t *key, const size_t k * 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] user_data Pointer to user specific data + * @param[in,out] user_data Pointer to user specific data * passed to the callback function in case more * information is needed. */ @@ -701,6 +741,8 @@ void iscsi_hashmap_remove_free(iscsi_hashmap *map, const uint8_t *key, const siz } /** + * @brief Retrieves the number of elements of the hash map, ignoring elements marked for removal. + * * Returns the number of elements stored in the specified * hash map. Elements marked for removal are not included. * @@ -715,6 +757,8 @@ int iscsi_hashmap_size(iscsi_hashmap *map) } /** + * @brief Iterator with callback function invoked on each element which has not been removed. + * * 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 @@ -726,7 +770,7 @@ int iscsi_hashmap_size(iscsi_hashmap *map) * 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. - * @param[in] user_data Pointer to user specific data + * @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 @@ -753,6 +797,8 @@ int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, u } /** + * @brief Allocate and initialize an iSCSI BHS packet. + * * Allocates an iSCSI packet data Basic Header Segment (BHS) * and zero fills the structure. * @@ -786,6 +832,8 @@ iscsi_bhs_packet *iscsi_create_packet() } /** + * @brief Free resources allocated by iscsi_create_packet. + * * Deallocates all aquired resources by iscsi_create_packet. * * @param[in] packet_data Pointer to packet data to deallocate. If this is @@ -798,9 +846,11 @@ void iscsi_destroy_packet(iscsi_bhs_packet *packet_data) } /** + * @brief Allocate and initialize an iSCSI AHS packet and append to existing data stream. + * * 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! + * 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 @@ -847,6 +897,8 @@ iscsi_bhs_packet *iscsi_append_ahs_packet(iscsi_bhs_packet *packet_data, const u } /** + * @brief Counts number of AHS packets in an iSCSI data packet stream. + * * Gets the total number of AHS packets. * * @param[in] packet_data Pointer to packet data of which the @@ -878,6 +930,8 @@ int iscsi_get_ahs_packets(const iscsi_bhs_packet *packet_data) } /** + * @brief Retrieves the pointer to an specific AHS packet by index. + * * Gets the pointer of an AHS packet by specified index. * * @param[in] packet_data Pointer to packet data of which the @@ -914,9 +968,11 @@ iscsi_ahs_packet *iscsi_get_ahs_packet(const iscsi_bhs_packet *packet_data, cons } /** - * Constructs and appends DataSegment (DS) to already allocated packet data. + * @brief Allocate and initialize an iSCSI DS packet and append to existing data stream. + * + * 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! + * to the old structure need to be updated!\n * This function currently erases an already available DataSegment and * throws away any data beyond DS. * @@ -961,7 +1017,8 @@ iscsi_bhs_packet *iscsi_append_ds_packet(iscsi_bhs_packet *packet_data, const in return packet_data; } -static const uint32_t crc32c_lut[] = { // Created with a polynomial reflect value of 0x82F63B78 +/// 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, @@ -996,7 +1053,9 @@ static const uint32_t crc32c_lut[] = { // Created with a polynomial reflect valu 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351}; /** - * Calculates CRC32C with 0x82F63B78 polynomial reflect according to iSCSI specs + * @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. @@ -1018,6 +1077,8 @@ static inline uint32_t iscsi_crc32c_update(const uint8_t *data, const uint len, } /** + * @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. @@ -1034,6 +1095,8 @@ void iscsi_calc_header_digest(const iscsi_bhs_packet *packet_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. * @@ -1051,8 +1114,10 @@ int iscsi_validate_header_digest(const iscsi_bhs_packet *packet_data) } /** + * @brief Calculate iSCSI data digest (CRC32C). + * * Calculates data digest (CRC32) with 0x82F63B78 polynomial reflect - * of a whole DataSegment (CRC32C) according to the iSCSI specs. + * 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. @@ -1073,6 +1138,8 @@ void iscsi_calc_data_digest(const iscsi_bhs_packet *packet_data, const int heade } /** + * @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. * @@ -1096,6 +1163,8 @@ int iscsi_validate_data_digest(const iscsi_bhs_packet *packet_data, const int he } /** + * @brief Validates a single text key / value pair according to iSCSI specs. + * * Validates an iSCSI protocol key and value pair for compliance * with the iSCSI specs. * @@ -1132,6 +1201,8 @@ static int iscsi_validate_text_key_value_pair(const uint8_t *packet_data, const } /** + * @brief Validates all text key / value pairs according to iSCSI specs. + * * Validates all iSCSI protocol key and value pairs for * compliance with the iSCSI specs. * @@ -1158,11 +1229,13 @@ static int iscsi_validate_key_value_pairs(const uint8_t *packet_data, uint len) offset += rc; } - return (offset != len) ? ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS : ISCSI_VALIDATE_PACKET_RESULT_OK; + 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; } /** - * Checks whether packet data is an iSCSI packet or not. + * @brief Check if valid iSCSI packet and validate if necessarily. + * + * 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 @@ -1210,8 +1283,8 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint const iscsi_nop_out_packet *nop_out_pkt = (const iscsi_nop_out_packet *) packet_data; - if ( (*(uint16_t *) nop_out_pkt->reserved != 0) || (nop_out_pkt->reserved[2] != 0) || (nop_out_pkt->reserved2[0] != 0) || (nop_out_pkt->reserved2[1] != 0) ) - return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + 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 break; } @@ -1232,43 +1305,43 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint const iscsi_task_mgmt_func_req_packet *task_mgmt_func_req_pkt = (const iscsi_task_mgmt_func_req_packet *) packet_data; - if ( (task_mgmt_func_req_pkt->reserved != 0) || (task_mgmt_func_req_pkt->reserved2 != 0) ) - return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + 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 break; } case ISCSI_CLIENT_LOGIN_REQ : { - if ( (packet_data->opcode != (opcode | 0x40)) || (ahs_len != 0) || (ds_len != 0) ) - return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bit 6 always MUST be set, AHS and DataSegment MUST be zero according to specs -> invalid iSCSI packet data + 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; 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 ( (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; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + if ( ((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_TRANSMIT) != 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; // If C bit is set, T MUST be cleared and reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data return iscsi_validate_key_value_pairs( ((const uint8_t *) login_req_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len ); break; } case ISCSI_CLIENT_TEXT_REQ : { - if ( ((int8_t) packet_data->opcode < 0) || (ds_len == 0) ) - return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // MSB always MUST be cleared for this opcode and DataSegment is mandatory -> invalid iSCSI packet data + 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 const iscsi_text_req_packet *text_req_pkt = (const iscsi_text_req_packet *) packet_data; - if ( (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; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + 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 return iscsi_validate_key_value_pairs( ((const uint8_t *) text_req_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len ); break; } case ISCSI_CLIENT_SCSI_DATA_OUT : { - 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 + 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 const iscsi_scsi_data_out_req_packet *scsi_data_out_req_pkt = (const iscsi_scsi_data_out_req_packet *) packet_data; @@ -1283,8 +1356,8 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint const iscsi_logout_req_packet *logout_req_pkt = (const iscsi_logout_req_packet *) packet_data; - if ( (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; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + 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; } @@ -1294,8 +1367,8 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint const iscsi_snack_req_packet *snack_req_pkt = (const iscsi_snack_req_packet *) packet_data; - if ( (snack_req_pkt->reserved != 0) || (snack_req_pkt->reserved2 != 0) || (snack_req_pkt->reserved3 != 0) ) - return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + 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; } @@ -1310,8 +1383,8 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint const iscsi_nop_in_packet *nop_in_pkt = (const iscsi_nop_in_packet *) packet_data; - if ( (*(uint16_t *) nop_in_pkt->reserved != 0) || (nop_in_pkt->reserved[2] != 0) || (nop_in_pkt->reserved2[0] != 0) || (nop_in_pkt->reserved2[1] != 0) ) - return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + 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; } @@ -1319,6 +1392,11 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint 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 + const iscsi_scsi_response_packet *scsi_response_pkt = (iscsi_scsi_response_packet *) packet_data; + + 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_SERVER_TASK_FUNC_RES : { @@ -1327,43 +1405,43 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint const iscsi_task_mgmt_func_response_packet *task_mgmt_func_response_pkt = (const iscsi_task_mgmt_func_response_packet *) packet_data; - if ( (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; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + 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_SERVER_LOGIN_RES : { - 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 -> invalid iSCSI packet data and DataSegment is mandatory + 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; 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; - if ( (login_response_pkt->reserved != 0) || (login_response_pkt->reserved2 != 0) || (login_response_pkt->reserved3 != 0) ) - return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + if ( ((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_TRANSMIT) != 0) || (login_response_pkt->reserved != 0) || (login_response_pkt->reserved2 != 0) || (login_response_pkt->reserved3 != 0) ) + return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // If C bit is set, T MUST be cleared and reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data return iscsi_validate_key_value_pairs( ((const uint8_t *) login_response_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len ); break; } case ISCSI_SERVER_TEXT_RES : { - 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 + 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 const iscsi_text_response_packet *text_response_pkt = (const iscsi_text_response_packet *) packet_data; - if ( (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; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + 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 return iscsi_validate_key_value_pairs( ((const uint8_t *) text_response_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len ); break; } case ISCSI_SERVER_SCSI_DATA_IN : { - 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 + 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 const iscsi_scsi_data_in_response_packet *scsi_data_in_pkt = (const iscsi_scsi_data_in_response_packet *) packet_data; @@ -1378,8 +1456,8 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint const iscsi_logout_response_packet *logout_response_pkt = (const iscsi_logout_response_packet *) packet_data; - if ( (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; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + 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; } @@ -1389,8 +1467,8 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint const iscsi_r2t_packet *r2t_pkt = (const iscsi_r2t_packet *) packet_data; - if ( r2t_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 ( (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; } @@ -1400,8 +1478,8 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint const iscsi_async_msg_packet *async_msg_pkt = (const iscsi_async_msg_packet *) packet_data; - if ( (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; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data + 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; } @@ -1416,8 +1494,8 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint const iscsi_reject_packet *reject_pkt = (const iscsi_reject_packet *) packet_data; - if ( (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; // Reserved fields need all to be zero, but are NOT, tag always MUST be 0xFFFFFFFFUL -> invalid iSCSI packet data + 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; } @@ -1438,6 +1516,8 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint } /** + * @brief Extracts a single text key / value pairs out of an iSCSI packet into a hash map. + * * Parses and extracts a specific key and value pair out of an iSCSI packet * data stream amd puts the extracted data into a hash map to be used by * the iSCSI implementation. @@ -1524,6 +1604,8 @@ static int iscsi_parse_text_key_value_pair(iscsi_hashmap *pairs, const uint8_t * } /** + * @brief Extracts all text key / value pairs out of an iSCSI packet into a hash map. + * * Parses and extracts all key and value pairs out of iSCSI packet * data amd puts the extracted data into a hash map to be used by * the iSCSI implementation. @@ -1540,7 +1622,7 @@ 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 *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 @@ -1615,6 +1697,8 @@ int iscsi_parse_key_value_pairs(iscsi_hashmap **pairs, const uint8_t *packet_dat } /** + * @brief Creates a single partial iSCSI packet stream out of a single text key and value pair. + * * Callback function for constructing an iSCSI packet data * stream for a single key=value text pair. * @@ -1625,7 +1709,7 @@ int iscsi_parse_key_value_pairs(iscsi_hashmap **pairs, const uint8_t *packet_dat * be careful. * @param[in] value Value of the key, NULL creates an * empty key assignment. - * @param[in] user_data Pointer to a data structure + * @param[in,out] user_data Pointer to a data structure * containing the memory buffer and length for the * iSCSI packet data (iscsi_key_value_pair_packet * structure), may NOT be NULL, so be careful. @@ -1640,7 +1724,7 @@ int iscsi_create_key_value_pair_packet_callback(uint8_t *key, const size_t key_s const uint8_t *buf = iscsi_sprintf_alloc( "%s=%s", key, ((value != NULL) ? value : "") ); if ( buf == NULL ) { - logadd( LOG_ERROR, "iscsi_create_key_value_pair_callback: Out of memory generating text key / value pair DataSegment" ); + logadd( LOG_ERROR, "iscsi_create_key_value_pair_packet_callback: Out of memory generating text key / value pair DataSegment" ); return -1L; } @@ -1651,7 +1735,7 @@ int iscsi_create_key_value_pair_packet_callback(uint8_t *key, const size_t key_s uint8_t *new_buf = realloc( packet_data->buf, new_len ); if ( new_buf == NULL ) { - logadd( LOG_ERROR, "iscsi_create_key_value_pair_callback: Out of memory generating text key / value pair DataSegment" ); + logadd( LOG_ERROR, "iscsi_create_key_value_pair_packet_callback: Out of memory generating text key / value pair DataSegment" ); free( buf ); @@ -1667,6 +1751,8 @@ int iscsi_create_key_value_pair_packet_callback(uint8_t *key, const size_t key_s } /** + * @brief Creates a properly aligned iSCSI packet DataSegment out of a hash map containing text key and value pairs. + * * Creates a properly aligned iSCSI DataSegment * containing NUL terminated key=value pairs * out of an hash map. The result can directly @@ -1723,6 +1809,8 @@ iscsi_key_value_pair_packet *iscsi_create_key_value_pairs_packet(const iscsi_has } /** + * @brief Creates data structure for an iSCSI connection request. + * * Creates a data structure for an incoming iSCSI connection request * from iSCSI packet data. * @@ -1742,7 +1830,7 @@ iscsi_connection *iscsi_connection_create(const iscsi_login_req_packet *login_re return NULL; } - conn->key_value_pairs = iscsi_hashmap_create( 0UL ); + conn->key_value_pairs = iscsi_hashmap_create( 32UL ); if ( conn->key_value_pairs == NULL ) { logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI text key / value pair hash map" ); @@ -1770,6 +1858,8 @@ iscsi_connection *iscsi_connection_create(const iscsi_login_req_packet *login_re } /** + * @brief Deallocates all resources acquired by iscsi_connection_create. + * * Deallocates a data structure of an iSCSI connection * request and all allocated hash maps which don't * require closing of external resources like closing @@ -1795,6 +1885,8 @@ void iscsi_connection_destroy(iscsi_connection *conn) } /** + * @brief iSCSI connection destructor callback for hash map. + * * Callback function for deallocation of an iSCSI * connection stored in the hash map managing all * iSCSI connections. @@ -1805,7 +1897,7 @@ void iscsi_connection_destroy(iscsi_connection *conn) * 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] user_data This argument is not used by + * @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. diff --git a/src/server/iscsi.h b/src/server/iscsi.h index 8035d94..690166b 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -18,6 +18,17 @@ * */ +/** + * @file iscsi.h + * @author Sebastian Vater + * @date 07 Jul 2025 + * @brief iSCSI header for DNBD3. + * + * This file contains the header file for the iSCSI + * implementation according to RFC7143 for dnbd3-server. + * @see https://www.rfc-editor.org/rfc/rfc7143 + */ + #ifndef DNBD3_ISCSI_H_ #define DNBD3_ISCSI_H_ @@ -104,7 +115,7 @@ static inline void iscsi_put_be64(uint8_t *data, const uint64_t val) #error "Unknown CPU endianness" #endif -// Align a value so that it's evenly divisable by n +/// Aligns value x by rounding up, so it's evenly divisable by n. #define iscsi_align(x, n) (((x) + (n) - 1) & ~((n) - 1)) uint8_t *iscsi_vsprintf_append_realloc(char *buf, const char *format, va_list args); // Allocates and appends a buffer and sprintf's it @@ -112,34 +123,84 @@ uint8_t *iscsi_sprintf_append_realloc(char *buf, const char *format, ...); // Al 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 -#define ISCSI_HASHMAP_DEFAULT_CAPACITY_SHIFT 5UL // Shift factor for default capacity -#define ISCSI_HASHMAP_DEFAULT_CAPACITY (1UL << (ISCSI_HASHMAP_DEFAULT_CAPACITY_SHIFT)) // Default capacity is 32 buckets -#define ISCSI_HASHMAP_RESIZE_SHIFT 1UL // Number of bits to shift left when resizing -#define ISCSI_HASHMAP_KEY_ALIGN_SHIFT 3UL // Key data shift value for alignment enforcement -#define ISCSI_HASHMAP_KEY_ALIGN (1UL << (ISCSI_HASHMAP_KEY_ALIGN_SHIFT)) // Key data size must be multiple of 8 bytes by now -#define ISCSI_HASHMAP_HASH_INITIAL 0x811C9DC5UL // Initial hash code -#define ISCSI_HASHMAP_HASH_MUL 0xBF58476D1CE4E5B9ULL // Value to multiply hash code with +/// Shift factor for default capacity. +#define ISCSI_HASHMAP_DEFAULT_CAPACITY_SHIFT 5UL + +/// Default capacity is 32 buckets. +#define ISCSI_HASHMAP_DEFAULT_CAPACITY (1UL << (ISCSI_HASHMAP_DEFAULT_CAPACITY_SHIFT)) + +/// Number of bits to shift left when resizing. +#define ISCSI_HASHMAP_RESIZE_SHIFT 1UL + +/// 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. +#define ISCSI_HASHMAP_KEY_ALIGN (1UL << (ISCSI_HASHMAP_KEY_ALIGN_SHIFT)) +/// Initial hash code. +#define ISCSI_HASHMAP_HASH_INITIAL 0x811C9DC5UL + +/// Value to multiply hash code with. +#define ISCSI_HASHMAP_HASH_MUL 0xBF58476D1CE4E5B9ULL + +/** + * @brief Hash map bucket containing key, value and hash code. + * + * This structure is used by the iSCSI hash map implementation + * in order to maintain the elements. + */ typedef struct iscsi_hashmap_bucket { - struct iscsi_hashmap_bucket *next; // Must be first element + /// Next bucket, must be first element. + struct iscsi_hashmap_bucket *next; + + /// 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_t key_size; + + /// Hash code for the key. + uint32_t hash; - uint8_t *key; // Data used as key, zero padding - size_t key_size; // Size of key, must be a multiple of 8 bytes - uint32_t hash; // Hash code - uint8_t *value; // Value + /// Associate4d value to the key, NULL is allowed. + uint8_t *value; } iscsi_hashmap_bucket; +/** + * @brief Hash map containing an expandable list of buckets + * + * 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. + */ typedef struct iscsi_hashmap { - iscsi_hashmap_bucket *buckets; // Hashmap buckets - uint capacity; // Current capacity in elements + /// Linked list containing the hash map buckets. + iscsi_hashmap_bucket *buckets; + + /// 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 count; // Number of buckets - uint removed_count; // Number of removed buckets - iscsi_hashmap_bucket *first; // First bucket of linked list - iscsi_hashmap_bucket *last; // Last bucket, allows faster traversion + + /// Current count of buckets including ones marked for removal. + 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. + * * Callback function. This is a pointer to a * function for various purposes like iterating * through a hash map. It is also used for replacing @@ -151,6 +212,11 @@ typedef struct iscsi_hashmap { * 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 User data to be used by the + * callback function. User data can be modified if + * desired and may also be NULL if the callback + * function handles this case. See the documentation + * of the callback implementation for details. * @return A negative result indicates as fatal error, * 0 means successful operation and a positive value * indicates a non-fatal error or a warning. @@ -158,8 +224,8 @@ typedef struct iscsi_hashmap { typedef int (*iscsi_hashmap_callback)(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // A Callback for iterating over map, freeing and removing entries. // user_data is free for personal use -iscsi_hashmap *hashmap_create(const uint capacity); // Creates an empty hash map with default capacity specified as above -void iscsi_hashmap_destroy(iscsi_hashmap *map); // Deallocates the hash map objects and buckets, not elements. +iscsi_hashmap *iscsi_hashmap_create(const uint capacity); // Creates an empty hash map with either specified or default capacity +void iscsi_hashmap_destroy(iscsi_hashmap *map); // Deallocates the hash map objects and buckets, not elements // 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_destroy(uint8_t *key); // Deallocates all resources acquired by iscsi_hashmap_create_key @@ -169,7 +235,7 @@ int iscsi_hashmap_get_put(iscsi_hashmap *map, const uint8_t *key, const size_t k int iscsi_hashmap_put_free(iscsi_hashmap *map, const 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_get(iscsi_hashmap *map, const uint8_t *key, const size_t key_size, uint8_t **out_val); // Retrieves the value of a specified key +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. @@ -179,2684 +245,5308 @@ int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, u /* iSCSI protocol stuff (all WORD/DWORD/QWORD values are big endian by default unless specified otherwise). */ -#define ISCSI_BHS_SIZE 48UL // iSCSI Basic Header Segment size -#define ISCSI_DIGEST_SIZE 4UL // iSCSI header and data digest size (CRC32C) -#define ISCSI_ALIGN_SIZE 4UL // iSCSI packet data alignment (BHS, AHS and DS) +/// iSCSI Basic Header Segment size. +#define ISCSI_BHS_SIZE 48UL + +/// iSCSI header and data digest size (CRC32C). +#define ISCSI_DIGEST_SIZE 4UL + +/// iSCSI packet data alignment (BHS, AHS and DataSegment). +#define ISCSI_ALIGN_SIZE 4UL -#define ISCSI_VERSION_MIN 0 // Current minimum iSCSI protocol version supported by this implementation -#define ISCSI_VERSION_MAX 0 // Current maximum iSCSI protocol version supported by this implementation +/// Current minimum iSCSI protocol version supported by this implementation. +#define ISCSI_VERSION_MIN 0 -// CRC32C constants for header and data digest +/// Current maximum iSCSI protocol version supported by this implementation. +#define ISCSI_VERSION_MAX 0 + + +/// CRC32C initial constant for header and data digest. #define ISCSI_CRC32C_INITIAL 0xFFFFFFFFUL + +/// CRC32C initial constant for header and data digest. #define ISCSI_CRC32C_XOR 0xFFFFFFFFUL -// iSCSI initiator (client) command opcodes -#define ISCSI_CLIENT_NOP_OUT 0x00 // NOP-Out -#define ISCSI_CLIENT_SCSI_CMD 0x01 // SCSI Command (encapsulates a SCSI Command Descriptor Block) -#define ISCSI_CLIENT_TASK_FUNC_REQ 0x02 // SCSI Task Management Function Request -#define ISCSI_CLIENT_LOGIN_REQ 0x03 // Login Request -#define ISCSI_CLIENT_TEXT_REQ 0x04 // Text Request -#define ISCSI_CLIENT_SCSI_DATA_OUT 0x05 // SCSI Data-Out (for write operations) -#define ISCSI_CLIENT_LOGOUT_REQ 0x06 // Logout Request -#define ISCSI_CLIENT_SNACK_REQ 0x10 // SNACK Request -#define ISCSI_CLIENT_VENDOR_CODE1 0x1C // Vendor-specific code #1 -#define ISCSI_CLIENT_VENDOR_CODE2 0x1D // Vendor-specific code #2 -#define ISCSI_CLIENT_VENDOR_CODE3 0x1E // Vendor-specific code #3 - -#define ISCSI_CLIENT_FIRST_OPCODE 0x00 // First client opcode value -#define ISCSI_CLIENT_LAST_OPCODE 0x1F // Last client opcode value - -// iSCSI target (server) command opcodes -#define ISCSI_SERVER_NOP_IN 0x20 // NOP-In -#define ISCSI_SERVER_SCSI_RESPONSE 0x21 // SCSI Response - contains SCSI status and possibly sense information or other response information -#define ISCSI_SERVER_TASK_FUNC_RES 0x22 // SCSI Task Management Function Response -#define ISCSI_SERVER_LOGIN_RES 0x23 // Login Response -#define ISCSI_SERVER_TEXT_RES 0x24 // Text Response -#define ISCSI_SERVER_SCSI_DATA_IN 0x25 // SCSI Data-In (for read operations) -#define ISCSI_SERVER_LOGOUT_RES 0x26 // Logout Response -#define ISCSI_SERVER_READY_XFER 0x31 // Ready To Transfer (R2T) - sent by target when it is ready to receive data -#define ISCSI_SERVER_ASYNC_MSG 0x32 // Asynchronous Message - sent by target to indicate certain special conditions -#define ISCSI_SERVER_VENDOR_CODE1 0x3C // Vendor-specific code #1 -#define ISCSI_SERVER_VENDOR_CODE2 0x3D // Vendor-specific code #2 -#define ISCSI_SERVER_VENDOR_CODE3 0x3E // Vendor-specific code #3 -#define ISCSI_SERVER_REJECT 0x3F // Reject - -#define ISCSI_SERVER_FIRST_OPCODE 0x20 // First client opcode value -#define ISCSI_SERVER_LAST_OPCODE 0x3F // Last client opcode value - -#define ISCSI_OPCODE_MASK 0x3F // ISCSI opcode bit mask (bits 0-5 used) -#define ISCSI_GET_OPCODE(x) ((x) & ISCSI_OPCODE_MASK) // Funky macro to get iSCSI packet opcode +/// iSCSI initiator (client) command opcode: NOP-Out. +#define ISCSI_CLIENT_NOP_OUT 0x00 + +/// iSCSI initiator (client) command opcode: SCSI Command (encapsulates a SCSI Command Descriptor Block). +#define ISCSI_CLIENT_SCSI_CMD 0x01 + +/// iSCSI initiator (client) command opcode: SCSI Task Management Function Request. +#define ISCSI_CLIENT_TASK_FUNC_REQ 0x02 + +/// iSCSI initiator (client) command opcode: Login Request. +#define ISCSI_CLIENT_LOGIN_REQ 0x03 + +/// iSCSI initiator (client) command opcode: Text Request. +#define ISCSI_CLIENT_TEXT_REQ 0x04 + +/// iSCSI initiator (client) command opcode: SCSI Data-Out (for write operations). +#define ISCSI_CLIENT_SCSI_DATA_OUT 0x05 + +/// iSCSI initiator (client) command opcode: Logout Request. +#define ISCSI_CLIENT_LOGOUT_REQ 0x06 + +/// iSCSI initiator (client) command opcode: Selective Negative / Sequence Number Acknowledgment (SNACK) Request. +#define ISCSI_CLIENT_SNACK_REQ 0x10 + +/// iSCSI initiator (client) command opcode: Vendor-specific code #1. +#define ISCSI_CLIENT_VENDOR_CODE1 0x1C + +/// iSCSI initiator (client) command opcode: Vendor-specific code #2. +#define ISCSI_CLIENT_VENDOR_CODE2 0x1D + +/// iSCSI initiator (client) command opcode: Vendor-specific code #3. +#define ISCSI_CLIENT_VENDOR_CODE3 0x1E + +/// First iSCSI initiator (client) command opcode. +#define ISCSI_CLIENT_FIRST_OPCODE 0x00 + +/// Last iSCSI initiator (client) command opcode. +#define ISCSI_CLIENT_LAST_OPCODE 0x1F + + +/// iSCSI target (server) command opcode: NOP-In. +#define ISCSI_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 + +/// iSCSI target (server) command opcode: SCSI Task Management Function Response. +#define ISCSI_SERVER_TASK_FUNC_RES 0x22 + +/// iSCSI target (server) command opcode: Login Response. +#define ISCSI_SERVER_LOGIN_RES 0x23 + +/// iSCSI target (server) command opcode: Text Response. +#define ISCSI_SERVER_TEXT_RES 0x24 + +/// iSCSI target (server) command opcode: SCSI Data-In (for read operations). +#define ISCSI_SERVER_SCSI_DATA_IN 0x25 + +/// iSCSI target (server) command opcode: Logout Response. +#define ISCSI_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 + +/// iSCSI target (server) command opcode: Asynchronous Message - sent by target to indicate certain special conditions. +#define ISCSI_SERVER_ASYNC_MSG 0x32 + +/// iSCSI target (server) command opcode: Vendor-specific code #1. +#define ISCSI_SERVER_VENDOR_CODE1 0x3C + +/// iSCSI target (server) command opcode: Vendor-specific code #2. +#define ISCSI_SERVER_VENDOR_CODE2 0x3D + +/// iSCSI target (server) command opcode: Vendor-specific code #3. +#define ISCSI_SERVER_VENDOR_CODE3 0x3E + +/// iSCSI target (server) command opcode: Reject. +#define ISCSI_SERVER_REJECT 0x3F + + +/// First iSCSI target (server) command opcode. +#define ISCSI_SERVER_FIRST_OPCODE 0x20 + +/// Last iSCSI target (server) command opcode. +#define ISCSI_SERVER_LAST_OPCODE 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 +#define ISCSI_GET_OPCODE(x) ((x) & ISCSI_OPCODE_MASK) + +/** + * @brief iSCSI Basic Header Segment packet data. + * + * This structure contains the basic iSCSI packet + * data and is shared among all opcodes. This has + * to be used before the opcode of the packet data + * has been determined. + */ typedef struct __attribute__((packed)) iscsi_bhs_packet { - uint8_t opcode; // Command opcode (see above) - uint8_t opcode_fields[3]; // Opcode-specific fields - uint8_t total_ahs_len; // Total AHS length - uint8_t ds_len[3]; // Data segment length + /// Command opcode. + uint8_t opcode; + + /// Opcode-specific fields. + uint8_t opcode_fields[3]; + + /// Total length of AHS (Advanced Header Segment). + uint8_t total_ahs_len; + + /// Length of Data Segment. + uint8_t ds_len[3]; + union { - uint64_t lun; // LUN bitmask - uint8_t opcode_spec[8]; // Opcode-specific fields + /// SCSI LUN bit mask. + uint64_t lun; + + /// Opcode-specific fields. + uint8_t opcode_spec[8]; } lun_opcode; - uint32_t init_task_tag; // Initiator task tag + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /// Opcode-specific fields. uint8_t opcode_spec_fields[28]; } iscsi_bhs_packet; -#define ISCSI_AHS_TYPE_EXT_CDB_PACKET 0x01 // Command Descriptor Block (CDB) + +/// iSCSI AHS type: Extended Command Descriptor Block (CDB). +#define ISCSI_AHS_TYPE_EXT_CDB_PACKET 0x01 + +/// iSCSI AHS type: Bidirectional Read Expected Data Transfer Length. #define ISCSI_AHS_TYPE_BIDI_READ_EXP_XFER_AHS_PACKET 0x02 + +/** + * @brief iSCSI Advanced Header Segment packet data. + * + * This structure contains the advanced iSCSI packet + * data and is shared among all opcodes. This has + * to be used before the opcode of the packet data + * has been determined. + */ typedef struct __attribute__((packed)) iscsi_ahs_packet { - uint16_t len; // AHSLength - uint8_t type; // AHSType - uint8_t specific; // AHS-Specific - uint8_t data[0]; // AHS-Specific data + /// AHSLength. + uint16_t len; + + /// AHSType. + uint8_t type; + + /// AHS-Specific. + uint8_t specific; + + /// AHS-Specific data. + uint8_t data[0]; } iscsi_ahs_packet; -/* 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. -*/ +/** + * @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; -/* This type of AHS MUST NOT be used if the CDBLength is less than 17. - The length includes the reserved byte 3. -*/ +/** + * @brief iSCSI Extended CDB AHS packet data structure. + * + * This type of AHS MUST NOT be used if the CDBLength is less than 17. + * The length includes the reserved byte 3. + */ typedef struct __attribute__((packed)) iscsi_ext_cdb_ahs_packet { - uint16_t len; // AHSLength - (CDBLength - 15) - uint8_t type; // Identifier (always 0x01 according to specs) - uint8_t reserved; // Reserved for future usage - uint8_t data[0]; // ExtendedCDB + /// AHSLength: AHSLength - (CDBLength - 15). + uint16_t len; + + // AHSType: Identifier (always 1 according to iSCSI specifications). + uint8_t type; + + /// Reserved for future usage, always MUST be 0. + uint8_t reserved; + + /// ExtendedCDB. + uint8_t data[0]; } iscsi_ext_cdb_ahs_packet; +/** + * @brief iSCSI Bidirectional Read Expected Data Transfer Length AHS packet data structure. + * + * This structure is used to determine the bidirectional read + * expected data transfer length. + */ typedef struct __attribute__((packed)) iscsi_bidi_read_exp_xfer_ahs_packet { - uint16_t len; // Always 0x0005 according to specs + /// AHSLength: Always 5 according to ISCSI specifications for now. + uint16_t len; + + /// AHSType: Always 2 according to ISCSI specifications for now. uint8_t type; // Identifier (always 0x02 according to specs) - uint8_t reserved; // Reserved for future usage - uint32_t bidi_read_exp_xfer_len; // Bidirectional Read Expected Data Transfer Length + + /// Reserved for future usage, always MUST be 0. + uint8_t reserved; + + /// Bidirectional Read Expected Data Transfer Length. + uint32_t bidi_read_exp_xfer_len; } iscsi_bidi_read_exp_xfer_ahs_packet; -/* Certain iSCSI conditions result in the command being terminated at - the target (response code of Command Completed at Target) with a SCSI - CHECK CONDITION Status as outlined in the following definitions - (Sense key: Aborted Command 0x0B): -*/ -#define ISCSI_DS_ERROR_UNEXPECTED_UNSOLICITED_DATA_ASC 0x0C // Unexpected unsolicited data + +/** + * @brief DataSegment Error: Unexpected unsolicited data. + * + * Certain iSCSI conditions result in the command being terminated at + * the target (response code of Command Completed at Target) with a SCSI + * CHECK CONDITION Status as outlined in the following definitions + * (Sense key: Aborted Command 0x0B). + */ +#define ISCSI_DS_ERROR_UNEXPECTED_UNSOLICITED_DATA_ASC 0x0C + +/** + * @brief DataSegment Error: Unexpected unsolicited data. + * + * Certain iSCSI conditions result in the command being terminated at + * the target (response code of Command Completed at Target) with a SCSI + * CHECK CONDITION Status as outlined in the following definitions + * (Sense key: Aborted Command 0x0B). + */ #define ISCSI_DS_ERROR_UNEXPECTED_UNSOLICITED_DATA_ASCQ 0x0C -#define ISCSI_DS_ERROR_INCORRECT_AMOUNT_OF_DATA_ASC 0x0C // Incorrect amount of data + +/** + * @brief DataSegment Error: Incorrect amount of data. + * + * Certain iSCSI conditions result in the command being terminated at + * the target (response code of Command Completed at Target) with a SCSI + * CHECK CONDITION Status as outlined in the following definitions + * (Sense key: Aborted Command 0x0B). + */ +#define ISCSI_DS_ERROR_INCORRECT_AMOUNT_OF_DATA_ASC 0x0C + +/** + * @brief DataSegment Error: Incorrect amount of data. + * + * Certain iSCSI conditions result in the command being terminated at + * the target (response code of Command Completed at Target) with a SCSI + * CHECK CONDITION Status as outlined in the following definitions + * (Sense key: Aborted Command 0x0B). + */ #define ISCSI_DS_ERROR_INCORRECT_AMOUNT_OF_DATA_ASCQ 0x0D -#define ISCSI_DS_ERROR_PROTOCOL_SERVICE_CRC_ERROR_ASC 0x47 // Protocol Service CRC error + +/** + * @brief DataSegment Error: Protocol Service CRC error. + * + * Certain iSCSI conditions result in the command being terminated at + * the target (response code of Command Completed at Target) with a SCSI + * CHECK CONDITION Status as outlined in the following definitions + * (Sense key: Aborted Command 0x0B). + */ +#define ISCSI_DS_ERROR_PROTOCOL_SERVICE_CRC_ERROR_ASC 0x47 + +/** + * @brief DataSegment Error: Protocol Service CRC error. + * + * Certain iSCSI conditions result in the command being terminated at + * the target (response code of Command Completed at Target) with a SCSI + * CHECK CONDITION Status as outlined in the following definitions + * (Sense key: Aborted Command 0x0B). + */ #define ISCSI_DS_ERROR_PROTOCOL_SERVICE_CRC_ERROR_ASCQ 0x05 -#define ISCSI_DS_ERROR_SNACK_REJECTED_ASC 0x11 // SNACK rejected + +/** + * @brief DataSegment Error: Selective Negative / Sequence Number Acknowledgment (SNACK) rejected. + * + * Certain iSCSI conditions result in the command being terminated at + * the target (response code of Command Completed at Target) with a SCSI + * CHECK CONDITION Status as outlined in the following definitions + * (Sense key: Aborted Command 0x0B). + */ +#define ISCSI_DS_ERROR_SNACK_REJECTED_ASC 0x11 + +/** + * @brief DataSegment Error: Selective Negative / Sequence Number Acknowledgment (SNACK) rejected. + * + * Certain iSCSI conditions result in the command being terminated at + * the target (response code of Command Completed at Target) with a SCSI + * CHECK CONDITION Status as outlined in the following definitions + * (Sense key: Aborted Command 0x0B). + */ #define ISCSI_DS_ERROR_SNACK_REJECTED_ASCQ 0x13 -/* Optional header and data digests protect the integrity of the header - and data, respectively. The digests, if present, are located, - respectively, after the header and PDU-specific data and cover, - respectively, the header and the PDU data, each including the padding - bytes, if any. - The existence and type of digests are negotiated during the Login - Phase. -*/ +/** + * @brief iSCSI header digest in case CRC32C has been negotiated. + * + * Optional header and data digests protect the integrity of the header + * and data, respectively. The digests, if present, are located, + * respectively, after the header and PDU-specific data and cover, + * respectively, the header and the PDU data, each including the padding + * bytes, if any. + * + * The existence and type of digests are negotiated during the Login + * Phase. + */ typedef struct __attribute__((packed)) iscsi_header_digest { - uint32_t crc32c; // Header digest is a CRC32C for ensuring integrity + /// Header digest is a CRC32C for ensuring integrity. + uint32_t crc32c; } iscsi_header_digest; +/** + * @brief iSCSI data digest in case CRC32C has been negotiated. + * + * Optional header and data digests protect the integrity of the header + * and data, respectively. The digests, if present, are located, + * respectively, after the header and PDU-specific data and cover, + * respectively, the header and the PDU data, each including the padding + * bytes, if any. + * + * The existence and type of digests are negotiated during the Login + * Phase. + */ typedef struct __attribute__((packed)) iscsi_data_digest { - uint32_t crc32c; // Data digest is a CRC32C for ensuring integrity + /// Data digest is a CRC32C for ensuring integrity. + uint32_t crc32c; } iscsi_data_digest; -/* iSCSI targets MUST support and enable Autosense. If Status is CHECK - CONDITION (0x02), then the data segment MUST contain sense data for - the failed command. +/** + * @brief iSCSI 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 + * the failed command. + * + * For some iSCSI responses, the response data segment MAY contain some + * 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 { + /// SenseLength: This field indicates the length of Sense Data. + uint16_t len; - For some iSCSI responses, the response data segment MAY contain some - response-related information (e.g., for a target failure, it may - contain a vendor-specific detailed description of the failure). -*/ + /// The Sense Data contains detailed information about a CHECK CONDITION. SPC3 specifies the format and content of the Sense Data. + uint8_t sense_data[0]; -typedef struct __attribute__((packed)) iscsi_ds_cmd_data { - uint16_t len; // SenseLength - This field indicates the length of Sense Data. - uint8_t sense_data[0]; // The Sense Data contains detailed information about a CHECK CONDITION. - // SPC3 specifies the format and content of the Sense Data. - uint8_t res_data[0]; // Response Data + /// Response Data. + uint8_t res_data[0]; } iscsi_ds_cmd_data; -// SCSI command opcodes (embedded in iSCSI protocol) -#define SCSI_OPCODE_TESTUNITREADY 0x00 // TEST UNIT READY -#define SCSI_OPCODE_READ6 0x08 // READ(6) -#define SCSI_OPCODE_INQUIRY 0x12 // INQUIRY -#define SCSI_OPCODE_MODESELECT6 0x15 // MODE SELECT(6) -#define SCSI_OPCODE_RESERVE6 0x16 // RESERVE(6) -#define SCSI_OPCODE_RELEASE6 0x17 // RELEASE(6) -#define SCSI_OPCODE_MODESENSE6 0x1A // MODE SENSE(6) -#define SCSI_OPCODE_STARTSTOPUNIT 0x1B // START STOP UNIT -#define SCSI_OPCODE_PREVENTALLOW 0x1E // PREVENT ALLOW MEDIUM REMOVAL -#define SCSI_OPCODE_READCAPACITY10 0x25 // READ CAPACITY(10) -#define SCSI_OPCODE_READ10 0x28 // READ(10) -#define SCSI_OPCODE_WRITE10 0x2A // WRITE(10) -#define SCSI_OPCODE_WRITE_VERIFY10 0x2E // WRITE AND VERIFY(10) -#define SCSI_OPCODE_VERIFY10 0x2F // VERIFY(10) -#define SCSI_OPCODE_PREFETCH10 0x34 // PRE-FETCH(10) -#define SCSI_OPCODE_SYNCHRONIZECACHE10 0x35 // SYNCHRONIZE CACHE(10) -#define SCSI_OPCODE_READ_DEFECT_DATA10 0x37 // READ DEFECT DATA(10) -#define SCSI_OPCODE_WRITE_SAME10 0x41 // WRITE SAME(10) -#define SCSI_OPCODE_UNMAP 0x42 // UNMAP -#define SCSI_OPCODE_READTOC 0x43 // READ TOC/PMA/ATIP -#define SCSI_OPCODE_SANITIZE 0x48 // SANITIZE -#define SCSI_OPCODE_MODESELECT10 0x55 // MODE SELECT(10) -#define SCSI_OPCODE_MODESENSE10 0x5A // MODE SENSE(10) -#define SCSI_OPCODE_PERSISTENT_RESERVE_IN 0x5E // PERSISTENT RESERVE IN -#define SCSI_OPCODE_PERSISTENT_RESERVE_OUT 0x5F // PERSISTENT RESERVE OUT -#define SCSI_OPCODE_EXTENDED_COPY 0x83 // Third-party Copy OUT -#define SCSI_OPCODE_RECEIVE_COPY_RESULTS 0x84 // Third-party Copy IN -#define SCSI_OPCODE_READ16 0x88 // READ(16) -#define SCSI_OPCODE_COMPARE_AND_WRITE 0x89 // COMPARE AND WRITE -#define SCSI_OPCODE_WRITE16 0x8A // WRITE(16) -#define SCSI_OPCODE_ORWRITE 0x8B // ORWRITE -#define SCSI_OPCODE_WRITE_VERIFY16 0x8E // WRITE AND VERIFY(16) -#define SCSI_OPCODE_VERIFY16 0x8F // VERIFY(16) -#define SCSI_OPCODE_PREFETCH16 0x90 // PRE-FETCH(16) -#define SCSI_OPCODE_SYNCHRONIZECACHE16 0x91 // SYNCHRONIZE CACHE(16) -#define SCSI_OPCODE_WRITE_SAME16 0x93 // WRITE SAME(16) -#define SCSI_OPCODE_WRITE_ATOMIC16 0x9C // WRITE ATOMIC(16) -#define SCSI_OPCODE_SERVICE_ACTION_IN 0x9E // SERVICE ACTION IN(16) -#define SCSI_OPCODE_REPORTLUNS 0xA0 // REPORT LUNS -#define SCSI_OPCODE_MAINTENANCE_IN 0xA3 // MAINTENANCE IN -#define SCSI_OPCODE_READ12 0xA8 // READ(12) -#define SCSI_OPCODE_WRITE12 0xAA // WRITE(12) -#define SCSI_OPCODE_WRITE_VERIFY12 0xAE // WRITE AND VERIFY(12) -#define SCSI_OPCODE_VERIFY12 0xAF // VERIFY(12) -#define SCSI_OPCODE_READ_DEFECT_DATA12 0xB7 // READ DEFECT DATA(12) - -#define ISCSI_SCSI_CMD_FLAGS_TASK_NO_UNSOLICITED_DATA (1 << 7) // (F) is set to 1 when no unsolicited SCSI Data-Out PDUs - // follow this PDU. When F = 1 for a write and if Expected - // Data Transfer Length is larger than the - // DataSegmentLength, the target may solicit additional data - // through R2T. -#define ISCSI_SCSI_CMD_FLAGS_TASK_READ (1 << 6) // (R) is set to 1 when the command is expected to input data -#define ISCSI_SCSI_CMD_FLAGS_TASK_WRITE (1 << 5) // (W) is set to 1 when the command is expected to output data - -#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_UNTAGGED 0x0 // Untagged task attribute -#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_SIMPLE 0x1 // Simple task attribute -#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_ORDERED 0x2 // Ordered task attribute -#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_HEAD_QUEUE 0x3 // Head of queue task attribute -#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_ACA 0x4 // ACA task attribute -#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_RESERVED_1 0x5 // ACA task attribute -#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_RESERVED_2 0x6 // ACA task attribute -#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_RESERVED_3 0x7 // ACA task attribute - -/* Flags and Task Attributes: - At least one of the W and F bits MUST be set to 1. - Either or both of R and W MAY be 1 when the Expected Data Transfer - Length and/or the Bidirectional Read Expected Data Transfer Length - are 0, but they MUST NOT both be 0 when the Expected Data Transfer - Length and/or Bidirectional Read Expected Data Transfer Length are - not 0 (i.e., when some data transfer is expected, the transfer - direction is indicated by the R and/or W bit). -*/ - -#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_MASK 0x7 // Task Attributes (ATTR) are encoded in the first three LSBs' +/// SCSI command opcode (embedded in iSCSI protocol): TEST UNIT READY. +#define ISCSI_SCSI_OPCODE_TESTUNITREADY 0x00 -typedef struct __attribute__((packed)) iscsi_scsi_cmd_packet { - uint8_t opcode; // Always 0x01 according to specification (see above) - int8_t flags_task; // Flags and Task Attributes - uint16_t reserved; // Reserved for future usage - uint8_t total_ahs_len; // Total AHS length - uint8_t ds_len[3]; // Data segment length - uint64_t lun; // LUN bitmask - uint32_t init_task_tag; // Initiator task tag - uint32_t exp_xfer_len; // Expected Data Transfer Length - // For unidirectional operations, the Expected Data Transfer Length - // field contains the number of bytes of data involved in this SCSI - // operation. For a unidirectional write operation (W flag set to 1 and - // R flag set to 0), the initiator uses this field to specify the number - // of bytes of data it expects to transfer for this operation. For a - // unidirectional read operation (W flag set to 0 and R flag set to 1), - // the initiator uses this field to specify the number of bytes of data - // it expects the target to transfer to the initiator. It corresponds - // to the SAM-2 byte count. - // For bidirectional operations (both R and W flags are set to 1), this - // field contains the number of data bytes involved in the write - // transfer. For bidirectional operations, an additional header segment - // MUST be present in the header sequence that indicates the - // Bidirectional Read Expected Data Transfer Length. The Expected Data - // Transfer Length field and the Bidirectional Read Expected Data - // Transfer Length field correspond to the SAM-2 byte count. - // If the Expected Data Transfer Length for a write and the length of - // the immediate data part that follows the command (if any) are the - // same, then no more data PDUs are expected to follow. In this case, - // the F bit MUST be set to 1. - // If the Expected Data Transfer Length is higher than the - // FirstBurstLength (the negotiated maximum amount of unsolicited data - // the target will accept), the initiator MUST send the maximum amount - // of unsolicited data OR ONLY the immediate data, if any. - // Upon completion of a data transfer, the target informs the initiator - // (through residual counts) of how many bytes were actually processed - // (sent and/or received) by the target. - uint32_t cmd_sn; // The CmdSN enables ordered delivery across multiple connections in a single session - uint32_t exp_stat_sn; // Command responses up to ExpStatSN - 1 (modulo 2**32) have been - // received (acknowledges status) on the connection. - struct iscsi_cdb scsi_cdb; // SCSI Command Descriptor Block (CDB) - // 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. - struct iscsi_ahs_packet ahs; // Optional AHS packet data - struct iscsi_header_digest hdr_digest; // Optional header digest - struct iscsi_ds_cmd_data ds_cmd_data; // Optional data segment, command data - struct iscsi_data_digest data_digest; // Optional data digest -} iscsi_scsi_cmd_packet; +/// SCSI command opcode (embedded in iSCSI protocol): READ(6). +#define ISCSI_SCSI_OPCODE_READ6 0x08 -#define ISCSI_SCSI_RESPONSE_FLAGS_RES_UNDERFLOW (1 << 1) // (U) set for Residual Underflow. In this case, the Residual - // Count indicates the number of bytes that were not - // transferred out of the number of bytes that were expected - // to be transferred. For a bidirectional operation, the - // Residual Count contains the residual for the write - // operation. - -#define ISCSI_SCSI_RESPONSE_FLAGS_RES_OVERFLOW (1 << 2) // (O) set for Residual Overflow. In this case, the Residual - // Count indicates the number of bytes that were not - // transferred because the initiator's Expected Data - // Transfer Length was not sufficient. For a bidirectional - // operation, the Residual Count contains the residual for - // the write operation. - -#define ISCSI_SCSI_RESPONSE_FLAGS_BIDI_READ_RES_UNDERFLOW (1 << 3) // (u) set for Bidirectional Read Residual Underflow. In this - // case, the Bidirectional Read Residual Count indicates the - // number of bytes that were not transferred to the - // initiator out of the number of bytes expected to be - // transferred. - -#define ISCSI_SCSI_RESPONSE_FLAGS_BIDI_READ_RES_OVERFLOW (1 << 4) // (o) set for Bidirectional Read Residual Overflow. In this - // case, the Bidirectional Read Residual Count indicates the - // number of bytes that were not transferred to the - // initiator because the initiator's Bidirectional Read - // Expected Data Transfer Length was not sufficient. - -/* Bits O and U and bits o and u are mutually exclusive (i.e., having - both o and u or O and U set to 1 is a protocol error). - - For a response other than "Command Completed at Target", bits 3-6 - MUST be 0. -*/ +/// SCSI command opcode (embedded in iSCSI protocol): INQUIRY. +#define ISCSI_SCSI_OPCODE_INQUIRY 0x12 -#define ISCSI_SCSI_RESPONSE_STATUS_GOOD 0x00 -#define ISCSI_SCSI_RESPONSE_STATUS_CHECK_COND 0x02 -#define ISCSI_SCSI_RESPONSE_STATUS_BUSY 0x08 -#define ISCSI_SCSI_RESPONSE_STATUS_RES_CONFLICT 0x18 -#define ISCSI_SCSI_RESPONSE_STATUS_TASK_SET_FULL 0x28 -#define ISCSI_SCSI_RESPONSE_STATUS_ACA_ACTIVE 0x30 -#define ISCSI_SCSI_RESPONSE_STATUS_TASK_ABORTED 0x40 - -/* The Status field is used to report the SCSI status of the command (as - specified in SAM2) and is only valid if the response code is - Command Completed at Target. - - If a SCSI device error is detected while data from the initiator is - still expected (the command PDU did not contain all the data and the - target has not received a data PDU with the Final bit set), the - target MUST wait until it receives a data PDU with the F bit set in - the last expected sequence before sending the Response PDU. -*/ +/// SCSI command opcode (embedded in iSCSI protocol): MODE SELECT(6). +#define ISCSI_SCSI_OPCODE_MODESELECT6 0x15 -#define ISCSI_SCSI_RESPONSE_CODE_OK 0x00 // Command Completed at Target -#define ISCSI_SCSI_RESPONSE_CODE_FAIL 0x01 // Target Failure -#define ISCSI_SCSI_RESPONSE_CODE_VENDOR_FIRST 0x80 // First vendor specific response code -#define ISCSI_SCSI_RESPONSE_CODE_VENDOR_LAST 0xFF // Last vendor specific response code +/// SCSI command opcode (embedded in iSCSI protocol): RESERVE(6). +#define ISCSI_SCSI_OPCODE_RESERVE6 0x16 -/* The Response field is used to report a service response. The mapping - of the response code into a SCSI service response code value, if - needed, is outside the scope of this document. However, in symbolic - terms, response value 0x00 maps to the SCSI service response (see -*/ -typedef struct __attribute__((packed)) iscsi_scsi_response_packet { - uint8_t opcode; // Always 0x21 according to specification (see above) - int8_t flags; // Flags (see above) - uint8_t response; // This field contains the iSCSI service response. - uint8_t status; // The Status field is used to report the SCSI status of the command (as - // specified in SAM2) and is only valid if the response code is - // Command Completed at Target. See above for codes. - uint8_t total_ahs_len; // Total AHS length - uint8_t ds_len[3]; // Data segment length - uint64_t reserved; // Reserved for future usage - uint32_t init_task_tag; // Initiator task tag - uint32_t snack_tag; // This field contains a copy of the SNACK Tag of the last SNACK Tag - // accepted by the target on the same connection and for the command for - // which the response is issued. Otherwise, it is reserved and should - // be set to 0. - // After issuing a R-Data SNACK, the initiator must discard any SCSI - // status unless contained in a SCSI Response PDU carrying the same - // SNACK Tag as the last issued R-Data SNACK for the SCSI command on the - // current connection. - uint32_t stat_sn; // StatSN - Status Sequence Number - // The StatSN is a sequence number that the target iSCSI layer generates - // per connection and that in turn enables the initiator to acknowledge - // status reception. The StatSN is incremented by 1 for every - // response/status sent on a connection, except for responses sent as a - // result of a retry or SNACK. In the case of responses sent due to a - // retransmission request, the StatSN MUST be the same as the first time - // the PDU was sent, unless the connection has since been restarted. - uint32_t exp_cmd_sn; // ExpCmdSN - Next Expected CmdSN from This Initiator - // The ExpCmdSN is a sequence number that the target iSCSI returns to - // the initiator to acknowledge command reception. It is used to update - // a local variable with the same name. An ExpCmdSN equal to - // MaxCmdSN + 1 indicates that the target cannot accept new commands. - uint32_t max_cmd_sn; // MaxCmdSN - Maximum CmdSN from This Initiator - // The MaxCmdSN is a sequence number that the target iSCSI returns to - // the initiator to indicate the maximum CmdSN the initiator can send. - // It is used to update a local variable with the same name. If the - // MaxCmdSN is equal to ExpCmdSN - 1, this indicates to the initiator - // that the target cannot receive any additional commands. When the - // MaxCmdSN changes at the target while the target has no pending PDUs - // to convey this information to the initiator, it MUST generate a - // NOP-In to carry the new MaxCmdSN. - uint32_t exp_data_sn; // ExpDataSN or Reserved - // This field indicates the number of Data-In (read) PDUs the target has - // sent for the command. - // This field MUST be 0 if the response code is not Command Completed at - // Target or the target sent no Data-In PDUs for the command. - uint32_t bidi_read_res_cnt; // Bidirectional Read Residual Count or Reserved - // The Bidirectional Read Residual Count field MUST be valid in the case - // where either the u bit or the o bit is set. If neither bit is set, - // the Bidirectional Read Residual Count field is reserved. Targets may - // set the Bidirectional Read Residual Count, and initiators may use it - // when the response code is Command Completed at Target. If the o bit - // is set, the Bidirectional Read Residual Count indicates the number of - // bytes that were not transferred to the initiator because the - // initiator's Bidirectional Read Expected Data Transfer Length was not - // sufficient. If the u bit is set, the Bidirectional Read Residual - // Count indicates the number of bytes that were not transferred to the - // initiator out of the number of bytes expected to be transferred. - uint32_t res_cnt; // Residual Count or Reserved - // The Residual Count field MUST be valid in the case where either the U - // bit or the O bit is set. If neither bit is set, the Residual Count - // field MUST be ignored on reception and SHOULD be set to 0 when - // sending. Targets may set the residual count, and initiators may use - // it when the response code is Command Completed at Target (even if the - // status returned is not GOOD). If the O bit is set, the Residual - // Count indicates the number of bytes that were not transferred because - // the initiator's Expected Data Transfer Length was not sufficient. If - // the U bit is set, the Residual Count indicates the number of bytes - // that were not transferred out of the number of bytes expected to be - // transferred. - struct iscsi_header_digest hdr_digest; // Optional header digest - struct iscsi_ds_cmd_data ds_cmd_data; // Optional data segment, command data - struct iscsi_data_digest data_digest; // Optional data digest -} iscsi_scsi_response_packet; +/// SCSI command opcode (embedded in iSCSI protocol): RELEASE(6). +#define ISCSI_SCSI_OPCODE_RELEASE6 0x17 -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_ABORT_TASK 0x01 // ABORT TASK - aborts the task identified by the Referenced Task Tag field -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_ABORT_TASK_SET 0x02 // ABORT TASK SET - aborts all tasks issued via this session on the LU -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_CLEAR_ACA 0x03 // CLEAR ACA - clears the Auto Contingent Allegiance condition -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_CLEAR_TASK_SET 0x04 // CLEAR TASK SET - aborts all tasks in the appropriate task set - // as defined by the TST field in the Control mode page - // (see SPC3) -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_LOGICAL_UNIT_RESET 0x05 // LOGICAL UNIT RESET -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_TARGET_WARM_RESET 0x06 // TARGET WARM RESET -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_TARGET_COLD_RESET 0x07 // TARGET COLD RESET -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_TASK_REASSIGN 0x08 // TASK REASSIGN - reassigns connection allegiance for the task - // identified by the Initiator Task Tag field to this connection, - // thus resuming the iSCSI exchanges for the task +/// SCSI command opcode (embedded in iSCSI protocol): MODE SENSE(6). +#define ISCSI_SCSI_OPCODE_MODESENSE6 0x1A -typedef struct __attribute__((packed)) iscsi_task_mgmt_func_req_packet { - uint8_t opcode; // Always 0x02 according to specification (see above) - uint8_t func; // Function. - // The task management functions provide an initiator with a way to - // explicitly control the execution of one or more tasks (SCSI and iSCSI - // tasks). The task management function codes are listed below. For a - // more detailed description of SCSI task management, see SAM2. - uint16_t reserved; // Reserved for future usage - uint8_t total_ahs_len; // TotalAHSLength (MUST be 0 for this PDU) - uint8_t ds_len[3]; // DataSegmentLength (MUST be 0 for this PDU) - uint64_t lun; // Logical Unit Number (LUN) or Reserved - // This field is required for functions that address a specific LU - // (ABORT TASK, CLEAR TASK SET, ABORT TASK SET, CLEAR ACA, LOGICAL UNIT - // RESET) and is reserved in all others - uint32_t init_task_tag; // Initiator task tag - // This is the Initiator Task Tag of the task to be aborted for the - // ABORT TASK function or reassigned for the TASK REASSIGN function. - // For all the other functions, this field MUST be set to the reserved - // value 0xFFFFFFFF - uint32_t ref_task_tag; // Referenced task tag or 0xFFFFFFFF - uint32_t cmd_sn; // CmdSN - uint32_t exp_stat_sn; // ExpStatSN - uint32_t ref_cmd_sn; // RefCmdSN or Reserved - // If an ABORT TASK is issued for a task created by an immediate - // command, then the RefCmdSN MUST be that of the task management - // request itself (i.e., the CmdSN and RefCmdSN are equal). - // For an ABORT TASK of a task created by a non-immediate command, the - // RefCmdSN MUST be set to the CmdSN of the task identified by the - // Referenced Task Tag field. Targets must use this field when the task - // identified by the Referenced Task Tag field is not with the target. - // Otherwise, this field is reserved - uint32_t exp_data_sn; // ExpDataSN or Reserved - // For recovery purposes, the iSCSI target and initiator maintain a data - // acknowledgment reference number - the first input DataSN number - // unacknowledged by the initiator. When issuing a new command, this - // number is set to 0. If the function is TASK REASSIGN, which - // establishes a new connection allegiance for a previously issued read - // or bidirectional command, the ExpDataSN will contain an updated data - // acknowledgment reference number or the value 0; the latter indicates - // that the data acknowledgment reference number is unchanged. The - // initiator MUST discard any data PDUs from the previous execution that - // it did not acknowledge, and the target MUST transmit all Data-In PDUs - // (if any) starting with the data acknowledgment reference number. The - // number of retransmitted PDUs may or may not be the same as the - // original transmission, depending on if there was a change in - // MaxRecvDataSegmentLength in the reassignment. The target MAY also - // send no more Data-In PDUs if all data has been acknowledged. - // The value of ExpDataSN MUST be 0 or higher than the DataSN of the - // last acknowledged Data-In PDU, but not larger than DataSN + 1 of the - // last Data-IN PDU sent by the target. Any other value MUST be ignored - // by the target. - // For other functions, this field is reserved - uint64_t reserved2; // Reserved for future usage - struct iscsi_header_digest hdr_digest; // Optional header digest -} iscsi_task_mgmt_func_req_packet; +/// SCSI command opcode (embedded in iSCSI protocol): START STOP UNIT. +#define ISCSI_SCSI_OPCODE_STARTSTOPUNIT 0x1B -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_COMPLETE 0x00 // Function complete -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_NO_EXIST 0x01 // Task does not exist -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_LUN_NO_EXIST 0x02 // LUN does not exist -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_ALLEGIANT 0x03 // Task still allegiant -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_UNSUPPORTED_ALLEGIANCE 0x04 // Task allegiance reassignment not supported -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_UNSUPPORTED_MGMT 0x05 // Task management function not supported -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_AUTH_FAILED 0x06 // Function authorization failed -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_REJECTED 0xFF // Function rejected - -/* For the functions ABORT TASK, ABORT TASK SET, CLEAR ACA, CLEAR TASK - SET, LOGICAL UNIT RESET, TARGET COLD RESET, TARGET WARM RESET, and - TASK REASSIGN, the target performs the requested task management - function and sends a task management response back to the initiator. - For TASK REASSIGN, the new connection allegiance MUST ONLY become - effective at the target after the target issues the task management - response. -*/ -typedef struct __attribute__((packed)) iscsi_task_mgmt_func_response_packet { - uint8_t opcode; // Always 0x22 according to specification (see above) - uint8_t flags; // Reserved for future usage - uint8_t response; // Function response (see above) - // For the TARGET COLD RESET and TARGET WARM RESET functions, the target - // cancels all pending operations across all LUs known to the issuing - // initiator. For the TARGET COLD RESET function, the target MUST then - // close all of its TCP connections to all initiators (terminates all - // sessions). - // The mapping of the response code into a SCSI service response code - // value, if needed, is outside the scope of this document. However, in - // symbolic terms, Response values 0 and 1 map to the SCSI service - // response of FUNCTION COMPLETE. Response value 2 maps to the SCSI - // service response of INCORRECT LOGICAL UNIT NUMBER. All other - // Response values map to the SCSI service response of FUNCTION - // REJECTED. If a Task Management Function Response PDU does not arrive - // before the session is terminated, the SCSI service response is - // SERVICE DELIVERY OR TARGET FAILURE. - // The response to ABORT TASK SET and CLEAR TASK SET MUST only be issued - // by the target after all of the commands affected have been received - // by the target, the corresponding task management functions have been - // executed by the SCSI target, and the delivery of all responses - // delivered until the task management function completion has been - // confirmed (acknowledged through the ExpStatSN) by the initiator on - // all connections of this session. - // For the ABORT TASK function, - // a) if the Referenced Task Tag identifies a valid task leading to a - // successful termination, then targets must return the "Function - // complete" response. - // b) if the Referenced Task Tag does not identify an existing task - // but the CmdSN indicated by the RefCmdSN field in the Task - // Management Function Request is within the valid CmdSN window - // and less than the CmdSN of the Task Management Function Request - // itself, then targets must consider the CmdSN as received and - // return the "Function complete" response. - // c) if the Referenced Task Tag does not identify an existing task - // and the CmdSN indicated by the RefCmdSN field in the Task - // Management Function Request is outside the valid CmdSN window, - // then targets must return the "Task does not exist" response - uint8_t reserved; // Reserved for future usage - uint8_t total_ahs_len; // TotalAHSLength (MUST be 0 for this PDU) - uint8_t ds_len[3]; // DataSegmentLength (MUST be 0 for this PDU) - uint64_t reserved2; // Reserved for future usage - uint32_t init_task_tag; // Initiator task tag - uint32_t reserved3; // Reserved for future usage - uint32_t stat_sn; // StatSN - uint32_t exp_cmd_sn; // ExpCmdSN - uint32_t max_cmd_sn; // MaxCmdSN - uint32_t reserved4; // Reserved for future usage - uint64_t reserved5; // Reserved for future usage - struct iscsi_header_digest hdr_digest; // Optional header digest -} iscsi_task_mgmt_func_response_packet; +/// SCSI command opcode (embedded in iSCSI protocol): PREVENT ALLOW MEDIUM REMOVAL. +#define ISCSI_SCSI_OPCODE_PREVENTALLOW 0x1E -#define ISCSI_SCSI_DATA_OUT_DATA_IN_FLAGS_IMMEDIATE (1 << 7) // Immediately process transfer +/// SCSI command opcode (embedded in iSCSI protocol): READ CAPACITY(10). +#define ISCSI_SCSI_OPCODE_READCAPACITY10 0x25 -typedef struct __attribute__((packed)) iscsi_scsi_data_out_req_packet { - uint8_t opcode; // Always 0x02 according to specification (see above) - int8_t flags; // Flags (see above) - uint16_t reserved; // Reserved for future usage - uint8_t total_ahs_len; // TotalAHSLength (MUST be 0 for this PDU) - uint8_t ds_len[3]; // DataSegmentLength (MUST be 0 for this PDU) - uint64_t lun; // Logical Unit Number (LUN) or Reserved - uint32_t init_task_tag; // Initiator task tag - uint32_t target_xfer_tag; // Target transfer tag or 0xFFFFFFFF - uint32_t reserved2; // Reserved for future usage - uint32_t exp_stat_sn; // ExpStatSN - uint32_t reserved3; // Reserved for future usage - uint32_t data_sn; // DataSN - uint32_t buf_offset; // Buffer offset - uint32_t reserved4; // Reserved for future usage - struct iscsi_header_digest hdr_digest; // Optional header digest - struct iscsi_data_digest data_digest; // Optional data digest -} iscsi_scsi_data_out_req_packet; +/// SCSI command opcode (embedded in iSCSI protocol): READ(10). +#define ISCSI_SCSI_OPCODE_READ10 0x28 -#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS (1 << 0) // (S) set to indicate that the Command Status field - // contains status. If this bit is set to 1, the - // F bit MUST also be set to 1 - -#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_UNDERFLOW (1 << 1) // (U) set for Residual Underflow. In this case, the Residual - // Count indicates the number of bytes that were not - // transferred out of the number of bytes that were expected - // to be transferred. For a bidirectional operation, the - // Residual Count contains the residual for the write - // operation. - -#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_OVERFLOW (1 << 2) // (O) set for Residual Overflow. In this case, the Residual - // Count indicates the number of bytes that were not - // transferred because the initiator's Expected Data - // Transfer Length was not sufficient. For a bidirectional - // operation, the Residual Count contains the residual for - // the write operation. - -#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_ACK (1 << 6) // (A) for sessions with ErrorRecoveryLevel=1 or higher, the target sets - // this bit to 1 to indicate that it requests a positive acknowledgment - // from the initiator for the data received. The target should use the - // A bit moderately; it MAY only set the A bit to 1 once every - // MaxBurstLength bytes, or on the last Data-In PDU that concludes the - // entire requested read data transfer for the task from the target's - // perspective, and it MUST NOT do so more frequently. The target MUST - // NOT set to 1 the A bit for sessions with ErrorRecoveryLevel=0. The - // initiator MUST ignore the A bit set to 1 for sessions with - // ErrorRecoveryLevel=0. - // On receiving a Data-In PDU with the A bit set to 1 on a session with - // ErrorRecoveryLevel greater than 0, if there are no holes in the read - // data until that Data-In PDU, the initiator MUST issue a SNACK of type - // DataACK, except when it is able to acknowledge the status for the - // task immediately via the ExpStatSN on other outbound PDUs if the - // status for the task is also received. In the latter case - // (acknowledgment through the ExpStatSN), sending a SNACK of type - // DataACK in response to the A bit is OPTIONAL, but if it is done, it - // must not be sent after the status acknowledgment through the - // ExpStatSN. If the initiator has detected holes in the read data - // prior to that Data-In PDU, it MUST postpone issuing the SNACK of type - // DataACK until the holes are filled. An initiator also MUST NOT - // acknowledge the status for the task before those holes are filled. A - // status acknowledgment for a task that generated the Data-In PDUs is - // considered by the target as an implicit acknowledgment of the Data-In - // PDUs if such an acknowledgment was requested by the target - -#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_FINAL (1 << 7) // (F) for outgoing data, this bit is 1 for the last PDU of unsolicited - // data or the last PDU of a sequence that answers an R2T. - // For incoming data, this bit is 1 for the last input (read) data PDU - // of a sequence. Input can be split into several sequences, each - // having its own F bit. Splitting the data stream into sequences does - // not affect DataSN counting on Data-In PDUs. It MAY be used as a - // "change direction" indication for bidirectional operations that need - // such a change. - // DataSegmentLength MUST NOT exceed MaxRecvDataSegmentLength for the - // direction it is sent, and the total of all the DataSegmentLength of - // all PDUs in a sequence MUST NOT exceed MaxBurstLength (or - // FirstBurstLength for unsolicited data). However, the number of - // individual PDUs in a sequence (or in total) may be higher than the - // ratio of MaxBurstLength (or FirstBurstLength) to - // MaxRecvDataSegmentLength (as PDUs may be limited in length by the - // capabilities of the sender). Using a DataSegmentLength of 0 may - // increase beyond what is reasonable for the number of PDUs and should - // therefore be avoided. - // For bidirectional operations, the F bit is 1 for both the end of the - // input sequences and the end of the output sequences +/// SCSI command opcode (embedded in iSCSI protocol): WRITE(10). +#define ISCSI_SCSI_OPCODE_WRITE10 0x2A -typedef struct __attribute__((packed)) iscsi_scsi_data_in_response_packet { - uint8_t opcode; // Always 0x25 according to specification (see above) - int8_t flags; // Incoming data flags (see above) - // The fields StatSN, Status, and Residual Count only have meaningful - // content if the S bit is set to 1 - uint8_t reserved; // Rserved for future usage - uint8_t status; // Status or Reserved - // Status can accompany the last Data-In PDU if the command did not end - // with an exception (i.e., the status is "good status" - GOOD, - // CONDITION MET, or INTERMEDIATE-CONDITION MET). The presence of - // status (and of a residual count) is signaled via the S flag bit. - // Although targets MAY choose to send even non-exception status in - // separate responses, initiators MUST support non-exception status in - // Data-In PDUs - uint8_t total_ahs_len; // TotalAHSLength - uint8_t ds_len[3]; // DataSegmentLength - // This is the data payload length of a SCSI Data-In or SCSI Data-Out - // PDU. The sending of 0-length data segments should be avoided, but - // initiators and targets MUST be able to properly receive 0-length data - // segments. - // The data segments of Data-In and Data-Out PDUs SHOULD be filled to - // the integer number of 4-byte words (real payload), unless the F bit - // is set to 1 - uint64_t lun; // Logical Unit Number (LUN) or Reserved - // If the Target Transfer Tag isprovided, then the LUN field MUST hold a - // valid value and be consistent with whatever was specified with the command; - // otherwise, the LUN field is reserved - uint32_t init_task_tag; // Initiator task tag - uint32_t target_xfer_tag; // On outgoing data, the Target Transfer Tag is provided to the target - // if the transfer is honoring an R2T. In this case, the Target - // Transfer Tag field is a replica of the Target Transfer Tag provided - // with the R2T. - // On incoming data, the Target Transfer Tag and LUN MUST be provided by - // the target if the A bit is set to 1; otherwise, they are reserved. - // The Target Transfer Tag and LUN are copied by the initiator into the - // SNACK of type DataACK that it issues as a result of receiving a SCSI - // Data-In PDU with the A bit set to 1. - // The Target Transfer Tag values are not specified by this protocol, - // except that the value 0xFFFFFFFF is reserved and means that the - // Target Transfer Tag is not supplied - uint32_t stat_sn; // StatSN - uint32_t exp_cmd_sn; // ExpCmdSN - uint32_t max_cmd_sn; // MaxCmdSN - uint32_t data_sn; // DataSN - // For input (read) or bidirectional Data-In PDUs, the DataSN is the - // input PDU number within the data transfer for the command identified - // by the Initiator Task Tag. - // R2T and Data-In PDUs, in the context of bidirectional commands, share - // the numbering sequence. - // For output (write) data PDUs, the DataSN is the Data-Out PDU number - // within the current output sequence. Either the current output - // sequence is identified by the Initiator Task Tag (for unsolicited - // data) or it is a data sequence generated for one R2T (for data - // solicited through R2T) - uint32_t buf_offset; // Buffer Offset - // The Buffer Offset field contains the offset of this PDU payload data - // within the complete data transfer. The sum of the buffer offset and - // length should not exceed the expected transfer length for the - // command. - // The order of data PDUs within a sequence is determined by - // DataPDUInOrder. When set to Yes, it means that PDUs have to be in - // increasing buffer offset order and overlays are forbidden. - // The ordering between sequences is determined by DataSequenceInOrder. - // When set to Yes, it means that sequences have to be in increasing - // buffer offset order and overlays are forbidden - uint32_t res_cnt; // Residual Count or Reserved - struct iscsi_header_digest hdr_digest; // Optional header digest - struct iscsi_ds_cmd_data ds_cmd_data; // Data segment - struct iscsi_data_digest data_digest; // Optional data digest -} iscsi_scsi_data_in_response_packet; +/// SCSI command opcode (embedded in iSCSI protocol): WRITE AND VERIFY(10). +#define ISCSI_SCSI_OPCODE_WRITE_VERIFY10 0x2E -/* When an initiator has submitted a SCSI command with data that passes - from the initiator to the target (write), the target may specify - which blocks of data it is ready to receive. The target may request - that the data blocks be delivered in whichever order is convenient - for the target at that particular instant. This information is - passed from the target to the initiator in the Ready To Transfer - (R2T) PDU. - - In order to allow write operations without an explicit initial R2T, - the initiator and target MUST have negotiated the key InitialR2T to - No during login. - - An R2T MAY be answered with one or more SCSI Data-Out PDUs with a - matching Target Transfer Tag. If an R2T is answered with a single - Data-Out PDU, the buffer offset in the data PDU MUST be the same as - the one specified by the R2T, and the data length of the data PDU - MUST be the same as the Desired Data Transfer Length specified in the - R2T. If the R2T is answered with a sequence of data PDUs, the buffer - offset and length MUST be within the range of those specified by the - R2T, and the last PDU MUST have the F bit set to 1. If the last PDU - (marked with the F bit) is received before the Desired Data Transfer - Length is transferred, a target MAY choose to reject that PDU with - the "Protocol Error" reason code. DataPDUInOrder governs the - Data-Out PDU ordering. If DataPDUInOrder is set to Yes, the buffer - offsets and lengths for consecutive PDUs MUST form a continuous - non-overlapping range, and the PDUs MUST be sent in increasing offset - order. - - The target may send several R2T PDUs. It therefore can have a number - of pending data transfers. The number of outstanding R2T PDUs is - limited by the value of the negotiated key MaxOutstandingR2T. Within - a task, outstanding R2Ts MUST be fulfilled by the initiator in the - order in which they were received. - - R2T PDUs MAY also be used to recover Data-Out PDUs. Such an R2T - (Recovery-R2T) is generated by a target upon detecting the loss of - one or more Data-Out PDUs due to: - - - Digest error - - - Sequence error - - - Sequence reception timeout - - A Recovery-R2T carries the next unused R2TSN but requests part of or - the entire data burst that an earlier R2T (with a lower R2TSN) had - already requested. - - DataSequenceInOrder governs the buffer offset ordering in consecutive - R2Ts. If DataSequenceInOrder is Yes, then consecutive R2Ts MUST - refer to continuous non-overlapping ranges, except for Recovery-R2Ts. -*/ -typedef struct __attribute__((packed)) iscsi_r2t_packet { - uint8_t opcode; // Always 0x31 according to specification (see above) - uint8_t flags; // Reserved for future usage - uint16_t reserved; // Reserved for future usage - uint8_t total_ahs_len; // TotalAHSLength, MUST be 0 for this PDU - uint8_t ds_len[3]; // DataSegmentLength, MUST be 0 0 for this PDU - uint64_t lun; // Logical Unit Number (LUN) or Reserved - uint32_t init_task_tag; // Initiator task tag - uint32_t target_xfer_tag; // Target transfer tag - uint32_t stat_sn; // The StatSN field will contain the next StatSN. The StatSN for this - // connection is not advanced after this PDU is sent - uint32_t exp_cmd_sn; // ExpCmdSN - uint32_t max_cmd_sn; // MaxCmdSN - uint32_t data_sn; // DataSN - uint32_t r2t_sn; // R2TSN is the R2T PDU input PDU number within the command identified - // by the Initiator Task Tag. - // For bidirectional commands, R2T and Data-In PDUs share the input PDU - // numbering sequence - uint32_t buf_offset; // Buffer Offset - // The target therefore also specifies a buffer offset that - // indicates the point at which the data transfer should begin, relative - // to the beginning of the total data transfer - uint32_t des_data_xfer_len; // Desired Data Transfer Length - // The target specifies how many bytes it wants the initiator to send - // because of this R2T PDU. The target may request the data from the - // initiator in several chunks, not necessarily in the original order of - // the data. The Desired Data Transfer Length MUST NOT be 0 and MUST NOT - // exceed MaxBurstLength - struct iscsi_header_digest hdr_digest; // Optional header digest -} iscsi_r2t_packet; +/// SCSI command opcode (embedded in iSCSI protocol): VERIFY(10). +#define ISCSI_SCSI_OPCODE_VERIFY10 0x2F -#define ISCSI_ASYNC_MSG_EVENT_SCSI_ASYNC_EVENT 0x00 // (SCSI Async Event) - a SCSI asynchronous event is reported in - // the sense data. Sense Data that accompanies the report, in - // the data segment, identifies the condition. The sending of a - // SCSI event ("asynchronous event reporting" in SCSI - // terminology) is dependent on the target support for SCSI - // asynchronous event reporting as indicated in the - // standard INQUIRY data. Its use may be enabled by - // parameters in the SCSI Control mode page -#define ISCSI_ASYNC_MSG_EVENT_LOGOUT_REQUEST 0x01 // (Logout Request) - the target requests Logout. This Async - // Message MUST be sent on the same connection as the one - // requesting to be logged out. The initiator MUST honor this - // request by issuing a Logout as early as possible but no later - // than Parameter3 seconds. The initiator MUST send a Logout - // with a reason code of "close the connection" OR "close the - // session" to close all the connections. Once this message is - // received, the initiator SHOULD NOT issue new iSCSI commands on - // the connection to be logged out. The target MAY reject any - // new I/O requests that it receives after this message with the - // reason code "Waiting for Logout". If the initiator does not - // log out in Parameter3 seconds, the target should send an Async - // PDU with iSCSI event code "Dropped the connection" if possible - // or simply terminate the transport connection. Parameter1 and - // Parameter2 are reserved -#define ISCSI_ASYNC_MSG_EVENT_CONNECT_DROP_NOTIFY 0x02 // (Connection Drop Notification) - the target indicates that it - // will drop the connection. - // The Parameter1 field indicates the CID of the connection that - // is going to be dropped. - // The Parameter2 field (Time2Wait) indicates, in seconds, the - // minimum time to wait before attempting to reconnect or - // reassign. - // The Parameter3 field (Time2Retain) indicates the maximum time - // allowed to reassign commands after the initial wait (in - // Parameter2). - // If the initiator does not attempt to reconnect and/or reassign - // the outstanding commands within the time specified by - // Parameter3, or if Parameter3 is 0, the target will terminate - // all outstanding commands on this connection. In this case, no - // other responses should be expected from the target for the - // outstanding commands on this connection. - // A value of 0 for Parameter2 indicates that reconnect can be - // attempted immediately -#define ISCSI_ASYNC_MSG_EVENT_SESSION_DROP_NOTIFY 0x03 // (Session Drop Notification) - the target indicates that it - // will drop all the connections of this session. - // The Parameter1 field is reserved. - // The Parameter2 field (Time2Wait) indicates, in seconds, the - // minimum time to wait before attempting to reconnect. - // The Parameter3 field (Time2Retain) indicates the maximum time - // allowed to reassign commands after the initial wait (in - // Parameter2). - // If the initiator does not attempt to reconnect and/or reassign - // the outstanding commands within the time specified by - // Parameter3, or if Parameter3 is 0, the session is terminated. - // In this case, the target will terminate all outstanding - // commands in this session; no other responses should be - // expected from the target for the outstanding commands in this - // session. A value of 0 for Parameter2 indicates that reconnect - // can be attempted immediately -#define ISCSI_ASYNC_MSG_EVENT_NEGOTIATION_REQUEST 0x04 // (Negotiation Request) - the target requests parameter - // negotiation on this connection. The initiator MUST honor this - // request by issuing a Text Request (that can be empty) on the - // same connection as early as possible, but no later than - // Parameter3 seconds, unless a Text Request is already pending - // on the connection, or by issuing a Logout Request. If the - // initiator does not issue a Text Request, the target may - // reissue the Asynchronous Message requesting parameter - // negotiation -#define ISCSI_ASYNC_MSG_EVENT_TASK_TERMINATION 0x05 // (Task Termination) - all active tasks for a LU with a matching - // LUN field in the Async Message PDU are being terminated. The - // receiving initiator iSCSI layer MUST respond to this message - // by taking the following steps, in order: - // - Stop Data-Out transfers on that connection for all active - // TTTs for the affected LUN quoted in the Async Message PDU. - // - Acknowledge the StatSN of the Async Message PDU via a - // NOP-Out PDU with ITT=0xFFFFFFFF (i.e., non-ping flavor), - // while copying the LUN field from the Async Message to - // NOP-Out. - // This value of AsyncEvent, however, MUST NOT be used on an - // iSCSI session unless the new TaskReporting text key was - // negotiated to FastAbort on the session -#define ISCSI_ASYNC_MSG_EVENT_VENDOR_FIRST 0xF8 // First vendor-specific iSCSI event. The AsyncVCode details the - // vendor code, and data MAY accompany the report -#define ISCSI_ASYNC_MSG_EVENT_VENDOR_LAST 0xFF // Last vendor-specific iSCSI event. The AsyncVCode details the - // vendor code, and data MAY accompany the report - -/* An Asynchronous Message may be sent from the target to the initiator - without corresponding to a particular command. The target specifies - the reason for the event and sense data. - Some Asynchronous Messages are strictly related to iSCSI, while - others are related to SCSI -*/ -typedef struct __attribute__((packed)) iscsi_async_msg_packet { - uint8_t opcode; // Always 0x32 according to specification (see above - uint8_t flags; // Reserved for future usage - uint16_t reserved; // Reserved for future usage - uint8_t total_ahs_len; // TotalAHSLength, MUST be 0 for this PDU - uint8_t ds_len[3]; // DataSegmentLength, MUST be 0 0 for this PDU - uint64_t lun; // The LUN field MUST be valid if AsyncEvent is 0. Otherwise, this - // field is reserved - uint32_t tag; // Tag (always 0xFFFFFFFF for now) - uint32_t reserved2; // Reserved for future usage - uint32_t stat_sn; // StatSN - // The StatSN counts this PDU as an acknowledgeable event (the StatSN is - // advanced), which allows for initiator and target state synchronization. - uint32_t exp_cmd_sn; // ExpCmdSN - uint32_t max_cmd_sn; // MaxCmdSN - uint8_t async_event; // AsyncEvent - uint8_t async_vcode; // AsyncVCode is a vendor-specific detail code that is only valid if the - // AsyncEvent field indicates a vendor-specific event. Otherwise, it is - // reserved - uint16_t param_1; // Parameter1 or Reserved - uint16_t param_2; // Parameter2 or Reserved - uint16_t param_3; // Parameter3 or Reserved - uint32_t reserved3; // Reserved for future usage - struct iscsi_header_digest hdr_digest; // Optional header digest - struct iscsi_ds_cmd_data ds_cmd_data; // Data segment - struct iscsi_data_digest data_digest; // Optional data digest -} iscsi_async_msg_packet; +/// SCSI command opcode (embedded in iSCSI protocol): PRE-FETCH(10). +#define ISCSI_SCSI_OPCODE_PREFETCH10 0x34 -/* For a SCSI event, this data accompanies the report in the data - segment and identifies the condition. +/// SCSI command opcode (embedded in iSCSI protocol): SYNCHRONIZE CACHE(10). +#define ISCSI_SCSI_OPCODE_SYNCHRONIZECACHE10 0x35 - 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. +/// SCSI command opcode (embedded in iSCSI protocol): READ DEFECT DATA(10). +#define ISCSI_SCSI_OPCODE_READ_DEFECT_DATA10 0x37 - If the DataSegmentLength is not 0, the format of the DataSegment is - as follows: -*/ -typedef struct __attribute__((packed)) iscsi_sense_event_data_packet { - uint16_t sense_len; // 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_data[0]; // Sense Data - uint16_t event_data[0]; // iSCSI Event Data -} iscsi_sense_event_data_packet; +/// SCSI command opcode (embedded in iSCSI protocol): WRITE SAME(10). +#define ISCSI_SCSI_OPCODE_WRITE_SAME10 0x41 -#define ISCSI_TEXT_REQ_FLAGS_CONTINUE (1 << 6) // (C) When set to 1, this bit indicates that the text (set of key=value - // pairs) in this Text Request is not complete (it will be continued on - // subsequent Text Requests); otherwise, it indicates that this Text - // Request ends a set of key=value pairs. A Text Request with the C bit - // set to 1 MUST have the F bit set to 0. -#define ISCSI_TEXT_REQ_FLAGS_FINAL (1 << 7) // (F) When set to 1, this bit indicates that this is the last or only Text - // Request in a sequence of Text Requests; otherwise, it indicates that - // more Text Requests will follow. - -/* The Text Request is provided to allow for the exchange of information - and for future extensions. It permits the initiator to inform a - target of its capabilities or request some special operations. - - An initiator MUST NOT have more than one outstanding Text Request on - a connection at any given time. - - On a connection failure, an initiator must either explicitly abort - any active allegiant text negotiation task or cause such a task to be - implicitly terminated by the target. -*/ -typedef struct __attribute__((packed)) iscsi_text_req_packet { - uint8_t opcode; // Always 0x04 according to specification (see above) - int8_t flags; // Text request flags (see above) - uint16_t reserved; // Reserved for future usage - uint8_t total_ahs_len; // TotalAHSLength - uint8_t ds_len[3]; // DataSegmentLength - uint64_t lun; // Logical Unit Number (LUN) or Reserved - uint32_t init_task_tag; // This is the initiator-assigned identifier for this Text Request. If - // the command is sent as part of a sequence of Text Requests and - // responses, the Initiator Task Tag MUST be the same for all the - // requests within the sequence (similar to linked SCSI commands). The - // I bit for all requests in a sequence also MUST be the same - uint32_t target_xfer_tag; // When the Target Transfer Tag is set to the reserved value 0xFFFFFFFF, - // it tells the target that this is a new request, and the target resets - // any internal state associated with the Initiator Task Tag (resets the - // current negotiation state). - // The target sets the Target Transfer Tag in a Text Response to a value - // other than the reserved value 0xFFFFFFFF whenever it indicates that - // it has more data to send or more operations to perform that are - // associated with the specified Initiator Task Tag. It MUST do so - // whenever it sets the F bit to 0 in the response. By copying the - // Target Transfer Tag from the response to the next Text Request, the - // initiator tells the target to continue the operation for the specific - // Initiator Task Tag. The initiator MUST ignore the Target Transfer - // Tag in the Text Response when the F bit is set to 1. - // This mechanism allows the initiator and target to transfer a large - // amount of textual data over a sequence of text-command/text-response - // exchanges or to perform extended negotiation sequences. - // If the Target Transfer Tag is not 0xFFFFFFFF, the LUN field MUST be - // sent by the target in the Text Response. - // A target MAY reset its internal negotiation state if an exchange is - // stalled by the initiator for a long time or if it is running out of - // resources. - // Long Text Responses are handled as shown in the following example: - // I->T Text SendTargets=All (F = 1, TTT = 0xFFFFFFFF) - // T->I Text (F = 0, TTT = 0x12345678) - // I->T Text (F = 1, TTT = 0x12345678) - // T->I Text (F = 0, TTT = 0x12345678) - // I->T Text (F = 1, TTT = 0x12345678) - // ... - // T->I Text (F = 1, TTT = 0xFFFFFFFF) - uint32_t cmd_sn; // CmdSN - uint32_t exp_stat_sn; // ExpStatSN - uint64_t reserved2[2]; // Reserved for future usage - struct iscsi_header_digest hdr_digest; // Optional header digest - struct iscsi_ds_cmd_data ds_cmd_data; // Data segment - // The data lengths of a Text Request MUST NOT exceed the iSCSI target - // MaxRecvDataSegmentLength (a parameter that is negotiated per - // connection and per direction). - // A key=value pair can span Text Request or Text Response boundaries. - // A key=value pair can start in one PDU and continue on the next. In - // other words, the end of a PDU does not necessarily signal the end of - // a key=value pair. - // The target responds by sending its response back to the initiator. - // The response text format is similar to the request text format. The - // Text Response MAY refer to key=value pairs presented in an earlier - // Text Request, and the text in the request may refer to earlier - // responses. - // Text operations are usually meant for parameter setting/negotiations - // but can also be used to perform some long-lasting operations - struct iscsi_data_digest data_digest; // Optional data digest -} iscsi_text_req_packet; +/// SCSI command opcode (embedded in iSCSI protocol): UNMAP. +#define ISCSI_SCSI_OPCODE_UNMAP 0x42 -#define ISCSI_TEXT_RESPONSE_FLAGS_CONTINUE (1 << 6) // (C) When set to 1, this bit indicates that the text (set of key=value - // pairs) in this Text Response is not complete (it will be continued on - // subsequent Text Responses); otherwise, it indicates that this Text - // Response ends a set of key=value pairs. A Text Response with the - // C bit set to 1 MUST have the F bit set to 0 -#define ISCSI_TEXT_RESPONSE_FLAGS_FINAL (1 << 7) // (F) When set to 1, in response to a Text Request with the Final bit set - // to 1, the F bit indicates that the target has finished the whole - // operation. Otherwise, if set to 0 in response to a Text Request with - // the Final Bit set to 1, it indicates that the target has more work to - // do (invites a follow-on Text Request). A Text Response with the - // F bit set to 1 in response to a Text Request with the F bit set to 0 - // is a protocol error. - // A Text Response with the F bit set to 1 MUST NOT contain key=value - // pairs that may require additional answers from the initiator. - // A Text Response with the F bit set to 1 MUST have a Target Transfer - // Tag field set to the reserved value 0xFFFFFFFF. - // A Text Response with the F bit set to 0 MUST have a Target Transfer - // Tag field set to a value other than the reserved value 0xFFFFFFFF - -/* The Text Response PDU contains the target's responses to the - initiator's Text Request. The format of the Text field matches that - of the Text Request. -*/ -typedef struct __attribute__((packed)) iscsi_text_response_packet { - uint8_t opcode; // Always 0x24 according to specification (see above) - int8_t flags; // Text response flags (see above) - uint16_t reserved; // Reserved for future usage - uint8_t total_ahs_len; // TotalAHSLength - uint8_t ds_len[3]; // DataSegmentLength - uint64_t lun; // Logical Unit Number (LUN) or Reserved - uint32_t init_task_tag; // The Initiator Task Tag matches the tag used in the initial Text Request - uint32_t target_xfer_tag; // When a target has more work to do (e.g., cannot transfer all the - // remaining text data in a single Text Response or has to continue the - // negotiation) and has enough resources to proceed, it MUST set the - // Target Transfer Tag to a value other than the reserved value - // 0xFFFFFFFF. Otherwise, the Target Transfer Tag MUST be set to - // 0xFFFFFFFF. - // When the Target Transfer Tag is not 0xFFFFFFFF, the LUN field may be - // significant. - // The initiator MUST copy the Target Transfer Tag and LUN in its next - // request to indicate that it wants the rest of the data. - // When the target receives a Text Request with the Target Transfer Tag - // set to the reserved value 0xFFFFFFFF, it resets its internal - // information (resets state) associated with the given Initiator Task - // Tag (restarts the negotiation). - // When a target cannot finish the operation in a single Text Response - // and does not have enough resources to continue, it rejects the Text - // Request with the appropriate Reject code. - // A target may reset its internal state associated with an Initiator - // Task Tag (the current negotiation state) as expressed through the - // Target Transfer Tag if the initiator fails to continue the exchange - // for some time. The target may reject subsequent Text Requests with - // the Target Transfer Tag set to the "stale" value - uint32_t stat_sn; // StatSN. The target StatSN variable is advanced by each Text Response sent - uint32_t exp_cmd_sn; // ExpCmdSN - uint32_t max_cmd_sn; // MaxCmdSN - uint64_t reserved2[2]; // Reserved for future usage - struct iscsi_header_digest hdr_digest; // Optional header digest - struct iscsi_ds_cmd_data ds_cmd_data; // Data segment - // The data lengths of a Text Response MUST NOT exceed the iSCSI - // initiator MaxRecvDataSegmentLength (a parameter that is negotiated - // per connection and per direction). - // The text in the Text Response Data is governed by the same rules as - // the text in the Text Request Data. - // Although the initiator is the requesting party and controls the - // request-response initiation and termination, the target can offer - // key=value pairs of its own as part of a sequence and not only in - // response to the initiator - struct iscsi_data_digest data_digest; // Optional data digest -} iscsi_text_response_packet; +/// SCSI command opcode (embedded in iSCSI protocol): READ TOC/PMA/ATIP. +#define ISCSI_SCSI_OPCODE_READTOC 0x43 -#define ISCSI_ISID_TYPE_BITS (1 << 6) // Two bits - The T field identifies the format and usage of A, B, C, and D as - // indicated below: -#define ISCSI_ISID_TYPE_FORMAT_OUI 0x0 // 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_EN 0x1 // EN: Format (IANA Enterprise Number) - // A: Reserved - // B and C: EN (IANA Enterprise Number) - // D: Qualifier -#define ISCSI_ISID_TYPE_FORMAT_RANDOM 0x2 // Random - // A: Reserved - // B and C: Random - // D: Qualifier - -/* Only the following keys are used during the SecurityNegotiation stage - of the Login Phase (other keys MUST NOT be used): -*/ -#define ISCSI_LOGIN_AUTH_TEXT_KEY_SESSION_TYPE "SessionType" // Use: LO, Declarative, Any-Stage - // Senders: Initiator - // Scope: SW - // SessionType= - // Default is Normal. - // The initiator indicates the type of session it wants to create. The - // target can either accept it or reject it. - // A Discovery session indicates to the target that the only purpose of - // this session is discovery. The only requests a target accepts in - // this type of session are a Text Request with a SendTargets key and a - // Logout Request with reason "close the session". - // The Discovery session implies MaxConnections = 1 and overrides both - // the default and an explicit setting. ErrorRecoveryLevel MUST be 0 - // (zero) for Discovery sessions. - // Depending on the type of session, a target may decide on resources to - // allocate, the security to enforce, etc., for the session. If the - // 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_TEXT_KEY_INITIATOR_NAME "InitiatorName" // Use: IO, Declarative, Any-Stage - // Senders: Initiator - // Scope: SW - // InitiatorName= - // Examples: - // InitiatorName=iqn.1992-04.de.uni-freiburg.bwlehrpool:qcow2.5003 - // InitiatorName=iqn.2001-02.de.uni-freiburg.matrix:basty.eduroam - // InitiatorName=naa.52004567BA64678D - // The initiator of the TCP connection MUST provide this key to the - // remote endpoint at the first login of the Login Phase for every - // connection. The InitiatorName key enables the initiator to identify - // itself to the remote endpoint. - // The InitiatorName MUST NOT be redeclared within the Login Phase -#define ISCSI_LOGIN_AUTH_TEXT_KEY_TARGET_NAME "TargetName" // Use: IO by initiator, FFPO by target - only as response to a - // SendTargets, Declarative, Any-Stage - // Senders: Initiator and target - // Scope: SW - // TargetName= - // Examples: - // TargetName=iqn.1993-11.de.uni-freiburg:diskarrays.sn.5003 - // TargetName=eui.020000023B040506 - // TargetName=naa.62004567BA64678D0123456789ABCDEF - // The initiator of the TCP connection MUST provide this key to the - // remote endpoint in the first Login Request if the initiator is not - // establishing a Discovery session. The iSCSI Target Name specifies - // the worldwide unique name of the target. - // The TargetName key may also be returned by the SendTargets Text - // Request (which is its only use when issued by a target). - // The TargetName MUST NOT be redeclared within the Login Phase -#define ISCSI_LOGIN_AUTH_TEXT_KEY_TARGET_ADDRESS "TargetAddress" // Use: ALL, Declarative, Any-Stage - // Senders: Target - // Scope: SW - // TargetAddress=domainname[:port][,portal-group-tag] - // The domainname can be specified as either a DNS host name, a dotted- - // decimal IPv4 address, or a bracketed IPv6 address as specified in - // RFC3986. - // If the TCP port is not specified, it is assumed to be the IANA- - // assigned default port for iSCSI. - // If the TargetAddress is returned as the result of a redirect status - // in a Login Response, the comma and portal-group-tag MUST be omitted. - // If the TargetAddress is returned within a SendTargets response, the - // portal-group-tag MUST be included. - // Examples: - // TargetAddress=10.0.0.1:5003,1 - // TargetAddress=[1080:0:0:0:8:800:200C:417A],65 - // TargetAddress=[1080::8:800:200C:417A]:5003,1 - // TargetAddress=gitlab.uni-freiburg.de,443 - // The formats for the port and portal-group-tag are the same as the one - // specified in TargetPortalGroupTag -#define ISCSI_LOGIN_AUTH_TEXT_KEY_INITIATOR_ALIAS "InitiatorAlias" // Use: ALL, Declarative, Any-Stage - // Senders: Initiator - // Scope: SW - // InitiatorAlias= - // Examples: - // InitiatorAlias=Web Server 5 - // InitiatorAlias=matrix.uni-freiburg.de - // InitiatorAlias=Matrix Server - // If an initiator has been configured with a human-readable name or - // description, it SHOULD be communicated to the target during a Login - // Request PDU. If not, the host name can be used instead. This string - // is not used as an identifier, nor is it meant to be used for - // authentication or authorization decisions. It can be displayed by - // the target's user interface in a list of initiators to which it is - // connected -#define ISCSI_LOGIN_AUTH_TEXT_KEY_TARGET_ALIAS "TargetAlias" // Use: ALL, Declarative, Any-Stage - // Senders: Target - // Scope: SW - // TargetAlias= - // Examples: - // TargetAlias=Bob-s Disk - // TargetAlias=Database Server 1 Log Disk - // TargetAlias=Web Server 3 Disk 20 - // If a target has been configured with a human-readable name or - // description, this name SHOULD be communicated to the initiator during - // a Login Response PDU if SessionType=Normal. This string is not used - // as an identifier, nor is it meant to be used for authentication or - // 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_TEXT_KEY_TARGET_PORTAL_GROUP_TAG "TargetPortalGroupTag" // Use: IO by target, Declarative, Any-Stage - // Senders: Target - // Scope: SW - // TargetPortalGroupTag=<16-bit-binary-value> - // Example: - // TargetPortalGroupTag=1 - // The TargetPortalGroupTag key is a 16-bit binary-value that uniquely - // identifies a portal group within an iSCSI target node. This key - // carries the value of the tag of the portal group that is servicing - // the Login Request. The iSCSI target returns this key to the - // initiator in the Login Response PDU to the first Login Request PDU - // that has the C bit set to 0 when TargetName is given by the - // initiator. - // SAM2 notes in its informative text that the TPGT value should be - // non-zero; note that this is incorrect. A zero value is allowed as a - // legal value for the TPGT. This discrepancy currently stands - // corrected in SAM4 -#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD "AuthMethod" // Use: During Login - Security Negotiation - // Senders: Initiator and target - // Scope: connection - // AuthMethod = - // The main item of security negotiation is the authentication method - // (AuthMethod). - // The authentication methods that can be used (appear in the list-of- - // values) are either vendor-unique methods or those listed in the - // following table: - // +--------------------------------------------------------------+ - // | Name | Description | - // +--------------------------------------------------------------+ - // | KRB5 | Kerberos V5 - defined in RFC4120 | - // +--------------------------------------------------------------+ - // | SRP | Secure Remote Password - | - // | | defined in RFC2945 | - // +--------------------------------------------------------------+ - // | CHAP | Challenge Handshake Authentication Protocol - | - // | | defined in RFC1994 | - // +--------------------------------------------------------------+ - // | None | No authentication | - // +--------------------------------------------------------------+ - // The AuthMethod selection is followed by an "authentication exchange" - // specific to the authentication method selected. - // The authentication method proposal may be made by either the - // initiator or the target. However, the initiator MUST make the first - // step specific to the selected authentication method as soon as it is - // selected. It follows that if the target makes the authentication - // method proposal, the initiator sends the first key(s) of the exchange - // together with its authentication method selection. - // The authentication exchange authenticates the initiator to the target - // and, optionally, the target to the initiator. Authentication is - // OPTIONAL to use but MUST be supported by the target and initiator. - // The initiator and target MUST implement CHAP. All other - // authentication methods are OPTIONAL. - // Private or public extension algorithms MAY also be negotiated for - // authentication methods. Whenever a private or public extension - // algorithm is part of the default offer (the offer made in the absence - // of explicit administrative action), the implementer MUST ensure that - // CHAP is listed as an alternative in the default offer and "None" is - // not part of the default offer. - // Extension authentication methods MUST be named using one of the - // following two formats: - // 1) Z-reversed.vendor.dns_name.do_something= - // 2) New public key with no name prefix constraints - // Authentication methods named using the Z- format are used as private - // extensions. New public keys must be registered with IANA using the - // IETF Review process RFC5226. New public extensions for - // authentication methods MUST NOT use the Z# name prefix. - // For all of the public or private extension authentication methods, - // the method-specific keys MUST conform to the format specified for - // standard-label. - // To identify the vendor for private extension authentication methods, - // we suggest using the reversed DNS-name as a prefix to the proper - // digest names. - // The part of digest-name following Z- MUST conform to the format for - // standard-label. - // Support for public or private extension authentication methods is - // OPTIONAL - -/* Kerberos V5 (KRB5) related authentication keys: */ -#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_KRB_AP_REQ "KRB_AP_REQ" // For KRB5 (Kerberos V5) (see RFC4120 and RFC1964), the initiator MUST use: - // KRB_AP_REQ= - // where KRB_AP_REQ is the client message as defined in RFC4120. - // The default principal name assumed by an iSCSI initiator or target - // (prior to any administrative configuration action) MUST be the iSCSI - // Initiator Name or iSCSI Target Name, respectively, prefixed by the - // string "iscsi/". - // If the initiator authentication fails, the target MUST respond with a - // Login reject with "Authentication Failure" status. Otherwise, if the - // initiator has selected the mutual authentication option (by setting - // MUTUAL-REQUIRED in the ap-options field of the KRB_AP_REQ), the - // target MUST reply with: -#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_KRB_AP_REP "KRB_AP_REP" // KRB_AP_REP= - // where KRB_AP_REP is the server's response message as defined in - // RFC4120. - // If mutual authentication was selected and target authentication - // fails, the initiator MUST close the connection. - // KRB_AP_REQ and KRB_AP_REP are binary-values, and their binary length - // (not the length of the character string that represents them in - // encoded form) MUST NOT exceed 65536 bytes. Hex or Base64 encoding - // may be used for KRB_AP_REQ and KRB_AP_REP - -/* Secure Remote Password (SRP) related authentication keys: */ -#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_SRP_SRP_U "SRP_U" // For SRP RFC2945, the initiator MUST use: - // SRP_U= TargetAuth=Yes or TargetAuth=No - // The target MUST answer with a Login reject with the "Authorization - // Failure" status or reply with: -#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_SRP_SRP_GROUP "SRP_GROUP" // SRP_GROUP= SRP_s= - // where G1,G2... are proposed groups, in order of preference. -#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_SRP_SRP_A "SRP_A" // The initiator MUST either close the connection or continue with: - // SRP_A= - // SRP_GROUP= - // where G is one of G1,G2... that were proposed by the target. - // The target MUST answer with a Login reject with the "Authentication - // Failure" status or reply with: -#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_SRP_SRP_B "SRP_B" // SRP_B= - // The initiator MUST close the connection or continue with: -#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_SRP_SRP_M "SRP_M" // SRP_M= - // If the initiator authentication fails, the target MUST answer with a - // Login reject with "Authentication Failure" status. Otherwise, if the - // initiator sent TargetAuth=Yes in the first message (requiring target - // authentication), the target MUST reply with: -#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_SRP_SRP_HM "SRP_HM" // SRP_HM= - // If the target authentication fails, the initiator MUST close the - // connection: - // where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using - // the SHA1 hash function, such as SRP-SHA1) and - // G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups - // specified in RFC3723. - // G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are - // binary-values. The length of s,A,B,M and H(A | M | K) in binary form - // (not the length of the character string that represents them in - // encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may - // be used for s,A,B,M and H(A | M | K). - // For the SRP_GROUP, all the groups specified in RFC3723 up to - // 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be - // supported by initiators and targets. To guarantee interoperability, - // targets MUST always offer "SRP-1536" as one of the proposed groups - -/* Challenge Handshake Authentication Protocol (CHAP) related authentication keys: */ -#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_A "CHAP_A" // For CHAP RFC1994, the initiator MUST use: - // CHAP_A= - // where A1,A2... are proposed algorithms, in order of preference. - // The target MUST answer with a Login reject with the "Authentication - // Failure" status or reply with: - // CHAP_A= -#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_I "CHAP_I" // CHAP_I= -#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_C "CHAP_C" // CHAP_C= - // where A is one of A1,A2... that were proposed by the initiator. - // The initiator MUST continue with: -#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_N "CHAP_N" // CHAP_N= -#define ISCSI_LOGIN_AUTH_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_R "CHAP_R" // CHAP_R= - // or, if it requires target authentication, with: - // CHAP_N= - // CHAP_R= - // CHAP_I= - // CHAP_C= - // If the initiator authentication fails, the target MUST answer with a - // Login reject with "Authentication Failure" status. Otherwise, if the - // initiator required target authentication, the target MUST either - // answer with a Login reject with "Authentication Failure" or reply - // with: - // CHAP_N= - // CHAP_R= - // If the target authentication fails, the initiator MUST close the - // connection: - // where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, - // Algorithm, Identifier, Challenge, and Response as defined in - // RFC1994. - // N is a text string; A,A1,A2, and I are numbers; C and R are - // binary-values. Their binary length (not the length of the character - // string that represents them in encoded form) MUST NOT exceed - // 1024 bytes. Hex or Base64 encoding may be used for C and R. - // For the Algorithm, as stated in [RFC1994], one value is required to - // be implemented: - // 5 (CHAP with MD5) - // To guarantee interoperability, initiators MUST always offer it as one - // of the proposed algorithms +/// SCSI command opcode (embedded in iSCSI protocol): SANITIZE. +#define ISCSI_SCSI_OPCODE_SANITIZE 0x48 -/* Login/Text Operational Text Keys +/// SCSI command opcode (embedded in iSCSI protocol): MODE SELECT(10). +#define ISCSI_SCSI_OPCODE_MODESELECT10 0x55 - Some session-specific parameters MUST only be carried on the leading - connection and cannot be changed after the leading connection login - (e.g., MaxConnections - the maximum number of connections). This - holds for a single connection session with regard to connection - restart. The keys that fall into this category have the "use: LO" - (Leading Only). +/// SCSI command opcode (embedded in iSCSI protocol): MODE SENSE(10). +#define ISCSI_SCSI_OPCODE_MODESENSE10 0x5A - Keys that can only be used during login have the "use: IO" - (Initialize Only), while those that can be used in both the Login - Phase and Full Feature Phase have the "use: ALL". +/// SCSI command opcode (embedded in iSCSI protocol): PERSISTENT RESERVE IN. +#define ISCSI_SCSI_OPCODE_PERSISTENT_RESERVE_IN 0x5E - Keys that can only be used during the Full Feature Phase use FFPO - (Full Feature Phase Only). +/// SCSI command opcode (embedded in iSCSI protocol): PERSISTENT RESERVE OUT. +#define ISCSI_SCSI_OPCODE_PERSISTENT_RESERVE_OUT 0x5F - Keys marked as Any-Stage may also appear in the SecurityNegotiation - stage, while all other keys described in this section are - operational keys. +/// SCSI command opcode (embedded in iSCSI protocol): Third-party Copy OUT. +#define ISCSI_SCSI_OPCODE_EXTENDED_COPY 0x83 - Keys that do not require an answer are marked as Declarative. +/// SCSI command opcode (embedded in iSCSI protocol): Third-party Copy IN. +#define ISCSI_SCSI_OPCODE_RECEIVE_COPY_RESULTS 0x84 - Key scope is indicated as session-wide (SW) or connection-only (CO). +/// SCSI command opcode (embedded in iSCSI protocol): READ(16). +#define ISCSI_SCSI_OPCODE_READ16 0x88 - "Result function", wherever mentioned, states the function that can - be applied to check the validity of the responder selection. - "Minimum" means that the selected value cannot exceed the offered - value. "Maximum" means that the selected value cannot be lower than - the offered value. "AND" means that the selected value must be a - possible result of a Boolean "and" function with an arbitrary Boolean - value (e.g., if the offered value is No the selected value must be - No). "OR" means that the selected value must be a possible result of - a Boolean "or" function with an arbitrary Boolean value (e.g., if the - offered value is Yes the selected value must be Yes). -*/ -#define ISCSI_LOGIN_AUTH_TEXT_KEY_HEADER_DIGEST "HeaderDigest" // Use: IO -#define ISCSI_LOGIN_AUTH_TEXT_KEY_DATA_DIGEST "DataDigest" // Senders: Initiator and target - // Scope: CO - // HeaderDigest = - // DataDigest = - // Default is None for both HeaderDigest and DataDigest. - // Digests enable the checking of end-to-end, non-cryptographic data - // integrity beyond the integrity checks provided by the link layers and - // the covering of the whole communication path, including all elements - // that may change the network-level PDUs, such as routers, switches, - // and proxies. - // The following table lists cyclic integrity checksums that can be - // negotiated for the digests and MUST be implemented by every iSCSI - // initiator and target. These digest options only have error detection - // significance. - // +---------------------------------------------+ - // | Name | Description | Generator | - // +---------------------------------------------+ - // | CRC32C | 32-bit CRC |0x11edc6f41| - // +---------------------------------------------+ - // | None | no digest | - // +---------------------------------------------+ - // The generator polynomial G(x) for this digest is given in hexadecimal - // notation (e.g. "0x3b" stands for 0011 1011, and the polynomial is - // x**5 + x**4 + x**3 + x + 1). - // When the initiator and target agree on a digest, this digest MUST be - // used for every PDU in the Full Feature Phase. - // Padding bytes, when present in a segment covered by a CRC, SHOULD be - // set to 0 and are included in the CRC. - // The CRC MUST be calculated by a method that produces the same results - // as the following process: - // - The PDU bits are considered as the coefficients of a polynomial - // M(x) of degree n - 1; bit 7 of the lowest numbered byte is - // considered the most significant bit (x**n - 1), followed by bit 6 - // of the lowest numbered byte through bit 0 of the highest numbered - // byte (x**0). - // - The most significant 32 bits are complemented. - // - The polynomial is multiplied by x**32, then divided by G(x). The - // generator polynomial produces a remainder R(x) of degree <= 31. - // - The coefficients of R(x) are formed into a 32-bit sequence. - // - The bit sequence is complemented, and the result is the CRC. - // - The CRC bits are mapped into the digest word. The x**31 - // coefficient is mapped to bit 7 of the lowest numbered byte of the - // digest, and the mapping continues with successive coefficients and - // bits so that the x**24 coefficient is mapped to bit 0 of the lowest - // numbered byte. The mapping continues further with the x**23 - // coefficient mapped to bit 7 of the next byte in the digest until - // the x**0 coefficient is mapped to bit 0 of the highest numbered - // byte of the digest. - // - Computing the CRC over any segment (data or header) extended to - // include the CRC built using the generator 0x11edc6f41 will always - // get the value 0x1c2d19ed as its final remainder (R(x)). This value - // is given here in its polynomial form (i.e., not mapped as the - // digest word). - // For a discussion about selection criteria for the CRC, see RFC3385. - // For a detailed analysis of the iSCSI polynomial, see Castagnoli93. - // Private or public extension algorithms MAY also be negotiated for - // digests. Whenever a private or public digest extension algorithm is - // part of the default offer (the offer made in the absence of explicit - // administrative action), the implementer MUST ensure that CRC32C is - // listed as an alternative in the default offer and "None" is not part - // of the default offer. - // Extension digest algorithms MUST be named using one of the following - // two formats: - // 1) Y-reversed.vendor.dns_name.do_something= - // 2) New public key with no name prefix constraints - // Digests named using the Y- format are used for private purposes - // (unregistered). New public keys must be registered with IANA using - // the IETF Review process (RFC5226). New public extensions for - // digests MUST NOT use the Y# name prefix. - // For private extension digests, to identify the vendor we suggest - // using the reversed DNS-name as a prefix to the proper digest names. - // The part of digest-name following Y- MUST conform to the format for - // standard-label specified. - // Support for public or private extension digests is OPTIONA -#define ISCSI_LOGIN_AUTH_TEXT_KEY_MAX_CONNECTIONS "MaxConnections" // Use: LO - // Senders: Initiator and target - // Scope: SW - // Irrelevant when: SessionType=Discovery - // MaxConnections= - // Default is 1. - // Result function is Minimum. - // The initiator and target negotiate the maximum number of connections - // requested/acceptable -#define ISCSI_LOGIN_AUTH_TEXT_KEY_SEND_TARGETS "SendTargets" // Use: FFPO - // Senders: Initiator - // Scope: SW - // The text in this appendix is a normative part of this document. - // To reduce the amount of configuration required on an initiator, iSCSI - // provides the SendTargets Text Request. The initiator uses the - // SendTargets request to get a list of targets to which it may have - // access, as well as the list of addresses (IP address and TCP port) on - // which these targets may be accessed. - // To make use of SendTargets, an initiator must first establish one of - // two types of sessions. If the initiator establishes the session - // using the key "SessionType=Discovery", the session is a Discovery - // session, and a target name does not need to be specified. Otherwise, - // the session is a Normal operational session. The SendTargets command - // MUST only be sent during the Full Feature Phase of a Normal or - // Discovery session. - // A system that contains targets MUST support Discovery sessions on - // each of its iSCSI IP address-port pairs and MUST support the - // SendTargets command on the Discovery session. In a Discovery - // session, a target MUST return all path information (IP address-port - // pairs and Target Portal Group Tags) for the targets on the target - // Network Entity that the requesting initiator is authorized to access. - // A target MUST support the SendTargets command on operational - // sessions; these will only return path information about the target to - // which the session is connected and do not need to return information - // about other target names that may be defined in the responding - // system. - // An initiator MAY make use of the SendTargets command as it sees fit. - // A SendTargets command consists of a single Text Request PDU. This - // PDU contains exactly one text key and value. The text key MUST be - // SendTargets. The expected response depends upon the value, as well - // as whether the session is a Discovery session or an operational - // session. - // The value must be one of: - // All - // The initiator is requesting that information on all relevant - // targets known to the implementation be returned. This value - // MUST be supported on a Discovery session and MUST NOT be - // supported on an operational session. - // - // If an iSCSI Target Name is specified, the session should - // respond with addresses for only the named target, if possible. - // This value MUST be supported on Discovery sessions. A - // Discovery session MUST be capable of returning addresses for - // those targets that would have been returned had value=All been - // designated. - // - // The session should only respond with addresses for the target - // to which the session is logged in. This MUST be supported on - // operational sessions and MUST NOT return targets other than the - // one to which the session is logged in. - // The response to this command is a Text Response that contains a list - // of zero or more targets and, optionally, their addresses. Each - // target is returned as a target record. A target record begins with - // the TargetName text key, followed by a list of TargetAddress text - // keys, and bounded by the end of the Text Response or the next - // TargetName key, which begins a new record. No text keys other than - // TargetName and TargetAddress are permitted within a SendTargets - // response. - // A Discovery session MAY respond to a SendTargets request with its - // complete list of targets, or with a list of targets that is based on - // the name of the initiator logged in to the session. - // A SendTargets response MUST NOT contain target names if there are no - // targets for the requesting initiator to access. - // Each target record returned includes zero or more TargetAddress - // fields. - // Each target record starts with one text key of the form: - // TargetName= - // followed by zero or more address keys of the form: - // TargetAddress=[:], - // - // The hostname-or-ipaddress contains a domain name, IPv4 address, or - // IPv6 address (RFC4291), as specified for the TargetAddress key. - // A hostname-or-ipaddress duplicated in TargetAddress responses for a - // given node (the port is absent or equal) would probably indicate that - // multiple address families are in use at once (IPv6 and IPv4). - // Each TargetAddress belongs to a portal group, identified by its - // numeric Target Portal Group Tag. The iSCSI Target - // Name, together with this tag, constitutes the SCSI port identifier; - // the tag only needs to be unique within a given target's name list of - // addresses. - // Multiple-connection sessions can span iSCSI addresses that belong to - // the same portal group. - // Multiple-connection sessions cannot span iSCSI addresses that belong - // to different portal groups. - // If a SendTargets response reports an iSCSI address for a target, it - // SHOULD also report all other addresses in its portal group in the - // same response. - // A SendTargets Text Response can be longer than a single Text Response - // PDU and makes use of the long Text Responses as specified. - // After obtaining a list of targets from the Discovery session, an - // iSCSI initiator may initiate new sessions to log in to the discovered - // targets for full operation. The initiator MAY keep the Discovery - // session open and MAY send subsequent SendTargets commands to discover - // new targets. - // Examples: - // This example is the SendTargets response from a single target that - // has no other interface ports. - // The initiator sends a Text Request that contains: - // SendTargets=All - // The target sends a Text Response that contains: - // TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.8675309 - // All the target had to return in this simple case was the target name. - // It is assumed by the initiator that the IP address and TCP port for - // this target are the same as those used on the current connection to - // the default iSCSI target. - // The next example has two internal iSCSI targets, each accessible via - // two different ports with different IP addresses. The following is - // the Text Response: - // TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.8675309 - // TargetAddress=10.1.0.45:5300,1 - // TargetAddress=10.1.1.45:5300,2 - // TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.1234567 - // TargetAddress=10.1.0.45:5300,1 - // TargetAddress=10.1.1.45:5300,2 - // Both targets share both addresses; the multiple addresses are likely - // used to provide multi-path support. The initiator may connect to - // either target name on either address. Each of the addresses has its - // own Target Portal Group Tag; they do not support spanning multiple- - // connection sessions with each other. Keep in mind that the Target - // Portal Group Tags for the two named targets are independent of one - // another; portal group "1" on the first target is not necessarily the - // same as portal group "1" on the second target. - // In the above example, a DNS host name or an IPv6 address could have - // been returned instead of an IPv4 address. - // The next Text Response shows a target that supports spanning sessions - // across multiple addresses and further illustrates the use of the - // Target Portal Group Tags: - // TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.8675309 - // TargetAddress=10.1.0.45:5300,1 - // TargetAddress=10.1.1.46:5300,1 - // TargetAddress=10.1.0.47:5300,2 - // TargetAddress=10.1.1.48:5300,2 - // TargetAddress=10.1.1.49:5300,3 - // In this example, any of the target addresses can be used to reach the - // same target. A single-connection session can be established to any - // of these TCP addresses. A multiple-connection session could span - // addresses .45 and .46 or .47 and .48 but cannot span any other - // combination. A TargetAddress with its own tag (.49) cannot be - // combined with any other address within the same session. - // This SendTargets response does not indicate whether .49 supports - // multiple connections per session; it is communicated via the - // MaxConnections text key upon login to the target -#define ISCSI_LOGIN_AUTH_TEXT_KEY_INITIAL_R2T "InitialR2T" // Use: LO - // Senders: Initiator and target - // Scope: SW - // Irrelevant when: SessionType=Discovery - // InitialR2T= - // Examples: - // I->InitialR2T=No - // T->InitialR2T=No - // Default is Yes. - // Result function is OR. - // The InitialR2T key is used to turn off the default use of R2T for - // unidirectional operations and the output part of bidirectional - // commands, thus allowing an initiator to start sending data to a - // target as if it has received an initial R2T with Buffer - // Offset=Immediate Data Length and Desired Data Transfer - // Length=(min(FirstBurstLength, Expected Data Transfer Length) - - // Received Immediate Data Length). - // The default action is that R2T is required, unless both the initiator - // and the target send this key-pair attribute specifying InitialR2T=No. - // 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_TEXT_KEY_IMMEDIATE_DATA "ImmediateData" // Use: LO - // Senders: Initiator and target - // Scope: SW - // Irrelevant when: SessionType=Discovery - // ImmediateData= - // Default is Yes. - // Result function is AND. - // The initiator and target negotiate support for immediate dataTo - // 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. - // If ImmediateData is set to Yes and InitialR2T is set to Yes - // (default), then only immediate data are accepted in the first burst. - // If ImmediateData is set to No and InitialR2T is set to Yes, then the - // initiator MUST NOT send unsolicited data and the target MUST reject - // unsolicited data with the corresponding response code. - // If ImmediateData is set to No and InitialR2T is set to No, then the - // initiator MUST NOT send unsolicited immediate data but MAY send one - // unsolicited burst of Data-OUT PDUs. - // If ImmediateData is set to Yes and InitialR2T is set to No, then the - // initiator MAY send unsolicited immediate data and/or one unsolicited - // burst of Data-OUT PDUs. - // The following table is a summary of unsolicited data options: - // +----------+-------------+------------------+-------------+ - // |InitialR2T|ImmediateData| Unsolicited |ImmediateData| - // | | | Data-Out PDUs | | - // +----------+-------------+------------------+-------------+ - // | No | No | Yes | No | - // +----------+-------------+------------------+-------------+ - // | No | Yes | Yes | Yes | - // +----------+-------------+------------------+-------------+ - // | Yes | No | No | No | - // +----------+-------------+------------------+-------------+ - // | Yes | Yes | No | Yes | - // +----------+-------------+------------------+-------------+ -#define ISCSI_LOGIN_AUTH_TEXT_KEY_MAX_RECV_DS_LEN "MaxRecvDataSegmentLength" // Use: ALL, Declarative - // Senders: Initiator and target - // Scope: CO - // MaxRecvDataSegmentLength= - // Default is 8192 bytes. - // The initiator or target declares the maximum data segment length in - // bytes it can receive in an iSCSI PDU. - // The transmitter (initiator or target) is required to send PDUs with a - // data segment that does not exceed MaxRecvDataSegmentLength of the - // receiver. - // A target receiver is additionally limited by MaxBurstLength for - // solicited data and FirstBurstLength for unsolicited dataAn - // initiator MUST NOT send solicited PDUs exceeding MaxBurstLength nor - // unsolicited PDUs exceeding FirstBurstLength (or FirstBurstLength- - // Immediate Data Length if immediate data were sent) -#define ISCSI_LOGIN_AUTH_TEXT_KEY_MAX_BURST_LEN "MaxBurstLength" // Use: LO - // Senders: Initiator and target - // Scope: SW - // Irrelevant when: SessionType=Discovery - // MaxBurstLength= - // Default is 262144 (256 KB). - // Result function is Minimum. - // The initiator and target negotiate the maximum SCSI data payload in - // bytes in a Data-In or a solicited Data-Out iSCSI sequence. A - // 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_TEXT_KEY_FIRST_BURST_LEN "FirstBurstLength" // Use: LO - // Senders: Initiator and target - // Scope: SW - // Irrelevant when: SessionType=Discovery - // Irrelevant when: ( InitialR2T=Yes and ImmediateData=No ) - // FirstBurstLength= - // Default is 65536 (64 KB). - // Result function is Minimum. - // The initiator and target negotiate the maximum amount in bytes of - // unsolicited data an iSCSI initiator may send to the target during the - // execution of a single SCSI command. This covers the immediate data - // (if any) and the sequence of unsolicited Data-Out PDUs (if any) that - // follow the command. - // FirstBurstLength MUST NOT exceed MaxBurstLength -#define ISCSI_LOGIN_AUTH_TEXT_KEY_DEFAULT_TIME_WAIT "DefaultTime2Wait" // Use: LO - // Senders: Initiator and target - // Scope: SW - // DefaultTime2Wait= - // Default is 2. - // Result function is Maximum. - // The initiator and target negotiate the minimum time, in seconds, to - // wait before attempting an explicit/implicit logout or an active task - // reassignment after an unexpected connection termination or a - // connection reset. - // A value of 0 indicates that logout or active task reassignment can be - // attempted immediately -#define ISCSI_LOGIN_AUTH_TEXT_KEY_DEFAULT_TIME_RETAIN "DefaultTime2Retain" // Use: LO - // Senders: Initiator and target - // Scope: SW - // DefaultTime2Retain= - // Default is 20. - // Result function is Minimum. - // The initiator and target negotiate the maximum time, in seconds, - // after an initial wait (Time2Wait), before which an active task - // reassignment is still possible after an unexpected connection - // termination or a connection reset. - // This value is also the session state timeout if the connection in - // question is the last LOGGED_IN connection in the session. - // A value of 0 indicates that connection/task state is immediately - // discarded by the target -#define ISCSI_LOGIN_AUTH_TEXT_KEY_MAX_OUTSTANDING_R2T "MaxOutstandingR2T" // Use: LO - // Senders: Initiator and target - // Scope: SW - // MaxOutstandingR2T= - // Irrelevant when: SessionType=Discovery - // Default is 1. - // Result function is Minimum. - // The initiator and target negotiate the maximum number of outstanding - // R2Ts per task, excluding any implied initial R2T that might be part - // of that task. An R2T is considered outstanding until the last data - // 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_TEXT_KEY_DATA_PDU_IN_ORDER "DataPDUInOrder" // Use: LO - // Senders: Initiator and target - // Scope: SW - // Irrelevant when: SessionType=Discovery - // DataPDUInOrder= - // Default is Yes. - // Result function is OR. - // "No" is used by iSCSI to indicate that the data PDUs within sequences - // can be in any order. "Yes" is used to indicate that data PDUs within - // sequences have to be at continuously increasing addresses and - // overlays are forbidden -#define ISCSI_LOGIN_AUTH_TEXT_KEY_DATA_SEQ_IN_ORDER "DataSequenceInOrder" // Use: LO - // Senders: Initiator and target - // Scope: SW - // Irrelevant when: SessionType=Discovery - // DataSequenceInOrder= - // Default is Yes. - // Result function is OR. - // A data sequence is a sequence of Data-In or Data-Out PDUs that end - // with a Data-In or Data-Out PDU with the F bit set to 1. A Data-Out - // sequence is sent either unsolicited or in response to an R2T. - // Sequences cover an offset-range. - // If DataSequenceInOrder is set to No, data PDU sequences may be - // transferred in any order. - // If DataSequenceInOrder is set to Yes, data sequences MUST be - // transferred using continuously non-decreasing sequence offsets (R2T - // buffer offset for writes, or the smallest SCSI Data-In buffer offset - // within a read data sequence). - // If DataSequenceInOrder is set to Yes, a target may retry at most the - // last R2T, and an initiator may at most request retransmission for the - // last read data sequence. For this reason, if ErrorRecoveryLevel is - // not 0 and DataSequenceInOrder is set to Yes, then MaxOutstandingR2T - // MUST be set to 1 -#define ISCSI_LOGIN_AUTH_TEXT_KEY_ERR_RECOVERY_LEVEL "ErrorRecoveryLevel" // Use: LO - // Senders: Initiator and target - // Scope: SW - // ErrorRecoveryLevel= - // Default is 0. - // Result function is Minimum. - // The initiator and target negotiate the recovery level supported. - // Recovery levels represent a combination of recovery capabilities. - // Each recovery level includes all the capabilities of the lower - // recovery levels and adds some new ones to them. - // In the description of recovery mechanisms, certain recovery classes - // are specified -#define ISCSI_LOGIN_AUTH_TEXT_KEY_PRIV_EXT_KEY_FMT "X-reversed.vendor" // Use: ALL - // Senders: Initiator and target - // Scope: specific key dependent - // X-reversed.vendor.dns_name.do_something= - // Keys with this format are used for private extension purposes. These - // keys always start with X- if unregistered with IANA (private). New - // public keys (if registered with IANA via an IETF Review RFC5226) no - // longer have an X# name prefix requirement; implementers may propose - // any intuitive unique name. - // For unregistered keys, to identify the vendor we suggest using the - // reversed DNS-name as a prefix to the key-proper. - // The part of key-name following X- MUST conform to the format for - // key-name. - // Vendor-specific keys MUST ONLY be used in Normal sessions. - // Support for public or private extension keys is OPTIONAL -#define ISCSI_LOGIN_AUTH_TEXT_KEY_TASK_REPORTING "TaskReporting" // Use: LO - // Senders: Initiator and target - // Scope: SW - // Irrelevant when: SessionType=Discovery - // TaskReporting= - // Default is RFC3720. - // This key is used to negotiate the task completion reporting semantics - // from the SCSI target. The following table describes the semantics - // that an iSCSI target MUST support for respective negotiated key - // values. Whenever this key is negotiated, at least the RFC3720 and - // ResponseFence values MUST be offered as options by the negotiation - // originator. - // +--------------+------------------------------------------+ - // | Name | Description | - // +--------------+------------------------------------------+ - // | RFC3720 | RFC 3720-compliant semantics. Response | - // | | fencing is not guaranteed, and fast | - // | | completion of multi-task aborting is not | - // | | supported. | - // +--------------+------------------------------------------+ - // | ResponseFence| Response Fence | - // | | semantics MUST be supported in reporting | - // | | task completions. | - // +--------------+------------------------------------------+ - // | FastAbort | Updated fast multi-task abort semantics | - // | | defined in MUST be supported. Support | - // | | for the Response. Fence is implied - | - // | | i.e., semantics MUST be supported as | - // | | well. | - // +--------------+------------------------------------------+ - // When TaskReporting is not negotiated to FastAbort, the - // standard multi-task abort semantics MUST be used -#define ISCSI_LOGIN_AUTH_TEXT_KEY_X_NODE_ARCH "X#NodeArchitecture" // Use: LO, Declarative - // Senders: Initiator and target - // Scope: SW - // X#NodeArchitecture= - // Default is None. - // Examples: - // X#NodeArchitecture=ExampleOS/v1234,ExampleInc_SW_Initiator/1.05a - // X#NodeArchitecture=ExampleInc_HW_Initiator/4010,Firmware/2.0.0.5 - // X#NodeArchitecture=ExampleInc_SW_Initiator/2.1,CPU_Arch/i686 - // This document does not define the structure or content of the list of - // values. - // The initiator or target declares the details of its iSCSI node - // architecture to the remote endpoint. These details may include, but - // are not limited to, iSCSI vendor software, firmware, or hardware - // versions; the OS version; or hardware architecture. This key may be - // declared on a Discovery session or a Normal session. - // The length of the key value (total length of the list-of-values) MUST - // NOT be greater than 255 bytes. - // X#NodeArchitecture MUST NOT be redeclared during the Login Phase. - // Functional behavior of the iSCSI node (this includes the iSCSI - // protocol logic - the SCSI, iSCSI, and TCP/IP protocols) MUST NOT - // depend on the presence, absence, or content of the X#NodeArchitecture - // key. The key MUST NOT be used by iSCSI nodes for interoperability or - // for exclusion of other nodes. To ensure proper use, key values - // SHOULD be set by the node itself, and there SHOULD NOT be provisions - // for the key values to contain user-defined text. - // Nodes implementing this key MUST choose one of the following - // implementation options: - // - only transmit the key, - // - only log the key values received from other nodes, or - // - both transmit and log the key values. - // Each node choosing to implement transmission of the key values MUST - // be prepared to handle the response of iSCSI nodes that do not - // understand the key. - // Nodes that implement transmission and/or logging of the key values - // may also implement administrative mechanisms that disable and/or - // change the logging and key transmission details. - // Thus, a valid behavior for this key may be that a node is completely - // 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_TEXT_KEY_IF_MARKER "IFMarker" // Obsoleted Keys -#define ISCSI_LOGIN_AUTH_TEXT_KEY_OF_MARKER "OFMarker" // This document obsoletes the following keys defined in RFC3720: -#define ISCSI_LOGIN_AUTH_TEXT_KEY_OF_MARK_INT "OFMarkInt" // IFMarker, OFMarker, OFMarkInt, and IFMarkInt. However, iSCSI -#define ISCSI_LOGIN_AUTH_TEXT_KEY_IF_MARK_INT "IFMarkInt" // implementations compliant to this document may still receive these - // obsoleted keys - i.e., in a responder role - in a text negotiation. - // When an IFMarker or OFMarker key is received, a compliant iSCSI - // implementation SHOULD respond with the constant "Reject" value. The - // implementation MAY alternatively respond with a "No" value. - // However, the implementation MUST NOT respond with a "NotUnderstood" - // value for either of these keys. - // When an IFMarkInt or OFMarkInt key is received, a compliant iSCSI - // implementation MUST respond with the constant "Reject" value. The - // implementation MUST NOT respond with a "NotUnderstood" value for - // either of these keys - -/* This is an initiator-defined component of the session identifier and - is structured as follows: - - For the T field values 00b and 01b, a combination of A and B (for - 00b) or B and C (for 01b) identifies the vendor or organization whose - component (software or hardware) generates this ISID. A vendor or - organization with one or more OUIs, or one or more Enterprise - Numbers, MUST use at least one of these numbers and select the - appropriate value for the T field when its components generate ISIDs. - An OUI or EN MUST be set in the corresponding fields in network byte - order (byte big-endian). - - If the T field is 10b, B and C are set to a random 24-bit unsigned - integer value in network byte order (byte big-endian). - - The Qualifier field is a 16-bit or 24-bit unsigned integer value that - provides a range of possible values for the ISID within the selected - namespace. It may be set to any value within the constraints - specified in the iSCSI protocol. - - If the ISID is derived from something assigned to a hardware adapter - or interface by a vendor as a preset default value, it MUST be - configurable to a value assigned according to the SCSI port behavior - desired by the system in which it is installed. The resultant ISID - MUST also be persistent over power cycles, reboot, card swap, etc. -*/ -typedef struct __attribute__((packed)) iscsi_isid { - uint8_t a; // Meaning depends on T bit (see above) - uint16_t b; // Meaning depends on T bit (see above) - uint8_t c; // Meaning depends on T bit (see above) - uint16_t d; // Meaning depends on T bit (see above) -} iscsi_isid; +/// SCSI command opcode (embedded in iSCSI protocol): COMPARE AND WRITE. +#define ISCSI_SCSI_OPCODE_COMPARE_AND_WRITE 0x89 -#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION 0x0 // SecurityNegotiation -#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 // LoginOperationalNegotiation -#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_FULL_FEATURE_PHASE 0x3 // FullFeaturePhase -#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE (1 << 0) // (NSG) - Two bits - the Login negotiation requests and responses are - // associated with a specific stage in the session (SecurityNegotiation, - // LoginOperationalNegotiation, FullFeaturePhase) and may indicate the - // 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_REQS_FLAGS_GET_NEXT_STAGE(x) ((x) & 3) +/// SCSI command opcode (embedded in iSCSI protocol): WRITE(16). +#define ISCSI_SCSI_OPCODE_WRITE16 0x8A -#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_SECURITY_NEGOTIATION 0x0 // SecurityNegotiation -#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 // LoginOperationalNegotiation -#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_FULL_FEATURE_PHASE 0x3 // FullFeaturePhase -#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE (1 << 2) // (CSG) - 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_REQS_FLAGS_GET_CURRENT_STAGE(x) (((x) >> 2) & 3) +/// SCSI command opcode (embedded in iSCSI protocol): ORWRITE. +#define ISCSI_SCSI_OPCODE_ORWRITE 0x8B -#define ISCSI_LOGIN_REQ_FLAGS_CONTINUE (1 << 6) // (C) When set to 1, this bit indicates that the text (set of key=value - // pairs) in this Login Request is not complete (it will be continued on - // subsequent Login Requests); otherwise, it indicates that this Login - // Request ends a set of key=value pairs. A Login Request with the - // C bit set to 1 MUST have the T bit set to 0. -#define ISCSI_LOGIN_REQ_FLAGS_TRANSMIT (1 << 7) // (T) When set to 1, this bit indicates that the initiator is ready to - // transit to the next stage. - // If the T bit is set to 1 and the NSG is set to FullFeaturePhase, then - // this also indicates that the initiator is ready for the Login - // Final-Response. - -/* After establishing a TCP connection between an initiator and a - target, the initiator MUST start a Login Phase to gain further access - to the target's resources. - - The Login Phase consists of a sequence of Login Requests and Login - Responses that carry the same Initiator Task Tag. - - Login Requests are always considered as immediate. -*/ -typedef struct __attribute__((packed)) iscsi_login_req_packet { - uint8_t opcode; // Always 0x03 according to specification (see above) - int8_t flags; // Login request flags (see above) - uint8_t version_max; // Version-max indicates the maximum version number supported. - // All Login Requests within the Login Phase MUST carry the same - // Version-max. Currently, this is always 0 - // The target MUST use the value presented with the first Login Request. - uint8_t version_min; // All Login Requests within the Login Phase MUST carry the same - // Version-min. The target MUST use the value presented with the first - // Login Request. Always 0 for now - uint8_t total_ahs_len; // TotalAHSLength - uint8_t ds_len[3]; // DataSegmentLength - struct iscsi_isid isid; // ISID (see above for declaration) - uint16_t tsih; // The TSIH must be set in the first Login Request. The reserved value - // 0 MUST be used on the first connection for a new session. Otherwise, - // the TSIH sent by the target at the conclusion of the successful login - // of the first connection for this session MUST be used. The TSIH - // identifies to the target the associated existing session for this new - // connection. - // All Login Requests within a Login Phase MUST carry the same TSIH. - // The target MUST check the value presented with the first Login - // Request - uint32_t init_task_tag; // Initiator task tag - uint16_t cid; // Connection ID. The CID provides a unique ID for this connection within the session. - // All Login Requests within the Login Phase MUST carry the same CID. - // The target MUST use the value presented with the first Login Request. - // A Login Request with a non-zero TSIH and a CID equal to that of an - // existing connection implies a logout of the connection followed by a - // login - uint16_t reserved; // Reserved for future usage - uint32_t cmd_sn; // The CmdSN is either the initial command sequence number of a session - // (for the first Login Request of a session - the "leading" login) or - // the command sequence number in the command stream if the login is for - // a new connection in an existing session. - // Examples: - // - Login on a leading connection: If the leading login carries the - // CmdSN 123, all other Login Requests in the same Login Phase carry - // the CmdSN 123, and the first non-immediate command in the Full - // Feature Phase also carries the CmdSN 123. - // - Login on other than a leading connection: If the current CmdSN at - // the time the first login on the connection is issued is 500, then - // that PDU carries CmdSN=500. Subsequent Login Requests that are - // needed to complete this Login Phase may carry a CmdSN higher than - // 500 if non-immediate requests that were issued on other connections - // in the same session advance the CmdSN. - // If the Login Request is a leading Login Request, the target MUST use - // the value presented in the CmdSN as the target value for the - // ExpCmdSN - uint32_t exp_stat_sn; // For the first Login Request on a connection, this is the ExpStatSN - // for the old connection, and this field is only valid if the Login - // Request restarts a connection - // For subsequent Login Requests, it is used to acknowledge the Login - // Responses with their increasing StatSN values - uint64_t reserved2[2]; // Reserved for future usage - struct iscsi_ds_cmd_data ds_cmd_data; // Data segment - Login Parameters in Text Request Format - // The initiator MUST provide some basic parameters in order - // 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_login_req_packet; +/// SCSI command opcode (embedded in iSCSI protocol): WRITE AND VERIFY(16). +#define ISCSI_SCSI_OPCODE_WRITE_VERIFY16 0x8E -#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION 0x0 // SecurityNegotiation -#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 // LoginOperationalNegotiation -#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FULL_FEATURE_PHASE 0x3 // FullFeaturePhase -#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE (1 << 0) // (NSG) - Two bits - the Login negotiation requests and responses are - // associated with a specific stage in the session (SecurityNegotiation, - // LoginOperationalNegotiation, FullFeaturePhase) and may indicate the - // 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_GET_NEXT_STAGE(x) ((x) & 3) +/// SCSI command opcode (embedded in iSCSI protocol): VERIFY(16). +#define ISCSI_SCSI_OPCODE_VERIFY16 0x8F -#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_SECURITY_NEGOTIATION 0x0 // SecurityNegotiation -#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 // LoginOperationalNegotiation -#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FULL_FEATURE_PHASE 0x3 // FullFeaturePhase -#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE (1 << 2) // (CSG) - 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_RESPONSE_FLAGS_GET_CURRENT_STAGE(x) (((x) >> 2) & 3) +/// SCSI command opcode (embedded in iSCSI protocol): PRE-FETCH(16). +#define ISCSI_SCSI_OPCODE_PREFETCH16 0x90 -#define ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE (1 << 6) // (C) When set to 1, this bit indicates that the text (set of key=value - // pairs) in this Login Response is not complete (it will be continued - // on subsequent Login Responses); otherwise, it indicates that this - // Login Response ends a set of key=value pairs. A Login Response with - // the C bit set to 1 MUST have the T bit set to 0 -#define ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT (1 << 7) // (T) The T bit is set to 1 as an indicator of the end of the stage. If - // the T bit is set to 1 and the NSG is set to FullFeaturePhase, then - // this is also the Login Final-Response. A T bit of 0 indicates a - // "partial" response, which means "more negotiation needed". - // A Login Response with the T bit set to 1 MUST NOT contain key=value - // pairs that may require additional answers from the initiator within - // the same stage. - // If the Status-Class is 0, the T bit MUST NOT be set to 1 if the T bit - // in the request was set to 0 - -#define ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SUCCESS 0x00 // Success - indicates that the iSCSI target successfully - // received, understood, and accepted the request. The numbering - // fields (StatSN, ExpCmdSN, MaxCmdSN) are only valid if Status- - // Class is 0 -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SUCCESS 0x00 // Login is proceeding OK. If the response T bit is set to 1 in both the - // request and the matching response, and the NSG is set to - // FullFeaturePhase in both the request and the matching response, the - // Login Phase is finished, and the initiator may proceed to issue SCSI - // commands. - -#define ISCSI_LOGIN_RESPONSE_STATUS_CLASS_REDIRECT 0x01 // Redirection - indicates that the initiator must take further - // action to complete the request. This is usually due to the - // target moving to a different address. All of the redirection - // Status-Class responses MUST return one or more text key - // parameters of the type "TargetAddress", which indicates the - // target's new address. A redirection response MAY be issued by - // a target prior to or after completing a security negotiation if - // a security negotiation is required. A redirection SHOULD be - // accepted by an initiator, even without having the target - // complete a security negotiation if any security negotiation is - // required, and MUST be accepted by the initiator after the - // completion of the security negotiation if any security - // negotiation is required -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_REDIRECT_TEMP 0x01 // The requested iSCSI Target Name (ITN) has temporarily moved - // to the address provided -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_REDIRECT_PERM 0x02 // The requested ITN has permanently moved to the address provided - -#define ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR 0x02 // Initiator Error (not a format error) - indicates that the - // initiator most likely caused the error. This MAY be due to a - // request for a resource for which the initiator does not have - // permission. The request should not be tried again -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC 0x00 // Miscellaneous iSCSI initiator errors -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_ERR 0x01 // The initiator could not be successfully - // authenticated or target authentication is - // not supported -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_FAIL 0x02 // The initiator is not allowed access to the - // given target -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_NOT_FOUND 0x03 // The requested ITN does not exist at this - // address -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_TARGET_REMOVED 0x04 // The requested ITN has been removed, and - // no forwarding address is provided -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_WRONG_VERSION 0x05 // The requested iSCSI version range is not - // supported by the target -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_TOO_MANY_CONNECTIONS 0x06 // Too many connections on this SSID -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISSING_PARAMETER 0x07 // Missing parameters (e.g. iSCSI Initiator - // Name and/or Target Name) -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_NO_SESSION_SPANNING 0x08 // arget does not support session spanning - // to this connection (address) -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_SESSION_NO_SUPPORT 0x09 // Target does not support this type of - // session or not from this initiator -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_SESSION_NO_EXIST 0x0A // Attempt to add a connection to a non- - // existent session -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_INVALID_LOGIN_REQ_TYPE 0x0B // Invalid request type during login - -#define ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SERVER_ERR 0x03 // Target Error - indicates that the target sees no errors in the - // initiator's Login Request but is currently incapable of - // fulfilling the request. The initiator may retry the same Login - // Request later -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_TARGET_ERROR 0x00 // Target hardware or software error -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_SERVICE_UNAVAILABLE 0x01 // The iSCSI service or target is not - // currently operational -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_OUT_OF_RESOURCES 0x02 // The target has insufficient session, - // connection, or other resources - -/* The Login Response indicates the progress and/or end of the Login - Phase. -*/ -typedef struct __attribute__((packed)) iscsi_login_response_packet { - uint8_t opcode; // Always 0x23 according to specification (see above) - int8_t flags; // Login response flags (see above) - uint8_t version_max; // This is the highest version number supported by the target. - // All Login Responses within the Login Phase MUST carry the same - // Version-max - uint8_t version_active; // Version-active indicates the highest version supported by the target - // and initiator. If the target does not support a version within the - // range specified by the initiator, the target rejects the login and - // this field indicates the lowest version supported by the target. - // All Login Responses within the Login Phase MUST carry the same - // Version-active. - // The initiator MUST use the value presented as a response to the first - // Login Request - uint8_t total_ahs_len; // TotalAHSLength - uint8_t ds_len[3]; // DataSegmentLength - struct iscsi_isid isid; // ISID (see above for declaration) - uint16_t tsih; // The TSIH is the target-assigned session-identifying handle. Its - // internal format and content are not defined by this protocol, except - // for the value 0, which is reserved. With the exception of the Login - // Final-Response in a new session, this field should be set to the TSIH - // provided by the initiator in the Login Request. For a new session, - // the target MUST generate a non-zero TSIH and ONLY return it in the - // Login Final-Response - uint32_t init_task_tag; // Initiator task tag - uint32_t reserved; // Reserved for future usage - uint32_t stat_sn; // For the first Login Response (the response to the first Login - // Request), this is the starting status sequence number for the - // connection. The next response of any kind - including the next - // Login Response, if any, in the same Login Phase - will carry this - // number + 1. This field is only valid if the Status-Class is 0 - uint32_t exp_cmd_sn; // ExpCmdSN - uint32_t max_cmd_sn; // MaxCmdSN - uint8_t status_class; // Status-class (see above for details). If the Status-Class is - // not 0, the initiator and target MUST close the TCP connection - // If the target wishes to reject the Login Request for more than one - // reason, it should return the primary reason for the rejection - uint8_t status_detail; // Status-detail (see above for details) - uint16_t reserved2; // Reserved for future usage - uint64_t reserved3; // Reserved for future usage - struct iscsi_ds_cmd_data ds_cmd_data; // Data segment - Login Parameters in Text Request Format - // The target MUST provide some basic parameters in order to enable the - // initiator to determine if it is connected to the correct port and the - // initial text parameters for the security exchange. - // All the rules specified for Text Responses also hold for Login - // Responses -} iscsi_login_response_packet; +/// SCSI command opcode (embedded in iSCSI protocol): SYNCHRONIZE CACHE(16). +#define ISCSI_SCSI_OPCODE_SYNCHRONIZECACHE16 0x91 -#define ISCSI_LOGOUT_REQ_REASON_CODE_CLOSE_SESSION 0x00 // Close the session. All commands associated with the - // session (if any) are terminated -#define ISCSI_LOGOUT_REQ_REASON_CODE_CLOSE_CONNECTION 0x01 // Close the connection. All commands associated with the - // connection (if any) are terminated -#define ISCSI_LOGOUT_REQ_REASON_CODE_REMOVE_CONNECTION_RECOVERY 0x02 // Remove the connection for recovery. The connection is - // closed, and all commands associated with it, if any, are - // to be prepared for a new allegiance - -/* The entire logout discussion in this section is also applicable for - an implicit Logout realized by way of a connection reinstatement or - session reinstatement. When a Login Request performs an implicit - Logout, the implicit Logout is performed as if having the reason - codes specified below: -*/ -#define ISCSI_LOGOUT_REQ_REASON_CODE_IMPLICIT_SESSION_REINSTATEMENT 0x00 // Session reinstatement -#define ISCSI_LOGOUT_REQ_REASON_CODE_IMPLICIT_CONNECTION_REINSTATEMENT 0x01 // connection reinstatement when the operational - // ErrorRecoveryLevel < 2 -#define ISCSI_LOGOUT_REQ_REASON_CODE_IMPLICIT_CONNECTION_REINSTATEMENT_2 0x02 // connection reinstatement when the operational - // ErrorRecoveryLevel = 2 - -/* The Logout Request is used to perform a controlled closing of a - connection. - - An initiator MAY use a Logout Request to remove a connection from a - session or to close an entire session. - - After sending the Logout Request PDU, an initiator MUST NOT send any - new iSCSI requests on the closing connection. If the Logout Request - is intended to close the session, new iSCSI requests MUST NOT be sent - on any of the connections participating in the session. - - When receiving a Logout Request with the reason code "close the - connection" or "close the session", the target MUST terminate all - pending commands, whether acknowledged via the ExpCmdSN or not, on - that connection or session, respectively. - - When receiving a Logout Request with the reason code "remove the - connection for recovery", the target MUST discard all requests not - yet acknowledged via the ExpCmdSN that were issued on the specified - connection and suspend all data/status/R2T transfers on behalf of - pending commands on the specified connection. - - The target then issues the Logout Response and half-closes the TCP - connection (sends FIN). After receiving the Logout Response and - attempting to receive the FIN (if still possible), the initiator MUST - completely close the logging-out connection. For the terminated - commands, no additional responses should be expected. - - A Logout for a CID may be performed on a different transport - connection when the TCP connection for the CID has already been - terminated. In such a case, only a logical "closing" of the iSCSI - connection for the CID is implied with a Logout. - - All commands that were not terminated or not completed (with status) - and acknowledged when the connection is closed completely can be - reassigned to a new connection if the target supports connection - recovery. - - If an initiator intends to start recovery for a failing connection, - it MUST use the Logout Request to "clean up" the target end of a - failing connection and enable recovery to start, or use the Login - Request with a non-zero TSIH and the same CID on a new connection for - the same effect. In sessions with a single connection, the - connection can be closed and then a new connection reopened. A - connection reinstatement login can be used for recovery. - - A successful completion of a Logout Request with the reason code - "close the connection" or "remove the connection for recovery" - results at the target in the discarding of unacknowledged commands - received on the connection being logged out. These are commands that - have arrived on the connection being logged out but that have not - been delivered to SCSI because one or more commands with a smaller - CmdSN have not been received by iSCSI. The resulting holes in the - command sequence numbers will have to be handled by appropriate - recovery, unless the session is also closed. -*/ -typedef struct __attribute__((packed)) iscsi_logout_req_packet { - uint8_t opcode; // Always 0x06 according to specification (see above) - int8_t reason_code; // Reason Code - // A target implicitly terminates the active tasks due to the iSCSI - // protocol in the following cases: - // a) When a connection is implicitly or explicitly logged out with - // the reason code "close the connection" and there are active - // tasks allegiant to that connection. - // b) When a connection fails and eventually the connection state - // times out and there are active tasks allegiant to that - // connection - // c) When a successful recovery Logout is performed while there are - // active tasks allegiant to that connection and those tasks - // eventually time out after the Time2Wait and Time2Retain periods - // without allegiance reassignment - // d) When a connection is implicitly or explicitly logged out with - // the reason code "close the session" and there are active tasks - // in that session - // If the tasks terminated in any of the above cases are SCSI tasks, - // they must be internally terminated as if with CHECK CONDITION status. - // This status is only meaningful for appropriately handling the - // internal SCSI state and SCSI side effects with respect to ordering, - // because this status is never communicated back as a terminating - // status to the initiator. However, additional actions may have to be - // taken at the SCSI level, depending on the SCSI context as defined by - // the SCSI standards (e.g., queued commands and ACA; UA for the next - // command on the I_T nexus in cases a), b), and c) above). After the - // tasks are terminated, the target MUST report a Unit Attention condition - // on the next command processed on any connection for each affected - // I_T_L nexus with the status of CHECK CONDITION, the ASC/ASCQ value - // of 0x47 / 0x7F ("SOME COMMANDS CLEARED BY ISCSI PROTOCOL EVENT"), etc. - uint16_t reserved; // Reserved for future usage - uint8_t total_ahs_len; // TotalAHSLength (MUST be 0 for this PDU) - uint8_t ds_len[3]; // DataSegmentLength (MUST be 0 for this PDU) - uint64_t reserved2; // Reserved for future usage - uint32_t init_task_tag; // Initiator task tag - uint16_t cid; // This is the connection ID of the connection to be closed (including - // closing the TCP stream). This field is only valid if the reason code - // is not "close the session" - uint16_t reserved3; // Reserved for future usage - uint32_t cmd_sn; // CmdSN - uint32_t exp_stat_sn; // This is the last ExpStatSN value for the connection to be closed - uint64_t reserved4[2]; // Reserved for future usage - struct iscsi_header_digest hdr_digest; // Optional header digest -} iscsi_logout_req_packet; +/// SCSI command opcode (embedded in iSCSI protocol): WRITE SAME(16). +#define ISCSI_SCSI_OPCODE_WRITE_SAME16 0x93 -#define ISCSI_LOGOUT_RESPONSE_CLOSED_SUCCESSFULLY 0x00 // Connection or session closed successfully -#define ISCSI_LOGOUT_RESPONSE_CID_NOT_FOUND 0x01 // CID not found -#define ISCSI_LOGOUT_RESPONSE_CONNECTION_RECOVERY_NOT_SUPPORTED 0x02 // Connection recovery is not supported (i.e., the Logout reason - // code was "remove the connection for recovery" and the target - // does not support it as indicated by the operational - // ErrorRecoveryLevel) -#define ISCSI_LOGOUT_RESPONSE_CLEANUP_FAILED 0x03 // Cleanup failed for various reasons +/// SCSI command opcode (embedded in iSCSI protocol): WRITE ATOMIC(16). +#define ISCSI_SCSI_OPCODE_WRITE_ATOMIC16 0x9C -/* The Logout Response is used by the target to indicate if the cleanup - operation for the connection(s) has completed. +/// SCSI command opcode (embedded in iSCSI protocol): SERVICE ACTION IN(16). +#define ISCSI_SCSI_OPCODE_SERVICE_ACTION_IN 0x9E - After Logout, the TCP connection referred by the CID MUST be closed - at both ends (or all connections must be closed if the logout reason - was session close). -*/ -typedef struct __attribute__((packed)) iscsi_logout_response_packet { - uint8_t opcode; // Always 0x26 according to specification (see above) - uint8_t flags; // Reserved for future usage - uint8_t response; // Response - uint8_t reserved; // Reserved for future usage - uint8_t total_ahs_len; // TotalAHSLength (MUST be 0 for this PDU) - uint8_t ds_len[3]; // DataSegmentLength (MUST be 0 for this PDU) - uint64_t reserved2; // Reserved for future usage - uint32_t init_task_tag; // Initiator task tag - uint32_t reserved3; // Reserved for future usage - uint32_t stat_sn; // StatSN - uint32_t exp_cmd_sn; // ExpCmdSN - uint32_t max_cmd_sn; // MaxCmdSN - uint32_t reserved4; // Reserved for future usage - uint16_t time_wait; // Time2Wait - // If the Logout response code is 0 and the operational - // ErrorRecoveryLevel is 2, this is the minimum amount of time, in - // seconds, to wait before attempting task reassignment. If the Logout - // response code is 0 and the operational ErrorRecoveryLevel is less - // than 2, this field is to be ignored. - // This field is invalid if the Logout response code is 1. - // If the Logout response code is 2 or 3, this field specifies the - // minimum time to wait before attempting a new implicit or explicit - // logout. - // If Time2Wait is 0, the reassignment or a new Logout may be attempted - // immediately - uint16_t time_retain; // Time2Retain - // If the Logout response code is 0 and the operational - // ErrorRecoveryLevel is 2, this is the maximum amount of time, in - // seconds, after the initial wait (Time2Wait) that the target waits for - // the allegiance reassignment for any active task, after which the task - // state is discarded. If the Logout response code is 0 and the - // operational ErrorRecoveryLevel is less than 2, this field is to be - // ignored. - // This field is invalid if the Logout response code is 1. - // If the Logout response code is 2 or 3, this field specifies the - // maximum amount of time, in seconds, after the initial wait - // (Time2Wait) that the target waits for a new implicit or explicit - // logout. - // If it is the last connection of a session, the whole session state is - // discarded after Time2Retain. - // If Time2Retain is 0, the target has already discarded the connection - // (and possibly the session) state along with the task states. No - // reassignment or Logout is required in this case - uint32_t reserved5; // Reserved for future usage - struct iscsi_header_digest hdr_digest; // Optional header digest -} iscsi_logout_response_packet; +/// SCSI command opcode (embedded in iSCSI protocol): REPORT LUNS. +#define ISCSI_SCSI_OPCODE_REPORTLUNS 0xA0 -#define ISCSI_SNACK_REQ_TYPE_DATA_R2T_SNACK 0x00 // Data/R2T SNACK: requesting retransmission of one or more - // Data-In or R2T PDUs -#define ISCSI_SNACK_REQ_TYPE_STATUS_SNACK 0x01 // Status SNACK: requesting retransmission of one or more - // numbered responses -#define ISCSI_SNACK_REQ_TYPE_DATA_ACK 0x02 // DataACK: positively acknowledges Data-In PDUs. - // If an initiator operates at ErrorRecoveryLevel 1 or higher, it MUST - // issue a SNACK of type DataACK after receiving a Data-In PDU with the - // A bit set to 1. However, if the initiator has detected holes in the - // input sequence, it MUST postpone issuing the SNACK of type DataACK - // until the holes are filled. An initiator MAY ignore the A bit if it - // deems that the bit is being set aggressively by the target (i.e., - // before the MaxBurstLength limit is reached). - // The DataACK is used to free resources at the target and not to - // request or imply data retransmission. - // An initiator MUST NOT request retransmission for any data it had - // already acknowledged -#define ISCSI_SNACK_REQ_TYPE_R_DATA_SNACK 0x03 // R-Data SNACK: requesting retransmission of Data-In PDUs with - // possible resegmentation and status tagging. - // If the initiator MaxRecvDataSegmentLength changed between the - // original transmission and the time the initiator requests - // retransmission, the initiator MUST issue a R-Data SNACK. - // With R-Data SNACK, the initiator indicates that it discards all the - // unacknowledged data and expects the target to resend it. It also - // expects resegmentation. In this case, the retransmitted Data-In PDUs - // MAY be different from the ones originally sent in order to reflect - // changes in MaxRecvDataSegmentLength. Their DataSN starts with the - // BegRun of the last DataACK received by the target if any was received; - // otherwise, it starts with 0 and is increased by 1 for each resent - // Data-In PDU. - // A target that has received a R-Data SNACK MUST return a SCSI Response - // that contains a copy of the SNACK Tag field from the R-Data SNACK in - // the SCSI Response SNACK Tag field as its last or only Response. For - // example, if it has already sent a response containing another value - // in the SNACK Tag field or had the status included in the last Data-In - // PDU, it must send a new SCSI Response PDU. If a target sends more - // than one SCSI Response PDU due to this rule, all SCSI Response PDUs - // must carry the same StatSN. If an initiator attempts to recover a lost - // SCSI Response when more than one response has been sent, the - // target will send the SCSI Response with the latest content known to - // the target, including the last SNACK Tag for the command. - // For considerations in allegiance reassignment of a task to a - // connection with a different MaxRecvDataSegmentLength. - -/* If the implementation supports ErrorRecoveryLevel greater than zero, - it MUST support all SNACK types. - - The SNACK is used by the initiator to request the retransmission of - numbered responses, data, or R2T PDUs from the target. The SNACK - Request indicates the numbered responses or data "runs" whose - retransmission is requested, where the run starts with the first - StatSN, DataSN, or R2TSN whose retransmission is requested and - indicates the number of Status, Data, or R2T PDUs requested, - including the first. 0 has special meaning when used as a starting - number and length: - - - When used in RunLength, it means all PDUs starting with the - initial. - - - When used in both BegRun and RunLength, it means all - unacknowledged PDUs. - - The numbered response(s) or R2T(s) requested by a SNACK MUST be - delivered as exact replicas of the ones that the target transmitted - originally, except for the fields ExpCmdSN, MaxCmdSN, and ExpDataSN, - which MUST carry the current values. R2T(s)requested by SNACK MUST - also carry the current value of the StatSN. - - The numbered Data-In PDUs requested by a Data SNACK MUST be delivered - as exact replicas of the ones that the target transmitted originally, - except for the fields ExpCmdSN and MaxCmdSN, which MUST carry the - current values; and except for resegmentation. - - Any SNACK that requests a numbered response, data, or R2T that was - not sent by the target or was already acknowledged by the initiator - MUST be rejected with a reason code of "Protocol Error". -*/ -typedef struct __attribute__((packed)) iscsi_snack_req_packet { - uint8_t opcode; // Always 0x10 according to specification (see above) - int8_t type; // Type - // Data/R2T SNACK, Status SNACK, or R-Data SNACK for a command MUST - // precede status acknowledgment for the given command - uint16_t reserved; // Reserved for future usage - uint8_t total_ahs_len; // TotalAHSLength - uint8_t ds_len[3]; // DataSegmentLength - uint64_t lun; // LUN or Reserved - uint32_t init_task_tag; // For a Status SNACK and DataACK, the Initiator Task Tag MUST be set to - // the reserved value 0xFFFFFFFF. In all other cases, the Initiator - // Task Tag field MUST be set to the Initiator Task Tag of the - // referenced command - uint32_t target_xfer_snack_tag; // For a R-Data SNACK, this field MUST contain a value that is different - // from 0 or 0xFFFFFFFF and is unique for the task (identified by the - // Initiator Task Tag). This value MUST be copied by the iSCSI target - // in the last or only SCSI Response PDU it issues for the command. - // For DataACK, the Target Transfer Tag MUST contain a copy of the - // Target Transfer Tag and LUN provided with the SCSI Data-In PDU with - // the A bit set to 1. - // In all other cases, the Target Transfer Tag field MUST be set to the - // reserved value 0xFFFFFFFF - uint32_t reserved2; // Reserved for future usage - uint32_t exp_stat_sn; // ExpStatSN - uint32_t reserved3; // Reserved for future usage - uint32_t beg_run; // BegRun - // This field indicates the DataSN, R2TSN, or StatSN of the first PDU - // whose retransmission is requested (Data/R2T and Status SNACK), or the - // next expected DataSN (DataACK SNACK). - // A BegRun of 0, when used in conjunction with a RunLength of 0, means - // "resend all unacknowledged Data-In, R2T or Response PDUs". - // BegRun MUST be 0 for a R-Data SNACK - uint32_t run_len; // RunLength - // This field indicates the number of PDUs whose retransmission is - // requested. - // A RunLength of 0 signals that all Data-In, R2T, or Response PDUs - // carrying the numbers equal to or greater than BegRun have to be - // resent. - // The RunLength MUST also be 0 for a DataACK SNACK in addition to a - // R-Data SNACK - struct iscsi_header_digest hdr_digest; // Optional header digest -} iscsi_snack_req_packet; +/// SCSI command opcode (embedded in iSCSI protocol): MAINTENANCE IN. +#define ISCSI_SCSI_OPCODE_MAINTENANCE_IN 0xA3 -#define ISCSI_REJECT_REASON_RESERVED 0x01 // Reserved, original PDU can't be resent -#define ISCSI_REJECT_REASON_DATA_DIGEST_ERR 0x02 // Data (payload) digest error, original - // PDU can be resent. - // For iSCSI, Data-Out PDU retransmission is only done if the - // target requests retransmission with a recovery R2T. However, - // if this is the data digest error on immediate data, the - // initiator may choose to retransmit the whole PDU, including - // the immediate data -#define ISCSI_REJECT_REASON_SNACK_REJECT 0x03 // SNACK Reject (original PDU can be resent) -#define ISCSI_REJECT_REASON_PROTOCOL_ERR 0x04 // Protocol Error (e.g., SNACK Request for a status that was - // already acknowledged). Original PDU can't be resent' -#define ISCSI_REJECT_REASON_COMMAND_NOT_SUPPORTED 0x05 // Command not supported (original PDU can't be resent) -#define ISCSI_REJECT_REASON_TOO_MANY_IMMEDIATE_COMMANDS 0x06 // Immediate command reject - too many immediate - // commands (original PDU can be resent) -#define ISCSI_REJECT_REASON_TASK_IN_PROGRESS 0x07 // Task in progress (original PDU can't be resent) -#define ISCSI_REJECT_REASON_INVALID_DATA_ACK 0x08 // Invalid data ack (original PDU can't be resent) -#define ISCSI_REJECT_REASON_INVALID_PDU_FIELD 0x09 // Invalid PDU field, original PDU can't be resent. - // A target should use this reason code for all invalid values - // of PDU fields that are meant to describe a task, a response, - // or a data transfer. Some examples are invalid TTT/ITT, - // buffer offset, LUN qualifying a TTT, and an invalid sequence - // number in a SNACK -#define ISCSI_REJECT_REASON_OUT_OF_RESOURCES 0x0A // Long op reject - Can't generate Target Transfer Tag - out of - // resources. Original PDU can be resent later -#define ISCSI_REJECT_REASON_DEPRECATED 0x0B // Deprecated; MUST NOT be used. Reason code 0x0B is deprecated - // and MUST NOT be used by implementations. An implementation - // receiving reason code 0x0B MUST treat it as a negotiation - // failure that terminates the Login Phase and the TCP connection -#define ISCSI_REJECT_REASON_WAITING_FOR_LOGOUT 0x0C // Waiting for Logout, original PDU can't be resent +/// SCSI command opcode (embedded in iSCSI protocol): READ(12). +#define ISCSI_SCSI_OPCODE_READ12 0xA8 -typedef struct __attribute__((packed)) iscsi_reject_packet { - uint8_t opcode; // Always 0x3F according to specification (see above) - uint8_t flags; // Reserved for future usage - uint8_t reason; // Reject reason (see above for definitions). - // In all the cases in which a pre-instantiated SCSI task is terminated - // because of the reject, the target MUST issue a proper SCSI command - // response with CHECK CONDITION. In these cases in which a status for - // the SCSI task was already sent before the reject, no additional - // status is required. If the error is detected while data from the - // initiator is still expected (i.e., the command PDU did not contain - // all the data and the target has not received a Data-Out PDU with the - // Final bit set to 1 for the unsolicited data, if any, and all - // outstanding R2Ts, if any), the target MUST wait until it receives - // the last expected Data-Out PDUs with the F bit set to 1 before - // sending the Response PDU - uint8_t reserved; // Reserved for future usage - uint8_t total_ahs_len; // TotalAHSLength - uint8_t ds_len[3]; // DataSegmentLength - uint64_t reserved2; // Reserved for future usage - uint32_t tag; // Always 0xFFFFFFFF for now - uint32_t reserved3; // Reserved for future usage - uint32_t stat_sn; // StatSN. This field carries its usual value and is not related to the - // rejected command. The StatSN is advanced after a Reject - uint32_t exp_cmd_sn; // ExpCmdSN. This field carries its usual value and is not related to the - // rejected command - uint32_t max_cmd_sn; // MaxCmdSN. This field carries its usual value and is not related to the - // rejected command - uint32_t data_r2tsn_sn; // DataSN/R2TSN or Reserved. - // This field is only valid if the rejected PDU is a Data/R2T SNACK and - // the Reject reason code is "Protocol Error". The DataSN/R2TSN is the - // next Data/R2T sequence number that the target would send for the - // task, if any - uint32_t reserved4[2]; // Reserved for future usage - struct iscsi_header_digest hdr_digest; // Optional header digest - struct iscsi_bhs_packet bad_pdu_hdr; // Complete Header of Bad PDU. The target returns the - // header (not including the digest) of the PDU in error - // as the data of the response - uint8_t vendor_data[0]; // Vendor-specific data (if any) - struct iscsi_data_digest data_digest; // Optional data digest -} iscsi_reject_packet; +/// SCSI command opcode (embedded in iSCSI protocol): WRITE(12). +#define ISCSI_SCSI_OPCODE_WRITE12 0xAA -/* NOP-Out may be used by an initiator as a "ping request" to verify - that a connection/session is still active and all its components are - operational. The NOP-In response is the "ping echo". +/// SCSI command opcode (embedded in iSCSI protocol): WRITE AND VERIFY(12). +#define ISCSI_SCSI_OPCODE_WRITE_VERIFY12 0xAE - A NOP-Out is also sent by an initiator in response to a NOP-In. +/// SCSI command opcode (embedded in iSCSI protocol): VERIFY(12). +#define ISCSI_SCSI_OPCODE_VERIFY12 0xAF - A NOP-Out may also be used to confirm a changed ExpStatSN if another - PDU will not be available for a long time. +/// SCSI command opcode (embedded in iSCSI protocol): READ DEFECT DATA(12). +#define ISCSI_SCSI_OPCODE_READ_DEFECT_DATA12 0xB7 - Upon receipt of a NOP-In with the Target Transfer Tag set to a valid - value (not the reserved value 0xffffffff), the initiator MUST respond - with a NOP-Out. In this case, the NOP-Out Target Transfer Tag MUST - contain a copy of the NOP-In Target Transfer Tag. The initiator - SHOULD NOT send a NOP-Out in response to any other received NOP-In, - in order to avoid lengthy sequences of NOP-In and NOP-Out PDUs sent - in response to each other. -*/ -typedef struct __attribute__((packed)) iscsi_nop_out_packet { - uint8_t opcode; // Always 0x00 according to specification (see above) - uint8_t reserved[3]; // Reserved for future usage - uint8_t total_ahs_len; // TotalAHSLength - uint8_t ds_len[3]; // DataSegmentLength - uint64_t lun; // LUN or Reserved - uint32_t init_task_tag; // The NOP-Out MUST have the Initiator Task Tag set to a valid value - // only if a response in the form of a NOP-In is requested (i.e., the - // NOP-Out is used as a ping request). Otherwise, the Initiator Task - // Tag MUST be set to 0xFFFFFFFF. - // When a target receives the NOP-Out with a valid Initiator Task Tag, - // it MUST respond with a NOP-In Response. - // If the Initiator Task Tag contains 0xFFFFFFFF, the I bit MUST be set - // to 1, and the CmdSN is not advanced after this PDU is sent - uint32_t target_xfer_tag; // The Target Transfer Tag is a target-assigned identifier for the - // operation. - // The NOP-Out MUST only have the Target Transfer Tag set if it is - // issued in response to a NOP-In with a valid Target Transfer Tag. In - // this case, it copies the Target Transfer Tag from the NOP-In PDU. - // Otherwise, the Target Transfer Tag MUST be set to 0xFFFFFFFF. - // When the Target Transfer Tag is set to a value other than 0xFFFFFFFF, - // the LUN field MUST also be copied from the NOP-In - uint32_t cmd_sn; // CmdSN - uint32_t exp_stat_sn; // ExpStatSN - uint64_t reserved2[2]; // Reserved for future usage - struct iscsi_header_digest hdr_digest; // Optional header digest - struct iscsi_ds_cmd_data ds_ping_data; // DataSegment - Ping Data (optional) - // Ping data is reflected in the NOP-In Response. The length of the - // reflected data is limited to MaxRecvDataSegmentLength. The length of - // ping data is indicated by the DataSegmentLength. 0 is a valid value - // for the DataSegmentLength and indicates the absence of ping data - struct iscsi_data_digest data_digest; // Optional data digest -} iscsi_nop_out_packet; +/** + * @brief iSCSI SCSI command flags: No unsolicited data. + * + * (F) is set to 1 when no unsolicited SCSI Data-Out PDUs + * follow this PDU. When F = 1 for a write and if Expected + * Data Transfer Length is larger than the + * DataSegmentLength, the target may solicit additional data + * through R2T. + */ +#define ISCSI_SCSI_CMD_FLAGS_TASK_NO_UNSOLICITED_DATA (1 << 7) -/* NOP-In is sent by a target as either a response to a NOP-Out, a - "ping" to an initiator, or a means to carry a changed ExpCmdSN and/or - MaxCmdSN if another PDU will not be available for a long time (as - determined by the target). - - When a target receives the NOP-Out with a valid Initiator Task Tag - (not the reserved value 0xFFFFFFFF), it MUST respond with a NOP-In - with the same Initiator Task Tag that was provided in the NOP-Out - request. It MUST also duplicate up to the first - MaxRecvDataSegmentLength bytes of the initiator-provided Ping Data. - For such a response, the Target Transfer Tag MUST be 0xFFFFFFFF. The - - target SHOULD NOT send a NOP-In in response to any other received - NOP-Out in order to avoid lengthy sequences of NOP-In and NOP-Out - PDUs sent in response to each other. - - Otherwise, when a target sends a NOP-In that is not a response to a - NOP-Out received from the initiator, the Initiator Task Tag MUST be - set to 0xFFFFFFFF, and the data segment MUST NOT contain any data - (DataSegmentLength MUST be 0). -*/ +/** + * @brief iSCSI SCSI command flags: Expected input data. + * + * (R) is set to 1 when the command is expected to input data. + */ +#define ISCSI_SCSI_CMD_FLAGS_TASK_READ (1 << 6) -typedef struct __attribute__((packed)) iscsi_nop_in_packet { - uint8_t opcode; // Always 0x20 according to specification (see above) - uint8_t reserved[3]; // Reserved for future usage - uint8_t total_ahs_len; // TotalAHSLength - uint8_t ds_len[3]; // DataSegmentLength - uint64_t lun; // A LUN MUST be set to a correct value when the Target Transfer Tag is - // valid (not the reserved value 0xFFFFFFFF) - uint32_t init_task_tag; // Initiator task tag or 0xFFFFFFFF - uint32_t target_xfer_tag; // If the target is responding to a NOP-Out, this field is set to the - // reserved value 0xFFFFFFFF. - // If the target is sending a NOP-In as a ping (intending to receive a - // corresponding NOP-Out), this field is set to a valid value (not the - // reserved value 0xFFFFFFFF). - // If the target is initiating a NOP-In without wanting to receive a - // corresponding NOP-Out, this field MUST hold the reserved value - // 0xFFFFFFFF - uint32_t stat_sn; // The StatSN field will always contain the next StatSN. However, when - // the Initiator Task Tag is set to 0xFFFFFFFF, the StatSN for the - // connection is not advanced after this PDU is sent +/** + * @brief iSCSI SCSI command flags: Expected output data. + * + * (W) is set to 1 when the command is expected to output data. + */ +#define ISCSI_SCSI_CMD_FLAGS_TASK_WRITE (1 << 5) + + +/// SCSI command flags task attribute: Untagged. +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_UNTAGGED 0x0 + +/// SCSI command flags task attribute: Simple. +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_SIMPLE 0x1 + +/// SCSI command flags task attribute: Ordered. +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_ORDERED 0x2 + +/// SCSI command flags task attribute: Head of queue. +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_HEAD_QUEUE 0x3 + +/// SCSI command flags task attribute: ACA. +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_ACA 0x4 + +/// SCSI command flags task attribute: Reserved. +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_RESERVED_1 0x5 + +/// SCSI command flags task attribute: Reserved. +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_RESERVED_2 0x6 + +/// SCSI command flags task attribute: Reserved. +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_RESERVED_3 0x7 + +/// SCSI command flags Task Attributes (ATTR) are encoded in the first three LSBs. +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_MASK 0x7 + + +/** + * @brief iSCSI Flag and Task Attributes for SCSI command packet data. + * + * Flags and Task Attributes: + * At least one of the W and F bits MUST be set to 1.\n + * Either or both of R and W MAY be 1 when the Expected Data Transfer + * Length and/or the Bidirectional Read Expected Data Transfer Length + * are 0, but they MUST NOT both be 0 when the Expected Data Transfer + * Length and/or Bidirectional Read Expected Data Transfer Length are + * not 0 (i.e., when some data transfer is expected, the transfer + * direction is indicated by the R and/or W bit). + */ +typedef struct __attribute__((packed)) iscsi_scsi_cmd_packet { + /// Always 1 according to the iSCSI specification. + uint8_t opcode; + + /// Flags and Task Attributes. + int8_t flags_task; + + /// Reserved for future usage, MUST always be 0. + uint16_t reserved; + + /// Total length of AHS. + uint8_t total_ahs_len; + + /// Length of DataSegment. + uint8_t ds_len[3]; + + /// SCSI LUN bit mask. + uint64_t lun; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /** + * @brief Expected Data Transfer Length. + * + * For unidirectional operations, the Expected Data Transfer Length + * field contains the number of bytes of data involved in this SCSI + * operation. For a unidirectional write operation (W flag set to 1 and + * R flag set to 0), the initiator uses this field to specify the number + * of bytes of data it expects to transfer for this operation. For a + * unidirectional read operation (W flag set to 0 and R flag set to 1), + * the initiator uses this field to specify the number of bytes of data + * it expects the target to transfer to the initiator. It corresponds + * to the SAM-2 byte count.\n + * For bidirectional operations (both R and W flags are set to 1), this + * field contains the number of data bytes involved in the write + * transfer. For bidirectional operations, an additional header segment + * MUST be present in the header sequence that indicates the + * Bidirectional Read Expected Data Transfer Length. The Expected Data + * Transfer Length field and the Bidirectional Read Expected Data + * Transfer Length field correspond to the SAM-2 byte count. + * If the Expected Data Transfer Length for a write and the length of + * the immediate data part that follows the command (if any) are the + * same, then no more data PDUs are expected to follow. In this case, + * the F bit MUST be set to 1.\n + * If the Expected Data Transfer Length is higher than the + * FirstBurstLength (the negotiated maximum amount of unsolicited data + * the target will accept), the initiator MUST send the maximum amount + * of unsolicited data OR ONLY the immediate data, if any. + * Upon completion of a data transfer, the target informs the initiator + * (through residual counts) of how many bytes were actually processed + * (sent and/or received) by the target. + */ + uint32_t exp_xfer_len; + + /// The CmdSN enables ordered delivery across multiple connections in a single session. + uint32_t cmd_sn; + + /// Command responses up to ExpStatSN - 1 (modulo 2**32) have been received (acknowledges status) on the connection. + uint32_t exp_stat_sn; + + /** + * @brief SCSI Command Descriptor Block (CDB). + * + * 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. + */ + iscsi_cdb scsi_cdb; + + /// Optional AHS packet data. + iscsi_ahs_packet ahs; + + /// Optional header digest. + iscsi_header_digest hdr_digest; + + /// Optional data segment, command data. + iscsi_ds_cmd_data ds_cmd_data; + + /// Optional data digest. + iscsi_data_digest data_digest; +} iscsi_scsi_cmd_packet; + + +/** + * @brief SCSI response flags: Residual Underflow. + * + * (U) set for Residual Underflow. In this case, the Residual + * Count indicates the number of bytes that were not + * transferred out of the number of bytes that were expected + * to be transferred. For a bidirectional operation, the + * Residual Count contains the residual for the write + * operation. + * + * Bits O and U and bits o and u are mutually exclusive (i.e., having + * both o and u or O and U set to 1 is a protocol error). + * + * For a response other than "Command Completed at Target", bits 3-6 + * MUST be 0. + */ +#define ISCSI_SCSI_RESPONSE_FLAGS_RES_UNDERFLOW (1 << 1) + +/** + * @brief SCSI response flags: Residual Overflow. + * + * (O) set for Residual Overflow. In this case, the Residual + * Count indicates the number of bytes that were not + * transferred because the initiator's Expected Data + * Transfer Length was not sufficient. For a bidirectional + * operation, the Residual Count contains the residual for + * the write operation. + * + * Bits O and U and bits o and u are mutually exclusive (i.e., having + * both o and u or O and U set to 1 is a protocol error). + * + * For a response other than "Command Completed at Target", bits 3-6 + * MUST be 0. + */ +#define ISCSI_SCSI_RESPONSE_FLAGS_RES_OVERFLOW (1 << 2) + +/** + * @brief SCSI response flags: Bidirectional Read Residual Underflow. + * + * (u) set for Bidirectional Read Residual Underflow. In this + * case, the Bidirectional Read Residual Count indicates the + * number of bytes that were not transferred to the + * initiator out of the number of bytes expected to be + * transferred. + * + * Bits O and U and bits o and u are mutually exclusive (i.e., having + * both o and u or O and U set to 1 is a protocol error). + * + * For a response other than "Command Completed at Target", bits 3-6 + * MUST be 0. + */ +#define ISCSI_SCSI_RESPONSE_FLAGS_BIDI_READ_RES_UNDERFLOW (1 << 3) + +/** + * @brief SCSI response flags: Bidirectional Read Residual Overflow. + * + + (o) set for Bidirectional Read Residual Overflow. In this + * case, the Bidirectional Read Residual Count indicates the + * number of bytes that were not transferred to the + * initiator because the initiator's Bidirectional Read + * Expected Data Transfer Length was not sufficient. + * + * Bits O and U and bits o and u are mutually exclusive (i.e., having + * both o and u or O and U set to 1 is a protocol error). + * + * For a response other than "Command Completed at Target", bits 3-6 + * MUST be 0. + */ +#define ISCSI_SCSI_RESPONSE_FLAGS_BIDI_READ_RES_OVERFLOW (1 << 4) + +/** + * @brief SCSI status response code: Good. + * + * The Status field is used to report the SCSI status of the command (as + * specified in SAM2) and is only valid if the response code is + * Command Completed at Target. + * + * If a SCSI device error is detected while data from the initiator is + * still expected (the command PDU did not contain all the data and the + * target has not received a data PDU with the Final bit set), the + * target MUST wait until it receives a data PDU with the F bit set in + * the last expected sequence before sending the Response PDU. + */ +#define ISCSI_SCSI_RESPONSE_STATUS_GOOD 0x00 + +/** + * @brief SCSI status response code: Check condition. + * + * The Status field is used to report the SCSI status of the command (as + * specified in SAM2) and is only valid if the response code is + * Command Completed at Target. + * + * If a SCSI device error is detected while data from the initiator is + * still expected (the command PDU did not contain all the data and the + * target has not received a data PDU with the Final bit set), the + * target MUST wait until it receives a data PDU with the F bit set in + * the last expected sequence before sending the Response PDU. + */ +#define ISCSI_SCSI_RESPONSE_STATUS_CHECK_COND 0x02 + +/** + * @brief SCSI status response code: Busy. + * + * The Status field is used to report the SCSI status of the command (as + * specified in SAM2) and is only valid if the response code is + * Command Completed at Target. + * + * If a SCSI device error is detected while data from the initiator is + * still expected (the command PDU did not contain all the data and the + * target has not received a data PDU with the Final bit set), the + * target MUST wait until it receives a data PDU with the F bit set in + * the last expected sequence before sending the Response PDU. + */ +#define ISCSI_SCSI_RESPONSE_STATUS_BUSY 0x08 + +/** + * @brief SCSI status response code: Residual conflict. + * + * The Status field is used to report the SCSI status of the command (as + * specified in SAM2) and is only valid if the response code is + * Command Completed at Target. + * + * If a SCSI device error is detected while data from the initiator is + * still expected (the command PDU did not contain all the data and the + * target has not received a data PDU with the Final bit set), the + * target MUST wait until it receives a data PDU with the F bit set in + * the last expected sequence before sending the Response PDU. + */ +#define ISCSI_SCSI_RESPONSE_STATUS_RES_CONFLICT 0x18 + +/** + * @brief SCSI status response code: Task set full. + * + * The Status field is used to report the SCSI status of the command (as + * specified in SAM2) and is only valid if the response code is + * Command Completed at Target. + * + * If a SCSI device error is detected while data from the initiator is + * still expected (the command PDU did not contain all the data and the + * target has not received a data PDU with the Final bit set), the + * target MUST wait until it receives a data PDU with the F bit set in + * the last expected sequence before sending the Response PDU. + */ +#define ISCSI_SCSI_RESPONSE_STATUS_TASK_SET_FULL 0x28 + +/** + * @brief SCSI status response code: ACA active. + * + * The Status field is used to report the SCSI status of the command (as + * specified in SAM2) and is only valid if the response code is + * Command Completed at Target. + * + * If a SCSI device error is detected while data from the initiator is + * still expected (the command PDU did not contain all the data and the + * target has not received a data PDU with the Final bit set), the + * target MUST wait until it receives a data PDU with the F bit set in + * the last expected sequence before sending the Response PDU. + */ +#define ISCSI_SCSI_RESPONSE_STATUS_ACA_ACTIVE 0x30 + +/** + * @brief SCSI status response code: Task aborted. + * + * The Status field is used to report the SCSI status of the command (as + * specified in SAM2) and is only valid if the response code is + * Command Completed at Target. + * + * If a SCSI device error is detected while data from the initiator is + * still expected (the command PDU did not contain all the data and the + * target has not received a data PDU with the Final bit set), the + * target MUST wait until it receives a data PDU with the F bit set in + * the last expected sequence before sending the Response PDU. + */ +#define ISCSI_SCSI_RESPONSE_STATUS_TASK_ABORTED 0x40 + +/// SCSI response code: Command Completed at Target. +#define ISCSI_SCSI_RESPONSE_CODE_OK 0x00 + +/// SCSI response code: Target Failure. +#define ISCSI_SCSI_RESPONSE_CODE_FAIL 0x01 + +/// SCSI response code: First vendor specific response code. +#define ISCSI_SCSI_RESPONSE_CODE_VENDOR_FIRST 0x80 + +/// SCSI response code: Last vendor specific response code. +#define ISCSI_SCSI_RESPONSE_CODE_VENDOR_LAST 0xFF + +/** + * @brief iSCSI SCSI command response packet data. + * + * The Response field is used to report a service response. The mapping + * of the response code into a SCSI service response code value, if + * needed, is outside the scope of this document. However, in symbolic + * terms, response value 0x00 maps to the SCSI service response (see + */ +typedef struct __attribute__((packed)) iscsi_scsi_response_packet { + /// Always 0x21 according to specification. + uint8_t opcode; + + /// Flags. + int8_t flags; + + /// This field contains the iSCSI service response. + uint8_t response; + + /// The Status field is used to report the SCSI status of the command (as specified in SAM2) and is only valid if the response code is Command Completed at Target. + uint8_t status; + + /// Total AHS length. + uint8_t total_ahs_len; + + /// Data segment length. + uint8_t ds_len[3]; + + /// Reserved for future usage. Always MUST be 0. + uint64_t reserved; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /** + * @brief Copy of the last accepted Selective Negative / Sequence Number Acknowledgment (SNACK) tag. + * + * This field contains a copy of the SNACK Tag of the last SNACK Tag + * accepted by the target on the same connection and for the command for + * which the response is issued. Otherwise, it is reserved and should + * be set to 0.\n + * After issuing a R-Data SNACK, the initiator must discard any SCSI + * status unless contained in a SCSI Response PDU carrying the same + * SNACK Tag as the last issued R-Data SNACK for the SCSI command on the + * current connection. + */ + uint32_t snack_tag; + + /** + * @brief StatSN - Status Sequence Number. + * + * The StatSN is a sequence number that the target iSCSI layer generates + * per connection and that in turn enables the initiator to acknowledge + * status reception. The StatSN is incremented by 1 for every + * response/status sent on a connection, except for responses sent as a + * result of a retry or SNACK. In the case of responses sent due to a + * retransmission request, the StatSN MUST be the same as the first time + * the PDU was sent, unless the connection has since been restarted. + */ + uint32_t stat_sn; + + /** + * @brief ExpCmdSN - Next Expected CmdSN from This Initiator. + * + * The ExpCmdSN is a sequence number that the target iSCSI returns to + * the initiator to acknowledge command reception. It is used to update + * a local variable with the same name. An ExpCmdSN equal to + * MaxCmdSN + 1 indicates that the target cannot accept new commands. + */ + uint32_t exp_cmd_sn; + + /** + * @brief MaxCmdSN - Maximum CmdSN from This Initiator. + * + * The MaxCmdSN is a sequence number that the target iSCSI returns to + * the initiator to indicate the maximum CmdSN the initiator can send. + * It is used to update a local variable with the same name. If the + * MaxCmdSN is equal to ExpCmdSN - 1, this indicates to the initiator + * that the target cannot receive any additional commands. When the + * MaxCmdSN changes at the target while the target has no pending PDUs + * to convey this information to the initiator, it MUST generate a + * NOP-In to carry the new MaxCmdSN. + */ + uint32_t max_cmd_sn; + + /** + * @brief ExpDataSN or Reserved. + * + * This field indicates the number of Data-In (read) PDUs the target has + * sent for the command.\n + * This field MUST be 0 if the response code is not Command Completed at + * Target or the target sent no Data-In PDUs for the command. + */ + uint32_t exp_data_sn; + + /** + * @brief Bidirectional Read Residual Count or Reserved. + * + * The Bidirectional Read Residual Count field MUST be valid in the case + * where either the u bit or the o bit is set. If neither bit is set, + * the Bidirectional Read Residual Count field is reserved. Targets may + * set the Bidirectional Read Residual Count, and initiators may use it + * when the response code is Command Completed at Target. If the o bit + * is set, the Bidirectional Read Residual Count indicates the number of + * bytes that were not transferred to the initiator because the + * initiator's Bidirectional Read Expected Data Transfer Length was not + * sufficient. If the u bit is set, the Bidirectional Read Residual + * Count indicates the number of bytes that were not transferred to the + * initiator out of the number of bytes expected to be transferred. + */ + uint32_t bidi_read_res_cnt; + + /** + * @brief Residual Count or Reserved. + * + * The Residual Count field MUST be valid in the case where either the U + * bit or the O bit is set. If neither bit is set, the Residual Count + * field MUST be ignored on reception and SHOULD be set to 0 when + * sending. Targets may set the residual count, and initiators may use + * it when the response code is Command Completed at Target (even if the + * status returned is not GOOD). If the O bit is set, the Residual + * Count indicates the number of bytes that were not transferred because + * the initiator's Expected Data Transfer Length was not sufficient. If + * the U bit is set, the Residual Count indicates the number of bytes + * that were not transferred out of the number of bytes expected to be + * transferred. + */ + uint32_t res_cnt; + + /// Optional header digest. + iscsi_header_digest hdr_digest; + + /// Optional data segment, command data. + iscsi_ds_cmd_data ds_cmd_data; + + /// Optional data digest. + iscsi_data_digest data_digest; +} iscsi_scsi_response_packet; + + +/// Task management request function: ABORT TASK: aborts the task identified by the Referenced Task Tag field. +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_ABORT_TASK 0x01 + +/// Task management request function: ABORT TASK SET: aborts all tasks issued via this session on the LU. +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_ABORT_TASK_SET 0x02 + +/// Task management request function: CLEAR ACA - clears the Auto Contingent Allegiance condition. +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_CLEAR_ACA 0x03 + +/// Task management request function: CLEAR TASK SET - aborts all tasks in the appropriate task set as defined by the TST field in the Control mode page (see SPC3). +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_CLEAR_TASK_SET 0x04 + +/// Task management request function: LOGICAL UNIT RESET. +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_LOGICAL_UNIT_RESET 0x05 + +/// Task management request function: TARGET WARM RESET. +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_TARGET_WARM_RESET 0x06 + +/// Task management request function: TARGET COLD RESET. +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_TARGET_COLD_RESET 0x07 + +/// Task management request function: TASK REASSIGN - reassigns connection allegiance for the task identified by the Initiator Task Tag field to this connection, thus resuming the iSCSI exchanges for the task. +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_TASK_REASSIGN 0x08 + + +/** + * @brief iSCSI Task Management Function Request packet data. + * + * This structure is used to explicity control the execution of one + * or more tasks (iSCSI and SCSI). + */ +typedef struct __attribute__((packed)) iscsi_task_mgmt_func_req_packet { + /// Always 2 according to iSCSI specification. + uint8_t opcode; + + /** + * @brief Function. + * + * The task management functions provide an initiator with a way to + * explicitly control the execution of one or more tasks (SCSI and iSCSI + * tasks). The task management function codes are listed below. For a + * more detailed description of SCSI task management, see SAM2. + */ + int8_t func; + + /// Reserved fot future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength (MUST be 0 for this PDU). + uint8_t total_ahs_len; + + /// DataSegmentLength (MUST be 0 for this PDU). + uint8_t ds_len[3]; + + /** + * @brief Logical Unit Number (LUN) or Reserved. + * + * This field is required for functions that address a specific LU + * (ABORT TASK, CLEAR TASK SET, ABORT TASK SET, CLEAR ACA, LOGICAL UNIT + * RESET) and is reserved in all others + */ + uint64_t lun; + + /** + * @brief Initiator Task Tag (ITT). + * + * This is the Initiator Task Tag of the task to be aborted for the + * ABORT TASK function or reassigned for the TASK REASSIGN function. + * For all the other functions, this field MUST be set to the reserved + * value 0xFFFFFFFF. + */ + uint32_t init_task_tag; + + /// Referenced task tag or 0xFFFFFFFF. + uint32_t ref_task_tag; + + /// CmdSN. + uint32_t cmd_sn; + + /// ExpStatSN + uint32_t exp_stat_sn; + + /** + * @brief RefCmdSN or Reserved. + * + * If an ABORT TASK is issued for a task created by an immediate + * command, then the RefCmdSN MUST be that of the task management + * request itself (i.e., the CmdSN and RefCmdSN are equal).\n + * For an ABORT TASK of a task created by a non-immediate command, the + * RefCmdSN MUST be set to the CmdSN of the task identified by the + * Referenced Task Tag field. Targets must use this field when the task + * identified by the Referenced Task Tag field is not with the target. + * Otherwise, this field is reserved. + */ + uint32_t ref_cmd_sn; + + /** + * @brief ExpDataSN or Reserved. + * + * For recovery purposes, the iSCSI target and initiator maintain a data + * acknowledgment reference number - the first input DataSN number + * unacknowledged by the initiator. When issuing a new command, this + * number is set to 0. If the function is TASK REASSIGN, which + * establishes a new connection allegiance for a previously issued read + * or bidirectional command, the ExpDataSN will contain an updated data + * acknowledgment reference number or the value 0; the latter indicates + * that the data acknowledgment reference number is unchanged. The + * initiator MUST discard any data PDUs from the previous execution that + * it did not acknowledge, and the target MUST transmit all Data-In PDUs + * (if any) starting with the data acknowledgment reference number. The + * number of retransmitted PDUs may or may not be the same as the + * original transmission, depending on if there was a change in + * MaxRecvDataSegmentLength in the reassignment. The target MAY also + * send no more Data-In PDUs if all data has been acknowledged. + * The value of ExpDataSN MUST be 0 or higher than the DataSN of the + * last acknowledged Data-In PDU, but not larger than DataSN + 1 of the + * last Data-IN PDU sent by the target. Any other value MUST be ignored + * by the target. + * For other functions, this field is reserved + */ + uint32_t exp_data_sn; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2; + + /// Optional header digest. + iscsi_header_digest hdr_digest; +} iscsi_task_mgmt_func_req_packet; + + +/// Task management function response: Function complete. +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_COMPLETE 0x00 + +/// Task management function response: Task does not exist. +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_NO_EXIST 0x01 + +/// Task management function response: LUN does not exist. +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_LUN_NO_EXIST 0x02 + +/// Task management function response: Task still allegiant. +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_ALLEGIANT 0x03 + +/// Task management function response: Task allegiance reassignment not supported. +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_UNSUPPORTED_ALLEGIANCE 0x04 + +/// Task management function response: Task management function not supported. +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_UNSUPPORTED_MGMT 0x05 + +/// Task management function response: Function authorization failed. +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_AUTH_FAILED 0x06 + +/// Task management function response: Function rejected. +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_REJECTED 0xFF + + +/** + * @brief iSCSI Task Management Function Response packet data. + * + * For the functions ABORT TASK, ABORT TASK SET, CLEAR ACA, CLEAR TASK + * SET, LOGICAL UNIT RESET, TARGET COLD RESET, TARGET WARM RESET, and + * TASK REASSIGN, the target performs the requested task management + * function and sends a task management response back to the initiator. + * For TASK REASSIGN, the new connection allegiance MUST ONLY become + * effective at the target after the target issues the task management + * response. + */ +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). + int8_t flags; + + /** + * @brief Function response. + * + * For the TARGET COLD RESET and TARGET WARM RESET functions, the target + * cancels all pending operations across all LUs known to the issuing + * initiator. For the TARGET COLD RESET function, the target MUST then + * close all of its TCP connections to all initiators (terminates all + * sessions).\n + * The mapping of the response code into a SCSI service response code + * value, if needed, is outside the scope of this document. However, in + * symbolic terms, Response values 0 and 1 map to the SCSI service + * response of FUNCTION COMPLETE. Response value 2 maps to the SCSI + * service response of INCORRECT LOGICAL UNIT NUMBER. All other + * Response values map to the SCSI service response of FUNCTION + * REJECTED. If a Task Management Function Response PDU does not arrive + * before the session is terminated, the SCSI service response is + * SERVICE DELIVERY OR TARGET FAILURE.\n + * The response to ABORT TASK SET and CLEAR TASK SET MUST only be issued + * by the target after all of the commands affected have been received + * by the target, the corresponding task management functions have been + * executed by the SCSI target, and the delivery of all responses + * delivered until the task management function completion has been + * confirmed (acknowledged through the ExpStatSN) by the initiator on + * all connections of this session.\n + * For the ABORT TASK function,\n + * -# if the Referenced Task Tag identifies a valid task leading to a + * successful termination, then targets must return the "Function + * complete" response. + * -# if the Referenced Task Tag does not identify an existing task + * but the CmdSN indicated by the RefCmdSN field in the Task + * Management Function Request is within the valid CmdSN window + * and less than the CmdSN of the Task Management Function Request + * itself, then targets must consider the CmdSN as received and + * return the "Function complete" response. + * -# if the Referenced Task Tag does not identify an existing task + * and the CmdSN indicated by the RefCmdSN field in the Task + * Management Function Request is outside the valid CmdSN window, + * then targets must return the "Task does not exist" response + */ + uint8_t response; + + /// Reserved for future usage, always MUST be 0. + uint8_t reserved; + + /// TotalAHSLength (MUST be 0 for this PDU). + uint8_t total_ahs_len; + + /// DataSegmentLength (MUST be 0 for this PDU). + uint8_t ds_len[3]; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved3; + + /// StatSN. + uint32_t stat_sn; + + /// ExpCmdSN. + uint32_t exp_cmd_sn; + + /// MaxCmdSN. + uint32_t max_cmd_sn; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved4; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved5; + + /// Optional header digest. + iscsi_header_digest hdr_digest; +} iscsi_task_mgmt_func_response_packet; + +/// SCSI data out / in flags: Immediately process transfer. +#define ISCSI_SCSI_DATA_OUT_DATA_IN_FLAGS_IMMEDIATE (1 << 7) + +/** + * @brief iSCSI SCSI Data Out request packet data. + * + * THis structure is used by iSCSI for SCSI data output + * requests, i.e. write operations. + */ +typedef struct __attribute__((packed)) iscsi_scsi_data_out_req_packet { + /// Always 2 according to iSCSI specification. + uint8_t opcode; + + /// Flags. + int8_t flags; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /** + * @brief DataSegmentLength. + * + * This is the data payload length of a SCSI Data-In or SCSI Data-Out + * PDU. The sending of 0-length data segments should be avoided, but + * initiators and targets MUST be able to properly receive 0-length data + * segments.\n + * The data segments of Data-In and Data-Out PDUs SHOULD be filled to + * the integer number of 4-byte words (real payload), unless the F bit + * is set to 1. + */ + uint8_t ds_len[3]; + + /** + * @brief Logical Unit Number (LUN) or Reserved. + * + * If the Target Transfer Tag is provided, then the LUN field MUST hold a + * valid value and be consistent with whatever was specified with the command; + * otherwise, the LUN field is reserved. + */ + uint64_t lun; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /** + * @brief Target Transfer Tag or 0xFFFFFFFF. + * + * On outgoing data, the Target Transfer Tag is provided to the target + * if the transfer is honoring an R2T. In this case, the Target + * Transfer Tag field is a replica of the Target Transfer Tag provided + * with the R2T.\n + * The Target Transfer Tag values are not specified by this protocol, + * except that the value 0xFFFFFFFF is reserved and means that the + * Target Transfer Tag is not supplied. + */ + uint32_t target_xfer_tag; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved2; + + /// ExpStatSN. + uint32_t exp_stat_sn; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved3; + + /** + * @brief DataSN. + * + * For output (write) data PDUs, the DataSN is the Data-Out PDU number + * within the current output sequence. Either the current output + * sequence is identified by the Initiator Task Tag (for unsolicited + * data) or it is a data sequence generated for one R2T (for data + * solicited through R2T). + */ + uint32_t data_sn; + + /** + * @brief Buffer Offset. + * + * The Buffer Offset field contains the offset of this PDU payload data + * within the complete data transfer. The sum of the buffer offset and + * length should not exceed the expected transfer length for the + * command.\n + * The order of data PDUs within a sequence is determined by + * DataPDUInOrder. When set to Yes, it means that PDUs have to be in + * increasing buffer offset order and overlays are forbidden.\n + * The ordering between sequences is determined by DataSequenceInOrder. + * When set to Yes, it means that sequences have to be in increasing + * buffer offset order and overlays are forbidden. + */ + uint32_t buf_offset; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved4; + + /// Optional header digest. + iscsi_header_digest hdr_digest; + + /// Data segment. + iscsi_ds_cmd_data ds_cmd_data; + + /// Optional data digest. + iscsi_data_digest data_digest; +} iscsi_scsi_data_out_req_packet; + +/** + * @brief SCSI Data In reponse flags: Status. + * + * (S) set to indicate that the Command Status field + * contains status. If this bit is set to 1, the + * F bit MUST also be set to 1. + */ +#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS (1 << 0) + +/** + * @brief SCSI Data In reponse flags: Residual Underflow. + * + * (U) set for Residual Underflow. In this case, the Residual + * Count indicates the number of bytes that were not + * transferred out of the number of bytes that were expected + * to be transferred. For a bidirectional operation, the + * Residual Count contains the residual for the write + * operation. + */ +#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_UNDERFLOW (1 << 1) + +/** + * @brief SCSI Data In reponse flags: Residual Overflow. + * + * (O) set for Residual Overflow. In this case, the Residual + * Count indicates the number of bytes that were not + * transferred because the initiator's Expected Data + * Transfer Length was not sufficient. For a bidirectional + * operation, the Residual Count contains the residual for + * the write operation. + */ +#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_OVERFLOW (1 << 2) + +/** + * @brief SCSI Data In reponse flags: ACK. + * + * (A) for sessions with ErrorRecoveryLevel=1 or higher, the target sets + * this bit to 1 to indicate that it requests a positive acknowledgment + * from the initiator for the data received. The target should use the + * A bit moderately; it MAY only set the A bit to 1 once every + * MaxBurstLength bytes, or on the last Data-In PDU that concludes the + * entire requested read data transfer for the task from the target's + * perspective, and it MUST NOT do so more frequently. The target MUST + * NOT set to 1 the A bit for sessions with ErrorRecoveryLevel=0. The + * initiator MUST ignore the A bit set to 1 for sessions with + * ErrorRecoveryLevel=0.\n + * On receiving a Data-In PDU with the A bit set to 1 on a session with + * ErrorRecoveryLevel greater than 0, if there are no holes in the read + * data until that Data-In PDU, the initiator MUST issue a SNACK of type + * DataACK, except when it is able to acknowledge the status for the + * task immediately via the ExpStatSN on other outbound PDUs if the + * status for the task is also received. In the latter case + * (acknowledgment through the ExpStatSN), sending a SNACK of type + * DataACK in response to the A bit is OPTIONAL, but if it is done, it + * must not be sent after the status acknowledgment through the + * ExpStatSN. If the initiator has detected holes in the read data + * prior to that Data-In PDU, it MUST postpone issuing the SNACK of type + * DataACK until the holes are filled. An initiator also MUST NOT + * acknowledge the status for the task before those holes are filled. A + * status acknowledgment for a task that generated the Data-In PDUs is + * considered by the target as an implicit acknowledgment of the Data-In + * PDUs if such an acknowledgment was requested by the target. + */ +#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_ACK (1 << 6) + +/** + * @brief SCSI Data In reponse flags: Final. + * + * (F) for outgoing data, this bit is 1 for the last PDU of unsolicited + * data or the last PDU of a sequence that answers an R2T. + * For incoming data, this bit is 1 for the last input (read) data PDU + * of a sequence. Input can be split into several sequences, each + * having its own F bit. Splitting the data stream into sequences does + * not affect DataSN counting on Data-In PDUs. It MAY be used as a + * "change direction" indication for bidirectional operations that need + * such a change.\n + * DataSegmentLength MUST NOT exceed MaxRecvDataSegmentLength for the + * direction it is sent, and the total of all the DataSegmentLength of + * all PDUs in a sequence MUST NOT exceed MaxBurstLength (or + * FirstBurstLength for unsolicited data). However, the number of + * individual PDUs in a sequence (or in total) may be higher than the + * ratio of MaxBurstLength (or FirstBurstLength) to + * MaxRecvDataSegmentLength (as PDUs may be limited in length by the + * capabilities of the sender). Using a DataSegmentLength of 0 may + * increase beyond what is reasonable for the number of PDUs and should + * therefore be avoided.\n + * For bidirectional operations, the F bit is 1 for both the end of the + * input sequences and the end of the output sequences + */ +#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_FINAL (1 << 7) + +/** + * @brief iSCSI SCSI Data In response packet data. + * + * THis structure is used by iSCSI for SCSI data input + * responses, i.e. read operations. + */ +typedef struct __attribute__((packed)) iscsi_scsi_data_in_response_packet { + /// Always 0x25 according to iSCSI specification. + uint8_t opcode; + + /// Incoming data flags. The fields StatSN, Status, and Residual Count only have meaningful content if the S bit is set to 1. + int8_t flags; + + /// Rserved for future usage, always MUST be 0. + uint8_t reserved; + + /** + * @brief Status or Reserved. + * + * Status can accompany the last Data-In PDU if the command did not end + * with an exception (i.e., the status is "good status" - GOOD, + * CONDITION MET, or INTERMEDIATE-CONDITION MET). The presence of + * status (and of a residual count) is signaled via the S flag bit. + * Although targets MAY choose to send even non-exception status in + * separate responses, initiators MUST support non-exception status in + * Data-In PDUs. + */ + uint8_t status; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /** + * @brief DataSegmentLength. + * + * This is the data payload length of a SCSI Data-In or SCSI Data-Out + * PDU. The sending of 0-length data segments should be avoided, but + * initiators and targets MUST be able to properly receive 0-length data + * segments.\n + * The data segments of Data-In and Data-Out PDUs SHOULD be filled to + * the integer number of 4-byte words (real payload), unless the F bit + * is set to 1. + */ + uint8_t ds_len[3]; + + /** + * @brief Logical Unit Number (LUN) or Reserved. + * + * If the Target Transfer Tag is provided, then the LUN field MUST hold a + * valid value and be consistent with whatever was specified with the command; + * otherwise, the LUN field is reserved. + */ + uint64_t lun; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /** + * @brief Target Transfer Tag or 0xFFFFFFFF. + * + * On incoming data, the Target Transfer Tag and LUN MUST be provided by + * the target if the A bit is set to 1; otherwise, they are reserved. + * The Target Transfer Tag and LUN are copied by the initiator into the + * SNACK of type DataACK that it issues as a result of receiving a SCSI + * Data-In PDU with the A bit set to 1.\n + * The Target Transfer Tag values are not specified by this protocol, + * except that the value 0xFFFFFFFF is reserved and means that the + * Target Transfer Tag is not supplied. + */ + uint32_t target_xfer_tag; + + /// StatSN. + uint32_t stat_sn; + + /// ExpCmdSN. + + uint32_t exp_cmd_sn; + + /// MaxCmdSN. + uint32_t max_cmd_sn; + + /** + * @brief DataSN. + * + * For input (read) or bidirectional Data-In PDUs, the DataSN is the + * input PDU number within the data transfer for the command identified + * by the Initiator Task Tag.\n + * R2T and Data-In PDUs, in the context of bidirectional commands, share + * the numbering sequence. + */ + uint32_t data_sn; + + /** + * @brief Buffer Offset. + * + * The Buffer Offset field contains the offset of this PDU payload data + * within the complete data transfer. The sum of the buffer offset and + * length should not exceed the expected transfer length for the + * command.\n + * The order of data PDUs within a sequence is determined by + * DataPDUInOrder. When set to Yes, it means that PDUs have to be in + * increasing buffer offset order and overlays are forbidden.\n + * The ordering between sequences is determined by DataSequenceInOrder. + * When set to Yes, it means that sequences have to be in increasing + * buffer offset order and overlays are forbidden. + */ + uint32_t buf_offset; + + /// Residual Count or Reserved. + uint32_t res_cnt; + + /// Optional header digest. + iscsi_header_digest hdr_digest; + + /// Data segment. + iscsi_ds_cmd_data ds_cmd_data; + + /// Optional data digest. + iscsi_data_digest data_digest; +} iscsi_scsi_data_in_response_packet; + +/** + * @brief iSCSI Ready To Transfer packet data. + * + * When an initiator has submitted a SCSI command with data that passes + * from the initiator to the target (write), the target may specify + * which blocks of data it is ready to receive. The target may request + * that the data blocks be delivered in whichever order is convenient + * for the target at that particular instant. This information is + * passed from the target to the initiator in the Ready To Transfer + * (R2T) PDU. + * + * In order to allow write operations without an explicit initial R2T, + * the initiator and target MUST have negotiated the key InitialR2T to + * No during login. + * + * An R2T MAY be answered with one or more SCSI Data-Out PDUs with a + * matching Target Transfer Tag. If an R2T is answered with a single + * Data-Out PDU, the buffer offset in the data PDU MUST be the same as + * the one specified by the R2T, and the data length of the data PDU + * MUST be the same as the Desired Data Transfer Length specified in the + * R2T. If the R2T is answered with a sequence of data PDUs, the buffer + * offset and length MUST be within the range of those specified by the + * R2T, and the last PDU MUST have the F bit set to 1. If the last PDU + * (marked with the F bit) is received before the Desired Data Transfer + * Length is transferred, a target MAY choose to reject that PDU with + * the "Protocol Error" reason code. DataPDUInOrder governs the + * Data-Out PDU ordering. If DataPDUInOrder is set to Yes, the buffer + * offsets and lengths for consecutive PDUs MUST form a continuous + * non-overlapping range, and the PDUs MUST be sent in increasing offset + * order. + * + * The target may send several R2T PDUs. It therefore can have a number + * of pending data transfers. The number of outstanding R2T PDUs is + * limited by the value of the negotiated key MaxOutstandingR2T. Within + * a task, outstanding R2Ts MUST be fulfilled by the initiator in the + * order in which they were received. + * + * R2T PDUs MAY also be used to recover Data-Out PDUs. Such an R2T + * (Recovery-R2T) is generated by a target upon detecting the loss of + * one or more Data-Out PDUs due to: + * + * - Digest error + * + * - Sequence error + * + * - Sequence reception timeout + * + * A Recovery-R2T carries the next unused R2TSN but requests part of or + * the entire data burst that an earlier R2T (with a lower R2TSN) had + * already requested. + * + * DataSequenceInOrder governs the buffer offset ordering in consecutive + * R2Ts. If DataSequenceInOrder is Yes, then consecutive R2Ts MUST + * refer to continuous non-overlapping ranges, except for Recovery-R2Ts. + */ +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). + int8_t flags; + + /// Reserved for future usage, always MUST be 0 for now. + uint16_t reserved; + + /// TotalAHSLength, MUST be 0 for this PDU. + uint8_t total_ahs_len; + + /// DataSegmentLength, MUST be 0 0 for this PDU. + uint8_t ds_len[3]; + + /// Logical Unit Number (LUN) or Reserved. + uint64_t lun; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /// Target Transfer Tag (TTT). + uint32_t target_xfer_tag; + + /// The StatSN field will contain the next StatSN. The StatSN for this connection is not advanced after this PDU is sent. + uint32_t stat_sn; + + /// ExpCmdSN. + uint32_t exp_cmd_sn; + + /// MaxCmdSN. + uint32_t max_cmd_sn; + + /// DataSN. + uint32_t data_sn; + + /// Ready To Transfer Sequence Number (R2TSN) is the R2T PDU input PDU number within the command identified by the Initiator Task Tag. For bidirectional commands, R2T and Data-In PDUs share the input PDU numbering sequence. + uint32_t r2t_sn; + + /** + * @brief Buffer Offset. + * + * The target therefore also specifies a buffer offset that indicates + * the point at which the data transfer should begin, relative to the + * beginning of the total data transfer. + */ + uint32_t buf_offset; + + /** + * @brief Desired Data Transfer Length. + * + * The target specifies how many bytes it wants the initiator to send + * because of this R2T PDU. The target may request the data from the + * initiator in several chunks, not necessarily in the original order of + * the data. The Desired Data Transfer Length MUST NOT be 0 and MUST NOT + * exceed MaxBurstLength. + */ + uint32_t des_data_xfer_len; + + /// Optional header digest. + iscsi_header_digest hdr_digest; +} iscsi_r2t_packet; + + +/** + * @brief SCSI Asynchronous Message Event: SCSI Async Event. + * + * A SCSI asynchronous event is reported in the sense data. + * Sense Data that accompanies the report, in the data + * segment, identifies the condition. The sending of a + * SCSI event ("asynchronous event reporting" in SCSI + * terminology) is dependent on the target support for SCSI + * asynchronous event reporting as indicated in the + * standard INQUIRY data. Its use may be enabled by + * parameters in the SCSI Control mode page. + */ +#define ISCSI_ASYNC_MSG_EVENT_SCSI_ASYNC_EVENT 0x00 + +/** + * @brief SCSI Asynchronous Message Event: Logout Request. + * + * The target requests Logout. This Async Message MUST + * be sent on the same connection as the one requesting + * to be logged out. The initiator MUST honor this request + * by issuing a Logout as early as possible but no later + * than Parameter3 seconds. The initiator MUST send a Logout + * with a reason code of "close the connection" OR "close the + * session" to close all the connections. Once this message is + * received, the initiator SHOULD NOT issue new iSCSI commands on + * the connection to be logged out. The target MAY reject any + * new I/O requests that it receives after this message with the + * reason code "Waiting for Logout". If the initiator does not + * log out in Parameter3 seconds, the target should send an Async + * PDU with iSCSI event code "Dropped the connection" if possible + * or simply terminate the transport connection. Parameter1 and + * Parameter2 are reserved. + */ +#define ISCSI_ASYNC_MSG_EVENT_LOGOUT_REQUEST 0x01 + +/** + * @brief SCSI Asynchronous Message Event: Connection Drop Notification. + * + * The target indicates that it will drop the connection. + * The Parameter1 field indicates the CID of the connection that + * is going to be dropped.\n + * The Parameter2 field (Time2Wait) indicates, in seconds, the + * minimum time to wait before attempting to reconnect or + * reassign.\n + * The Parameter3 field (Time2Retain) indicates the maximum time + * allowed to reassign commands after the initial wait (in + * Parameter2).\n + * If the initiator does not attempt to reconnect and/or reassign + * the outstanding commands within the time specified by + * Parameter3, or if Parameter3 is 0, the target will terminate + * all outstanding commands on this connection. In this case, no + * other responses should be expected from the target for the + * outstanding commands on this connection.\n + * A value of 0 for Parameter2 indicates that reconnect can be + * attempted immediately. + */ +#define ISCSI_ASYNC_MSG_EVENT_CONNECT_DROP_NOTIFY 0x02 + +/** + * @brief SCSI Asynchronous Message Event: Session Drop Notification. + * + * The target indicates that it will drop all the connections + * of this session.\n + * The Parameter1 field is reserved.\n + * The Parameter2 field (Time2Wait) indicates, in seconds, the + * minimum time to wait before attempting to reconnect.\n + * The Parameter3 field (Time2Retain) indicates the maximum time + * allowed to reassign commands after the initial wait (in + * Parameter2).\n + * If the initiator does not attempt to reconnect and/or reassign + * the outstanding commands within the time specified by + * Parameter3, or if Parameter3 is 0, the session is terminated.\n + * In this case, the target will terminate all outstanding + * commands in this session; no other responses should be + * expected from the target for the outstanding commands in this + * session. A value of 0 for Parameter2 indicates that reconnect + * can be attempted immediately. + */ +#define ISCSI_ASYNC_MSG_EVENT_SESSION_DROP_NOTIFY 0x03 + +/** + * @brief SCSI Asynchronous Message Event: Negotiation Request. + * + * The target requests parameter negotiation on this connection. + * The initiator MUST honor this request by issuing a Text + * Request (that can be empty) on the same connection as early + * as possible, but no later than Parameter3 seconds, unless a + * Text Request is already pending on the connection, or by + * issuing a Logout Request. If the initiator does not issue a + * Text Request, the target may reissue the Asynchronous Message + * requesting parameter negotiation. + */ +#define ISCSI_ASYNC_MSG_EVENT_NEGOTIATION_REQUEST 0x04 + +/** + * @brief SCSI Asynchronous Message Event: Task Termination. + * + * All active tasks for a LU with a matching LUN field in the + * Async Message PDU are being terminated. The receiving + * initiator iSCSI layer MUST respond to this message by + * taking the following steps, in order: + * - Stop Data-Out transfers on that connection for all active + * TTTs for the affected LUN quoted in the Async Message PDU. + * - Acknowledge the StatSN of the Async Message PDU via a + * NOP-Out PDU with ITT=0xFFFFFFFF (i.e., non-ping flavor), + * while copying the LUN field from the Async Message to + * NOP-Out. + * This value of AsyncEvent, however, MUST NOT be used on an + * iSCSI session unless the new TaskReporting text key was + * negotiated to FastAbort on the session. + */ +#define ISCSI_ASYNC_MSG_EVENT_TASK_TERMINATION 0x05 + +/// SCSI Asynchronous Message Event: First vendor-specific iSCSI event. The AsyncVCode details the vendor code, and data MAY accompany the report. +#define ISCSI_ASYNC_MSG_EVENT_VENDOR_FIRST 0xF8 + +/// SCSI Asynchronous Message Event: Last vendor-specific iSCSI event. The AsyncVCode details the vendor code, and data MAY accompany the report. +#define ISCSI_ASYNC_MSG_EVENT_VENDOR_LAST 0xFF + +/** + * @brief iSCSI Asynchronous Message packet data. + * + * An Asynchronous Message may be sent from the target to the initiator + * without corresponding to a particular command. The target specifies + * the reason for the event and sense data.\n + * Some Asynchronous Messages are strictly related to iSCSI, while + * others are related to SCSI + */ +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). + int8_t flags; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength, MUST be 0 for this PDU. + uint8_t total_ahs_len; + + /// DataSegmentLength, MUST be 0 0 for this PDU. + uint8_t ds_len[3]; + + /// The LUN field MUST be valid if AsyncEvent is 0. Otherwise, this field is reserved. + uint64_t lun; + + /// Tag (always 0xFFFFFFFF for now). + uint32_t tag; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved2; + + /** + * @brief StatSN. + * + * The StatSN counts this PDU as an acknowledgeable event (the StatSN is + * advanced), which allows for initiator and target state synchronization. + */ + uint32_t stat_sn; + + /// ExpCmdSN. + uint32_t exp_cmd_sn; + + /// MaxCmdSN. + uint32_t max_cmd_sn; + + /// AsyncEvent. + uint8_t async_event; + + /// AsyncVCode is a vendor-specific detail code that is only valid if the AsyncEvent field indicates a vendor-specific event. Otherwise, it is reserved. + uint8_t async_vcode; + + /// Parameter1 or Reserved. + uint16_t param_1; + + /// Parameter2 or Reserved. + uint16_t param_2; + + /// Parameter3 or Reserved. + uint16_t param_3; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved3; + + /// Optional header digest. + iscsi_header_digest hdr_digest; + + /// Data segment. + iscsi_ds_cmd_data ds_cmd_data; + + /// Optional data digest. + iscsi_data_digest data_digest; +} 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 + * pairs) in this Text Request is not complete (it will be continued on + * subsequent Text Requests); otherwise, it indicates that this Text + * Request ends a set of key=value pairs. A Text Request with the C bit + * set to 1 MUST have the F bit set to 0. + */ +#define ISCSI_TEXT_REQ_FLAGS_CONTINUE (1 << 6) + +/** + * @brief Text Request flags: Final. + * + * (F) When set to 1, this bit indicates that this is the last or only Text + * Request in a sequence of Text Requests; otherwise, it indicates that + * more Text Requests will follow. + */ +#define ISCSI_TEXT_REQ_FLAGS_FINAL (1 << 7) + +/** + * @brief iSCSI Text Request packet data. + * + * The Text Request is provided to allow for the exchange of information + * and for future extensions. It permits the initiator to inform a + * target of its capabilities or request some special operations. + * + * An initiator MUST NOT have more than one outstanding Text Request on + * a connection at any given time. + * + * On a connection failure, an initiator must either explicitly abort + * any active allegiant text negotiation task or cause such a task to be + * implicitly terminated by the target. + */ +typedef struct __attribute__((packed)) iscsi_text_req_packet { + /// Always 0x04 according to iSCSI specification. + uint8_t opcode; + + /// Text request flags. + int8_t flags; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// Logical Unit Number (LUN) or Reserved. + uint64_t lun; + + /** + * @brief Initiator Task Tag (ITT). + * + * This is the initiator-assigned identifier for this Text Request. If + * the command is sent as part of a sequence of Text Requests and + * responses, the Initiator Task Tag MUST be the same for all the + * requests within the sequence (similar to linked SCSI commands). The + * I bit for all requests in a sequence also MUST be the same. + */ + uint32_t init_task_tag; + + /** + * @brief Target Transfer Tag (TTT). + * + * When the Target Transfer Tag is set to the reserved value 0xFFFFFFFF, + * it tells the target that this is a new request, and the target resets + * any internal state associated with the Initiator Task Tag (resets the + * current negotiation state).\n + * The target sets the Target Transfer Tag in a Text Response to a value + * other than the reserved value 0xFFFFFFFF whenever it indicates that + * it has more data to send or more operations to perform that are + * associated with the specified Initiator Task Tag. It MUST do so + * whenever it sets the F bit to 0 in the response. By copying the + * Target Transfer Tag from the response to the next Text Request, the + * initiator tells the target to continue the operation for the specific + * Initiator Task Tag. The initiator MUST ignore the Target Transfer + * Tag in the Text Response when the F bit is set to 1.\n + * This mechanism allows the initiator and target to transfer a large + * amount of textual data over a sequence of text-command/text-response + * exchanges or to perform extended negotiation sequences.\n + * If the Target Transfer Tag is not 0xFFFFFFFF, the LUN field MUST be + * sent by the target in the Text Response.\n + * A target MAY reset its internal negotiation state if an exchange is + * stalled by the initiator for a long time or if it is running out of + * resources.\n + * Long Text Responses are handled as shown in the following example:\n + * @verbatim + * I->T Text SendTargets=All (F = 1, TTT = 0xFFFFFFFF) + * T->I Text (F = 0, TTT = 0x12345678) + * I->T Text (F = 1, TTT = 0x12345678) + * T->I Text (F = 0, TTT = 0x12345678) + * I->T Text (F = 1, TTT = 0x12345678) + * ... + * T->I Text (F = 1, TTT = 0xFFFFFFFF) + * @endverbatim + */ + uint32_t target_xfer_tag; + + /// CmdSN. + uint32_t cmd_sn; + + /// ExpStatSN. + uint32_t exp_stat_sn; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2[2]; + + /// Optional header digest. + iscsi_header_digest hdr_digest; + + /** + * @brief Data segment. + * + * The data lengths of a Text Request MUST NOT exceed the iSCSI target + * MaxRecvDataSegmentLength (a parameter that is negotiated per + * connection and per direction).\n + * A key=value pair can span Text Request or Text Response boundaries. + * A key=value pair can start in one PDU and continue on the next. In + * other words, the end of a PDU does not necessarily signal the end of + * a key=value pair.\n + * The target responds by sending its response back to the initiator. + * The response text format is similar to the request text format. The + * Text Response MAY refer to key=value pairs presented in an earlier + * Text Request, and the text in the request may refer to earlier + * responses.\n + * 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; + + /// Optional data digest. + iscsi_data_digest data_digest; +} iscsi_text_req_packet; + + +/** + * @brief Text Response flags: Continue. + * + * (C) When set to 1, this bit indicates that the text (set of key=value + * pairs) in this Text Response is not complete (it will be continued on + * subsequent Text Responses); otherwise, it indicates that this Text + * Response ends a set of key=value pairs. A Text Response with the + * C bit set to 1 MUST have the F bit set to 0. + */ +#define ISCSI_TEXT_RESPONSE_FLAGS_CONTINUE (1 << 6) + +/** + * @brief Text Response flags: Final. + * + * (F) When set to 1, in response to a Text Request with the Final bit set + * to 1, the F bit indicates that the target has finished the whole + * operation. Otherwise, if set to 0 in response to a Text Request with + * the Final Bit set to 1, it indicates that the target has more work to + * do (invites a follow-on Text Request). A Text Response with the + * F bit set to 1 in response to a Text Request with the F bit set to 0 + * is a protocol error.\n + * A Text Response with the F bit set to 1 MUST NOT contain key=value + * pairs that may require additional answers from the initiator. + * A Text Response with the F bit set to 1 MUST have a Target Transfer + * Tag field set to the reserved value 0xFFFFFFFF.\n + * A Text Response with the F bit set to 0 MUST have a Target Transfer + * Tag field set to a value other than the reserved value 0xFFFFFFFF. + */ +#define ISCSI_TEXT_RESPONSE_FLAGS_FINAL (1 << 7) + +/** + * @brief iSCSI Text Response packet data. + * + * The Text Response PDU contains the target's responses to the + * initiator's Text Request. The format of the Text field matches that + * of the Text Request. + */ +typedef struct __attribute__((packed)) iscsi_text_response_packet { + /// Always 0x24 according to iSCSI specification. + uint8_t opcode; + + /// Text response flags. + int8_t flags; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// Logical Unit Number (LUN) or Reserved. + uint64_t lun; + + /// The Initiator Task Tag matches the tag used in the initial Text Request. + uint32_t init_task_tag; + + /** + * @brief Target Transfer Tag (TTT). + * + * When a target has more work to do (e.g., cannot transfer all the + * remaining text data in a single Text Response or has to continue the + * negotiation) and has enough resources to proceed, it MUST set the + * Target Transfer Tag to a value other than the reserved value + * 0xFFFFFFFF. Otherwise, the Target Transfer Tag MUST be set to + * 0xFFFFFFFF.\n + * When the Target Transfer Tag is not 0xFFFFFFFF, the LUN field may be + * significant.\n + * The initiator MUST copy the Target Transfer Tag and LUN in its next + * request to indicate that it wants the rest of the data.\n + * When the target receives a Text Request with the Target Transfer Tag + * set to the reserved value 0xFFFFFFFF, it resets its internal + * information (resets state) associated with the given Initiator Task + * Tag (restarts the negotiation).\n + * When a target cannot finish the operation in a single Text Response + * and does not have enough resources to continue, it rejects the Text + * Request with the appropriate Reject code.\n + * A target may reset its internal state associated with an Initiator + * Task Tag (the current negotiation state) as expressed through the + * Target Transfer Tag if the initiator fails to continue the exchange + * for some time. The target may reject subsequent Text Requests with + * the Target Transfer Tag set to the "stale" value. + */ + uint32_t target_xfer_tag; + + /// StatSN. The target StatSN variable is advanced by each Text Response sent. + uint32_t stat_sn; + + /// ExpCmdSN. + uint32_t exp_cmd_sn; + + /// MaxCmdSN. + uint32_t max_cmd_sn; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2[2]; + + /// Optional header digest. + iscsi_header_digest hdr_digest; + + /** + * @brief Data segment. + * + * The data lengths of a Text Response MUST NOT exceed the iSCSI + * initiator MaxRecvDataSegmentLength (a parameter that is negotiated + * per connection and per direction).\n + * The text in the Text Response Data is governed by the same rules as + * the text in the Text Request Data.\n + * Although the initiator is the requesting party and controls the + * request-response initiation and termination, the target can offer + * 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; + + /// 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. + * + * 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 + +/** + * @brief 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 + +/** + * @brief Initiator Session ID (ISID) type: Random. + * + * A: Reserved + * B and C: Random + * D: Qualifier + */ +#define ISCSI_ISID_TYPE_FORMAT_RANDOM 0x2 + +/** + * @brief iSCSI Initiator Session ID (ISID) packet data. + * + * This is an initiator-defined component of the session identifier and + * is structured as follows: + * + * For the T field values 00b and 01b, a combination of A and B (for + * 00b) or B and C (for 01b) identifies the vendor or organization whose + * component (software or hardware) generates this ISID. A vendor or + * organization with one or more OUIs, or one or more Enterprise + * Numbers, MUST use at least one of these numbers and select the + * appropriate value for the T field when its components generate ISIDs. + * An OUI or EN MUST be set in the corresponding fields in network byte + * order (byte big-endian). + * + * If the T field is 10b, B and C are set to a random 24-bit unsigned + * integer value in network byte order (byte big-endian). + * + * The Qualifier field is a 16-bit or 24-bit unsigned integer value that + * provides a range of possible values for the ISID within the selected + * namespace. It may be set to any value within the constraints + * specified in the iSCSI protocol. + * + * If the ISID is derived from something assigned to a hardware adapter + * or interface by a vendor as a preset default value, it MUST be + * configurable to a value assigned according to the SCSI port behavior + * desired by the system in which it is installed. The resultant ISID + * MUST also be persistent over power cycles, reboot, card swap, etc. + */ +typedef struct __attribute__((packed)) iscsi_isid { + /// Meaning depends on T bit, either 22-bit OUI or reserved. + uint8_t a; + + /// Meaning depends on T bit, either 22-bit OUI, EN (IANA Enterprise Number) or random. + uint16_t b; + + /// Meaning depends on T bit, either 24-bit Qualifier, EN (IANA Enterprise Number) or random. + uint8_t c; + + /// Meaning depends on T bit, either 24-bit Qualifier or Qualifier. + uint16_t d; +} iscsi_isid; + + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Session type. + * + * @verbatim + * Use: LO, Declarative, Any-Stage + * Senders: Initiator + * Scope: SW + * SessionType= + * Default is Normal. + * @endverbatim + * The initiator indicates the type of session it wants to create. The + * target can either accept it or reject it.\n + * A Discovery session indicates to the target that the only purpose of + * this session is discovery. The only requests a target accepts in + * this type of session are a Text Request with a SendTargets key and a + * Logout Request with reason "close the session".\n + * The Discovery session implies MaxConnections = 1 and overrides both + * the default and an explicit setting. ErrorRecoveryLevel MUST be 0 + * (zero) for Discovery sessions.\n + * Depending on the type of session, a target may decide on resources to + * allocate, the security to enforce, etc., for the session. If the + * 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" + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Initiator name. + * + * @verbatim + * Use: IO, Declarative, Any-Stage + * Senders: Initiator + * Scope: SW + * InitiatorName= + * Examples: + * InitiatorName=iqn.1992-04.de.uni-freiburg.bwlehrpool:qcow2.5003 + * InitiatorName=iqn.2001-02.de.uni-freiburg.matrix:basty.eduroam + * InitiatorName=naa.52004567BA64678D + * @endverbatim + * The initiator of the TCP connection MUST provide this key to the + * remote endpoint at the first login of the Login Phase for every + * connection. The InitiatorName key enables the initiator to identify + * 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" + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Target name. + * + * @verbatim + * Use: IO by initiator, FFPO by target - only as response to a + * SendTargets, Declarative, Any-Stage + * Senders: Initiator and target + * Scope: SW + * TargetName= + * Examples: + * TargetName=iqn.1993-11.de.uni-freiburg:diskarrays.sn.5003 + * TargetName=eui.020000023B040506 + * TargetName=naa.62004567BA64678D0123456789ABCDEF + * @endverbatim + * The initiator of the TCP connection MUST provide this key to the + * remote endpoint in the first Login Request if the initiator is not + * establishing a Discovery session. The iSCSI Target Name specifies + * the worldwide unique name of the target.\n + * The TargetName key may also be returned by the SendTargets Text + * 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" + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Target address. + * + * @verbatim + * Use: ALL, Declarative, Any-Stage + * Senders: Target + * Scope: SW + * TargetAddress=domainname[:port][,portal-group-tag] + * @endverbatim + * The domainname can be specified as either a DNS host name, a dotted- + * decimal IPv4 address, or a bracketed IPv6 address as specified in + * RFC3986.\n + * If the TCP port is not specified, it is assumed to be the IANA- + * assigned default port for iSCSI.\n + * If the TargetAddress is returned as the result of a redirect status + * in a Login Response, the comma and portal-group-tag MUST be omitted. + * If the TargetAddress is returned within a SendTargets response, the + * portal-group-tag MUST be included.\n + * @verbatim + * Examples: + * TargetAddress=10.0.0.1:5003,1 + * TargetAddress=[1080:0:0:0:8:800:200C:417A],65 + * TargetAddress=[1080::8:800:200C:417A]:5003,1 + * TargetAddress=gitlab.uni-freiburg.de,443 + * @endverbatim + * 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" + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Initiator alias. + * + * @verbatim + * Use: ALL, Declarative, Any-Stage + * Senders: Initiator + * Scope: SW + * InitiatorAlias= + * Examples: + * InitiatorAlias=Web Server 5 + * InitiatorAlias=matrix.uni-freiburg.de + * InitiatorAlias=Matrix Server + * @endverbatim + * If an initiator has been configured with a human-readable name or + * description, it SHOULD be communicated to the target during a Login + * Request PDU. If not, the host name can be used instead. This string + * is not used as an identifier, nor is it meant to be used for + * authentication or authorization decisions. It can be displayed by + * 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" + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Target alias. + * + * @verbatim + * Use: ALL, Declarative, Any-Stage + * Senders: Target + * Scope: SW + * TargetAlias= + * Examples: + * TargetAlias=Bob-s Disk + * TargetAlias=Database Server 1 Log Disk + * TargetAlias=Web Server 3 Disk 20 + * @endverbatim + * If a target has been configured with a human-readable name or + * description, this name SHOULD be communicated to the initiator during + * a Login Response PDU if SessionType=Normal. This string is not used + * as an identifier, nor is it meant to be used for authentication or + * 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" + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Target portal group tag. + * + * @verbatim + * Use: IO by target, Declarative, Any-Stage + * Senders: Target + * Scope: SW + * TargetPortalGroupTag=<16-bit-binary-value> + * Example: + * TargetPortalGroupTag=1 + * @endverbatim + * The TargetPortalGroupTag key is a 16-bit binary-value that uniquely + * identifies a portal group within an iSCSI target node. This key + * carries the value of the tag of the portal group that is servicing + * the Login Request. The iSCSI target returns this key to the + * initiator in the Login Response PDU to the first Login Request PDU + * that has the C bit set to 0 when TargetName is given by the + * initiator.\n + * SAM2 notes in its informative text that the TPGT value should be + * non-zero; note that this is incorrect. A zero value is allowed as a + * legal value for the TPGT. This discrepancy currently stands + * corrected in SAM4. + */ +#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG "TargetPortalGroupTag" + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Authentication method. + * + * @verbatim + * Use: During Login - Security Negotiation + * Senders: Initiator and target + * Scope: connection + * AuthMethod = + * @endverbatim + * The main item of security negotiation is the authentication method + * (AuthMethod).\n + * The authentication methods that can be used (appear in the list-of- + * values) are either vendor-unique methods or those listed in the + * following table: + * Name | Description + * :--- | :--------------------------------------------------------------- + * KRB5 | Kerberos V5 - defined in RFC4120 + * SRP | Secure Remote Password - defined in RFC2945 + * CHAP | Challenge Handshake Authentication Protocol - defined in RFC1994 + * None | No authentication + * + * The AuthMethod selection is followed by an "authentication exchange" + * specific to the authentication method selected.\n + * The authentication method proposal may be made by either the + * initiator or the target. However, the initiator MUST make the first + * step specific to the selected authentication method as soon as it is + * selected. It follows that if the target makes the authentication + * method proposal, the initiator sends the first key(s) of the exchange + * together with its authentication method selection.\n + * The authentication exchange authenticates the initiator to the target + * and, optionally, the target to the initiator. Authentication is + * OPTIONAL to use but MUST be supported by the target and initiator. + * The initiator and target MUST implement CHAP. All other + * authentication methods are OPTIONAL.\n + * Private or public extension algorithms MAY also be negotiated for + * authentication methods. Whenever a private or public extension + * algorithm is part of the default offer (the offer made in the absence + * of explicit administrative action), the implementer MUST ensure that + * CHAP is listed as an alternative in the default offer and "None" is + * not part of the default offer.\n + * Extension authentication methods MUST be named using one of the + * following two formats: + * -# Z-reversed.vendor.dns_name.do_something= + * -# New public key with no name prefix constraints + * + * Authentication methods named using the Z- format are used as private + * extensions. New public keys must be registered with IANA using the + * IETF Review process RFC5226. New public extensions for + * authentication methods MUST NOT use the Z# name prefix.\n + * For all of the public or private extension authentication methods, + * the method-specific keys MUST conform to the format specified for + * standard-label.\n + * To identify the vendor for private extension authentication methods, + * we suggest using the reversed DNS-name as a prefix to the proper + * digest names.\n + * The part of digest-name following Z- MUST conform to the format for + * standard-label.\n + * Support for public or private extension authentication methods is + * OPTIONAL. + */ +#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD "AuthMethod" + + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Kerberos V5 (KRB5): KRB_AP_REQ. + * + * For KRB5 (Kerberos V5) (see RFC4120 and RFC1964), the initiator MUST use: + * @verbatim + * KRB_AP_REQ= + * @endverbatim + * where KRB_AP_REQ is the client message as defined in RFC4120. + * The default principal name assumed by an iSCSI initiator or target + * (prior to any administrative configuration action) MUST be the iSCSI + * Initiator Name or iSCSI Target Name, respectively, prefixed by the + * string "iscsi/".\n + * If the initiator authentication fails, the target MUST respond with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator has selected the mutual authentication option (by setting + * MUTUAL-REQUIRED in the ap-options field of the KRB_AP_REQ), the + * target MUST reply with: + * @verbatim + * KRB_AP_REP= + * @endverbatim + * where KRB_AP_REP is the server's response message as defined in + * RFC4120.\n + * If mutual authentication was selected and target authentication + * fails, the initiator MUST close the connection.\n + * KRB_AP_REQ and KRB_AP_REP are binary-values, and their binary length + * (not the length of the character string that represents them in + * 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" + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Kerberos V5 (KRB5): KRB_AP_REP. + * + * For KRB5 (Kerberos V5) (see RFC4120 and RFC1964), the initiator MUST use: + * @verbatim + * KRB_AP_REQ= + * @endverbatim + * where KRB_AP_REQ is the client message as defined in RFC4120. + * The default principal name assumed by an iSCSI initiator or target + * (prior to any administrative configuration action) MUST be the iSCSI + * Initiator Name or iSCSI Target Name, respectively, prefixed by the + * string "iscsi/".\n + * If the initiator authentication fails, the target MUST respond with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator has selected the mutual authentication option (by setting + * MUTUAL-REQUIRED in the ap-options field of the KRB_AP_REQ), the + * target MUST reply with: + * @verbatim + * KRB_AP_REP= + * @endverbatim + * where KRB_AP_REP is the server's response message as defined in + * RFC4120.\n + * If mutual authentication was selected and target authentication + * fails, the initiator MUST close the connection.\n + * KRB_AP_REQ and KRB_AP_REP are binary-values, and their binary length + * (not the length of the character string that represents them in + * 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" + + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_U. + * + * For SRP RFC2945, the initiator MUST use: + * @verbatim + * SRP_U= TargetAuth=Yes or TargetAuth=No + * @endverbatim + * The target MUST answer with a Login reject with the "Authorization + * Failure" status or reply with: + * @verbatim + * SRP_GROUP= SRP_s= + * @endverbatim + * where G1,G2... are proposed groups, in order of preference. + * The initiator MUST either close the connection or continue with: + * @verbatim + * SRP_A= + * SRP_GROUP= + * @endverbatim + * where G is one of G1,G2... that were proposed by the target. + * The target MUST answer with a Login reject with the "Authentication + * Failure" status or reply with: + * @verbatim + * SRP_B= + * @endverbatim + * The initiator MUST close the connection or continue with: + * @verbatim + * SRP_M= + * @endverbatim + * If the initiator authentication fails, the target MUST answer with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator sent TargetAuth=Yes in the first message (requiring target + * authentication), the target MUST reply with: + * @verbatim + * SRP_HM= + * @endverbatim + * If the target authentication fails, the initiator MUST close the + * connection:\n + * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using + * the SHA1 hash function, such as SRP-SHA1) and + * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups + * specified in RFC3723.\n + * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are + * binary-values. The length of s,A,B,M and H(A | M | K) in binary form + * (not the length of the character string that represents them in + * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may + * be used for s,A,B,M and H(A | M | K).\n + * For the SRP_GROUP, all the groups specified in RFC3723 up to + * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be + * 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" + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_GROUP. + * + * For SRP RFC2945, the initiator MUST use: + * @verbatim + * SRP_U= TargetAuth=Yes or TargetAuth=No + * @endverbatim + * The target MUST answer with a Login reject with the "Authorization + * Failure" status or reply with: + * @verbatim + * SRP_GROUP= SRP_s= + * @endverbatim + * where G1,G2... are proposed groups, in order of preference. + * The initiator MUST either close the connection or continue with: + * @verbatim + * SRP_A= + * SRP_GROUP= + * @endverbatim + * where G is one of G1,G2... that were proposed by the target. + * The target MUST answer with a Login reject with the "Authentication + * Failure" status or reply with: + * @verbatim + * SRP_B= + * @endverbatim + * The initiator MUST close the connection or continue with: + * @verbatim + * SRP_M= + * @endverbatim + * If the initiator authentication fails, the target MUST answer with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator sent TargetAuth=Yes in the first message (requiring target + * authentication), the target MUST reply with: + * @verbatim + * SRP_HM= + * @endverbatim + * If the target authentication fails, the initiator MUST close the + * connection:\n + * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using + * the SHA1 hash function, such as SRP-SHA1) and + * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups + * specified in RFC3723.\n + * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are + * binary-values. The length of s,A,B,M and H(A | M | K) in binary form + * (not the length of the character string that represents them in + * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may + * be used for s,A,B,M and H(A | M | K).\n + * For the SRP_GROUP, all the groups specified in RFC3723 up to + * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be + * 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" + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_A. + * + * For SRP RFC2945, the initiator MUST use: + * @verbatim + * SRP_U= TargetAuth=Yes or TargetAuth=No + * @endverbatim + * The target MUST answer with a Login reject with the "Authorization + * Failure" status or reply with: + * @verbatim + * SRP_GROUP= SRP_s= + * @endverbatim + * where G1,G2... are proposed groups, in order of preference. + * The initiator MUST either close the connection or continue with: + * @verbatim + * SRP_A= + * SRP_GROUP= + * @endverbatim + * where G is one of G1,G2... that were proposed by the target. + * The target MUST answer with a Login reject with the "Authentication + * Failure" status or reply with: + * @verbatim + * SRP_B= + * @endverbatim + * The initiator MUST close the connection or continue with: + * @verbatim + * SRP_M= + * @endverbatim + * If the initiator authentication fails, the target MUST answer with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator sent TargetAuth=Yes in the first message (requiring target + * authentication), the target MUST reply with: + * @verbatim + * SRP_HM= + * @endverbatim + * If the target authentication fails, the initiator MUST close the + * connection:\n + * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using + * the SHA1 hash function, such as SRP-SHA1) and + * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups + * specified in RFC3723.\n + * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are + * binary-values. The length of s,A,B,M and H(A | M | K) in binary form + * (not the length of the character string that represents them in + * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may + * be used for s,A,B,M and H(A | M | K).\n + * For the SRP_GROUP, all the groups specified in RFC3723 up to + * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be + * 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" + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_B. + * + * For SRP RFC2945, the initiator MUST use: + * @verbatim + * SRP_U= TargetAuth=Yes or TargetAuth=No + * @endverbatim + * The target MUST answer with a Login reject with the "Authorization + * Failure" status or reply with: + * @verbatim + * SRP_GROUP= SRP_s= + * @endverbatim + * where G1,G2... are proposed groups, in order of preference. + * The initiator MUST either close the connection or continue with: + * @verbatim + * SRP_A= + * SRP_GROUP= + * @endverbatim + * where G is one of G1,G2... that were proposed by the target. + * The target MUST answer with a Login reject with the "Authentication + * Failure" status or reply with: + * @verbatim + * SRP_B= + * @endverbatim + * The initiator MUST close the connection or continue with: + * @verbatim + * SRP_M= + * @endverbatim + * If the initiator authentication fails, the target MUST answer with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator sent TargetAuth=Yes in the first message (requiring target + * authentication), the target MUST reply with: + * @verbatim + * SRP_HM= + * @endverbatim + * If the target authentication fails, the initiator MUST close the + * connection:\n + * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using + * the SHA1 hash function, such as SRP-SHA1) and + * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups + * specified in RFC3723.\n + * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are + * binary-values. The length of s,A,B,M and H(A | M | K) in binary form + * (not the length of the character string that represents them in + * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may + * be used for s,A,B,M and H(A | M | K).\n + * For the SRP_GROUP, all the groups specified in RFC3723 up to + * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be + * 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" + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_M. + * + * For SRP RFC2945, the initiator MUST use: + * @verbatim + * SRP_U= TargetAuth=Yes or TargetAuth=No + * @endverbatim + * The target MUST answer with a Login reject with the "Authorization + * Failure" status or reply with: + * @verbatim + * SRP_GROUP= SRP_s= + * @endverbatim + * where G1,G2... are proposed groups, in order of preference. + * The initiator MUST either close the connection or continue with: + * @verbatim + * SRP_A= + * SRP_GROUP= + * @endverbatim + * where G is one of G1,G2... that were proposed by the target. + * The target MUST answer with a Login reject with the "Authentication + * Failure" status or reply with: + * @verbatim + * SRP_B= + * @endverbatim + * The initiator MUST close the connection or continue with: + * @verbatim + * SRP_M= + * @endverbatim + * If the initiator authentication fails, the target MUST answer with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator sent TargetAuth=Yes in the first message (requiring target + * authentication), the target MUST reply with: + * @verbatim + * SRP_HM= + * @endverbatim + * If the target authentication fails, the initiator MUST close the + * connection:\n + * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using + * the SHA1 hash function, such as SRP-SHA1) and + * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups + * specified in RFC3723.\n + * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are + * binary-values. The length of s,A,B,M and H(A | M | K) in binary form + * (not the length of the character string that represents them in + * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may + * be used for s,A,B,M and H(A | M | K).\n + * For the SRP_GROUP, all the groups specified in RFC3723 up to + * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be + * 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" + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_HM. + * + * For SRP RFC2945, the initiator MUST use: + * @verbatim + * SRP_U= TargetAuth=Yes or TargetAuth=No + * @endverbatim + * The target MUST answer with a Login reject with the "Authorization + * Failure" status or reply with: + * @verbatim + * SRP_GROUP= SRP_s= + * @endverbatim + * where G1,G2... are proposed groups, in order of preference. + * The initiator MUST either close the connection or continue with: + * @verbatim + * SRP_A= + * SRP_GROUP= + * @endverbatim + * where G is one of G1,G2... that were proposed by the target. + * The target MUST answer with a Login reject with the "Authentication + * Failure" status or reply with: + * @verbatim + * SRP_B= + * @endverbatim + * The initiator MUST close the connection or continue with: + * @verbatim + * SRP_M= + * @endverbatim + * If the initiator authentication fails, the target MUST answer with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator sent TargetAuth=Yes in the first message (requiring target + * authentication), the target MUST reply with: + * @verbatim + * SRP_HM= + * @endverbatim + * If the target authentication fails, the initiator MUST close the + * connection:\n + * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using + * the SHA1 hash function, such as SRP-SHA1) and + * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups + * specified in RFC3723.\n + * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are + * binary-values. The length of s,A,B,M and H(A | M | K) in binary form + * (not the length of the character string that represents them in + * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may + * be used for s,A,B,M and H(A | M | K).\n + * For the SRP_GROUP, all the groups specified in RFC3723 up to + * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be + * 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" + + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_A. + * + * For CHAP RFC1994, the initiator MUST use: + * @verbatim + * CHAP_A= + * @endverbatim + * where A1,A2... are proposed algorithms, in order of preference. + * The target MUST answer with a Login reject with the "Authentication + * Failure" status or reply with: + * @verbatim + * CHAP_A= + * CHAP_I= + * CHAP_C= + * @endverbatim + * where A is one of A1,A2... that were proposed by the initiator. + * The initiator MUST continue with: + * @verbatim + * CHAP_N= + * CHAP_R= + * @endverbatim + * or, if it requires target authentication, with: + * @verbatim + * CHAP_N= + * CHAP_R= + * CHAP_I= + * CHAP_C= + * @endverbatim + * If the initiator authentication fails, the target MUST answer with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator required target authentication, the target MUST either + * answer with a Login reject with "Authentication Failure" or reply + * with: + * @verbatim + * CHAP_N= + * CHAP_R= + * @endverbatim + * If the target authentication fails, the initiator MUST close the + * connection:\n + * where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, + * Algorithm, Identifier, Challenge, and Response as defined in + * RFC1994.\n + * N is a text string; A,A1,A2, and I are numbers; C and R are + * binary-values. Their binary length (not the length of the character + * string that represents them in encoded form) MUST NOT exceed + * 1024 bytes. Hex or Base64 encoding may be used for C and R.\n + * For the Algorithm, as stated in [RFC1994], one value is required to + * be implemented: + * @verbatim + * 5 (CHAP with MD5) + * @endverbatim + * 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" + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_I. + * + * For CHAP RFC1994, the initiator MUST use: + * @verbatim + * CHAP_A= + * @endverbatim + * where A1,A2... are proposed algorithms, in order of preference. + * The target MUST answer with a Login reject with the "Authentication + * Failure" status or reply with: + * @verbatim + * CHAP_A= + * CHAP_I= + * CHAP_C= + * @endverbatim + * where A is one of A1,A2... that were proposed by the initiator. + * The initiator MUST continue with: + * @verbatim + * CHAP_N= + * CHAP_R= + * @endverbatim + * or, if it requires target authentication, with: + * @verbatim + * CHAP_N= + * CHAP_R= + * CHAP_I= + * CHAP_C= + * @endverbatim + * If the initiator authentication fails, the target MUST answer with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator required target authentication, the target MUST either + * answer with a Login reject with "Authentication Failure" or reply + * with: + * @verbatim + * CHAP_N= + * CHAP_R= + * @endverbatim + * If the target authentication fails, the initiator MUST close the + * connection:\n + * where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, + * Algorithm, Identifier, Challenge, and Response as defined in + * RFC1994.\n + * N is a text string; A,A1,A2, and I are numbers; C and R are + * binary-values. Their binary length (not the length of the character + * string that represents them in encoded form) MUST NOT exceed + * 1024 bytes. Hex or Base64 encoding may be used for C and R.\n + * For the Algorithm, as stated in [RFC1994], one value is required to + * be implemented: + * @verbatim + * 5 (CHAP with MD5) + * @endverbatim + * 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" + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_C. + * + * For CHAP RFC1994, the initiator MUST use: + * @verbatim + * CHAP_A= + * @endverbatim + * where A1,A2... are proposed algorithms, in order of preference. + * The target MUST answer with a Login reject with the "Authentication + * Failure" status or reply with: + * @verbatim + * CHAP_A= + * CHAP_I= + * CHAP_C= + * @endverbatim + * where A is one of A1,A2... that were proposed by the initiator. + * The initiator MUST continue with: + * @verbatim + * CHAP_N= + * CHAP_R= + * @endverbatim + * or, if it requires target authentication, with: + * @verbatim + * CHAP_N= + * CHAP_R= + * CHAP_I= + * CHAP_C= + * @endverbatim + * If the initiator authentication fails, the target MUST answer with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator required target authentication, the target MUST either + * answer with a Login reject with "Authentication Failure" or reply + * with: + * @verbatim + * CHAP_N= + * CHAP_R= + * @endverbatim + * If the target authentication fails, the initiator MUST close the + * connection:\n + * where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, + * Algorithm, Identifier, Challenge, and Response as defined in + * RFC1994.\n + * N is a text string; A,A1,A2, and I are numbers; C and R are + * binary-values. Their binary length (not the length of the character + * string that represents them in encoded form) MUST NOT exceed + * 1024 bytes. Hex or Base64 encoding may be used for C and R.\n + * For the Algorithm, as stated in [RFC1994], one value is required to + * be implemented: + * @verbatim + * 5 (CHAP with MD5) + * @endverbatim + * 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" + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_N. + * + * For CHAP RFC1994, the initiator MUST use: + * @verbatim + * CHAP_A= + * @endverbatim + * where A1,A2... are proposed algorithms, in order of preference. + * The target MUST answer with a Login reject with the "Authentication + * Failure" status or reply with: + * @verbatim + * CHAP_A= + * CHAP_I= + * CHAP_C= + * @endverbatim + * where A is one of A1,A2... that were proposed by the initiator. + * The initiator MUST continue with: + * @verbatim + * CHAP_N= + * CHAP_R= + * @endverbatim + * or, if it requires target authentication, with: + * @verbatim + * CHAP_N= + * CHAP_R= + * CHAP_I= + * CHAP_C= + * @endverbatim + * If the initiator authentication fails, the target MUST answer with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator required target authentication, the target MUST either + * answer with a Login reject with "Authentication Failure" or reply + * with: + * @verbatim + * CHAP_N= + * CHAP_R= + * @endverbatim + * If the target authentication fails, the initiator MUST close the + * connection:\n + * where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, + * Algorithm, Identifier, Challenge, and Response as defined in + * RFC1994.\n + * N is a text string; A,A1,A2, and I are numbers; C and R are + * binary-values. Their binary length (not the length of the character + * string that represents them in encoded form) MUST NOT exceed + * 1024 bytes. Hex or Base64 encoding may be used for C and R.\n + * For the Algorithm, as stated in [RFC1994], one value is required to + * be implemented: + * @verbatim + * 5 (CHAP with MD5) + * @endverbatim + * 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" + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_R. + * + * For CHAP RFC1994, the initiator MUST use: + * @verbatim + * CHAP_A= + * @endverbatim + * where A1,A2... are proposed algorithms, in order of preference. + * The target MUST answer with a Login reject with the "Authentication + * Failure" status or reply with: + * @verbatim + * CHAP_A= + * CHAP_I= + * CHAP_C= + * @endverbatim + * where A is one of A1,A2... that were proposed by the initiator. + * The initiator MUST continue with: + * @verbatim + * CHAP_N= + * CHAP_R= + * @endverbatim + * or, if it requires target authentication, with: + * @verbatim + * CHAP_N= + * CHAP_R= + * CHAP_I= + * CHAP_C= + * @endverbatim + * If the initiator authentication fails, the target MUST answer with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator required target authentication, the target MUST either + * answer with a Login reject with "Authentication Failure" or reply + * with: + * @verbatim + * CHAP_N= + * CHAP_R= + * @endverbatim + * If the target authentication fails, the initiator MUST close the + * connection:\n + * where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, + * Algorithm, Identifier, Challenge, and Response as defined in + * RFC1994.\n + * N is a text string; A,A1,A2, and I are numbers; C and R are + * binary-values. Their binary length (not the length of the character + * string that represents them in encoded form) MUST NOT exceed + * 1024 bytes. Hex or Base64 encoding may be used for C and R.\n + * For the Algorithm, as stated in [RFC1994], one value is required to + * be implemented: + * @verbatim + * 5 (CHAP with MD5) + * @endverbatim + * 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" + +/* Login/Text Operational Text Keys + + Some session-specific parameters MUST only be carried on the leading + connection and cannot be changed after the leading connection login + (e.g., MaxConnections - the maximum number of connections). This + holds for a single connection session with regard to connection + restart. The keys that fall into this category have the "use: LO" + (Leading Only). + + Keys that can only be used during login have the "use: IO" + (Initialize Only), while those that can be used in both the Login + Phase and Full Feature Phase have the "use: ALL". + + Keys that can only be used during the Full Feature Phase use FFPO + (Full Feature Phase Only). + + Keys marked as Any-Stage may also appear in the SecurityNegotiation + stage, while all other keys described in this section are + operational keys. + + Keys that do not require an answer are marked as Declarative. + + Key scope is indicated as session-wide (SW) or connection-only (CO). + + "Result function", wherever mentioned, states the function that can + be applied to check the validity of the responder selection. + "Minimum" means that the selected value cannot exceed the offered + value. "Maximum" means that the selected value cannot be lower than + the offered value. "AND" means that the selected value must be a + possible result of a Boolean "and" function with an arbitrary Boolean + value (e.g., if the offered value is No the selected value must be + No). "OR" means that the selected value must be a possible result of + a Boolean "or" function with an arbitrary Boolean value (e.g., if the + offered value is Yes the selected value must be Yes). +*/ + +/** + * @brief Login/Text Operational Session Text Key: Header digest. + * + * @verbatim + * Use: IO + * Senders: Initiator and target + * Scope: CO + * HeaderDigest = + * Default is None for HeaderDigest. + * @endverbatim + * Digests enable the checking of end-to-end, non-cryptographic data + * integrity beyond the integrity checks provided by the link layers and + * the covering of the whole communication path, including all elements + * that may change the network-level PDUs, such as routers, switches, + * and proxies.\n + * The following table lists cyclic integrity checksums that can be + * negotiated for the digests and MUST be implemented by every iSCSI + * initiator and target. These digest options only have error detection + * significance. + * Name | Description | Generator + * :----- | :---------- | :---------- + * CRC32C | 32-bit CRC | 0x11EDC6F41 + * None | no digest || + * + * The generator polynomial G(x) for this digest is given in hexadecimal + * notation (e.g. "0x3b" stands for 0011 1011, and the polynomial is + * x**5 + x**4 + x**3 + x + 1).\n + * When the initiator and target agree on a digest, this digest MUST be + * used for every PDU in the Full Feature Phase.\n + * Padding bytes, when present in a segment covered by a CRC, SHOULD be + * set to 0 and are included in the CRC.\n + * The CRC MUST be calculated by a method that produces the same results + * as the following process: + * - The PDU bits are considered as the coefficients of a polynomial + * M(x) of degree n - 1; bit 7 of the lowest numbered byte is + * considered the most significant bit (x**n - 1), followed by bit 6 + * of the lowest numbered byte through bit 0 of the highest numbered + * byte (x**0). + * - The most significant 32 bits are complemented. + * - The polynomial is multiplied by x**32, then divided by G(x). The + * generator polynomial produces a remainder R(x) of degree <= 31. + * - The coefficients of R(x) are formed into a 32-bit sequence. + * - The bit sequence is complemented, and the result is the CRC. + * - The CRC bits are mapped into the digest word. The x**31 + * coefficient is mapped to bit 7 of the lowest numbered byte of the + * digest, and the mapping continues with successive coefficients and + * bits so that the x**24 coefficient is mapped to bit 0 of the lowest + * numbered byte. The mapping continues further with the x**23 + * coefficient mapped to bit 7 of the next byte in the digest until + * the x**0 coefficient is mapped to bit 0 of the highest numbered + * byte of the digest. + * - Computing the CRC over any segment (data or header) extended to + * include the CRC built using the generator 0x11edc6f41 will always + * get the value 0x1c2d19ed as its final remainder (R(x)). This value + * is given here in its polynomial form (i.e., not mapped as the + * digest word). + * + * For a discussion about selection criteria for the CRC, see RFC3385.\n + * For a detailed analysis of the iSCSI polynomial, see Castagnoli93.\n + * Private or public extension algorithms MAY also be negotiated for + * digests. Whenever a private or public digest extension algorithm is + * part of the default offer (the offer made in the absence of explicit + * administrative action), the implementer MUST ensure that CRC32C is + * listed as an alternative in the default offer and "None" is not part + * of the default offer.\n + * Extension digest algorithms MUST be named using one of the following + * two formats: + * 1. Y-reversed.vendor.dns_name.do_something= + * 2. New public key with no name prefix constraints + * + * Digests named using the Y- format are used for private purposes + * (unregistered). New public keys must be registered with IANA using + * the IETF Review process (RFC5226). New public extensions for + * digests MUST NOT use the Y# name prefix.\n + * For private extension digests, to identify the vendor we suggest + * using the reversed DNS-name as a prefix to the proper digest names.\n + * The part of digest-name following Y- MUST conform to the format for + * standard-label specified.\n + * Support for public or private extension digests is OPTIONAL. + */ +#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_HEADER_DIGEST "HeaderDigest" + +/** + * @brief Login/Text Operational Session Text Key: Data digest. + * + * @verbatim + * Use: IO + * Senders: Initiator and target + * Scope: CO + * DataDigest = + * Default is None for DataDigest. + * @endverbatim + * Digests enable the checking of end-to-end, non-cryptographic data + * integrity beyond the integrity checks provided by the link layers and + * the covering of the whole communication path, including all elements + * that may change the network-level PDUs, such as routers, switches, + * and proxies.\n + * The following table lists cyclic integrity checksums that can be + * negotiated for the digests and MUST be implemented by every iSCSI + * initiator and target. These digest options only have error detection + * significance. + * Name | Description | Generator + * :----- | :---------- | :---------- + * CRC32C | 32-bit CRC | 0x11EDC6F41 + * None | no digest || + * + * The generator polynomial G(x) for this digest is given in hexadecimal + * notation (e.g. "0x3b" stands for 0011 1011, and the polynomial is + * x**5 + x**4 + x**3 + x + 1).\n + * When the initiator and target agree on a digest, this digest MUST be + * used for every PDU in the Full Feature Phase.\n + * Padding bytes, when present in a segment covered by a CRC, SHOULD be + * set to 0 and are included in the CRC.\n + * The CRC MUST be calculated by a method that produces the same results + * as the following process: + * - The PDU bits are considered as the coefficients of a polynomial + * M(x) of degree n - 1; bit 7 of the lowest numbered byte is + * considered the most significant bit (x**n - 1), followed by bit 6 + * of the lowest numbered byte through bit 0 of the highest numbered + * byte (x**0). + * - The most significant 32 bits are complemented. + * - The polynomial is multiplied by x**32, then divided by G(x). The + * generator polynomial produces a remainder R(x) of degree <= 31. + * - The coefficients of R(x) are formed into a 32-bit sequence. + * - The bit sequence is complemented, and the result is the CRC. + * - The CRC bits are mapped into the digest word. The x**31 + * coefficient is mapped to bit 7 of the lowest numbered byte of the + * digest, and the mapping continues with successive coefficients and + * bits so that the x**24 coefficient is mapped to bit 0 of the lowest + * numbered byte. The mapping continues further with the x**23 + * coefficient mapped to bit 7 of the next byte in the digest until + * the x**0 coefficient is mapped to bit 0 of the highest numbered + * byte of the digest. + * - Computing the CRC over any segment (data or header) extended to + * include the CRC built using the generator 0x11edc6f41 will always + * get the value 0x1c2d19ed as its final remainder (R(x)). This value + * is given here in its polynomial form (i.e., not mapped as the + * digest word). + * + * For a discussion about selection criteria for the CRC, see RFC3385.\n + * For a detailed analysis of the iSCSI polynomial, see Castagnoli93.\n + * Private or public extension algorithms MAY also be negotiated for + * digests. Whenever a private or public digest extension algorithm is + * part of the default offer (the offer made in the absence of explicit + * administrative action), the implementer MUST ensure that CRC32C is + * listed as an alternative in the default offer and "None" is not part + * of the default offer.\n + * Extension digest algorithms MUST be named using one of the following + * two formats: + * 1. Y-reversed.vendor.dns_name.do_something= + * 2. New public key with no name prefix constraints + * + * Digests named using the Y- format are used for private purposes + * (unregistered). New public keys must be registered with IANA using + * the IETF Review process (RFC5226). New public extensions for + * digests MUST NOT use the Y# name prefix.\n + * For private extension digests, to identify the vendor we suggest + * using the reversed DNS-name as a prefix to the proper digest names.\n + * The part of digest-name following Y- MUST conform to the format for + * standard-label specified.\n + * Support for public or private extension digests is OPTIONAL. + */ +#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST "DataDigest" + +/** + * @brief Login/Text Operational Session Text Key: New connections. + * + * @verbatim + * Use: LO + * Senders: Initiator and target + * Scope: SW + * Irrelevant when: SessionType=Discovery + * MaxConnections= + * Default is 1. + * @endverbatim + * Result function is Minimum.\n + * The initiator and target negotiate the maximum number of connections + * requested/acceptable. + */ +#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS "MaxConnections" + +/** + * @brief Login/Text Operational Session Text Key: Send targets. + * + * @verbatim + * Use: FFPO + * Senders: Initiator + * Scope: SW + * @endverbatim + * The text in this appendix is a normative part of this document.\n + * To reduce the amount of configuration required on an initiator, iSCSI + * provides the SendTargets Text Request. The initiator uses the + * SendTargets request to get a list of targets to which it may have + * access, as well as the list of addresses (IP address and TCP port) on + * which these targets may be accessed.\n + * To make use of SendTargets, an initiator must first establish one of + * two types of sessions. If the initiator establishes the session + * using the key "SessionType=Discovery", the session is a Discovery + * session, and a target name does not need to be specified. Otherwise, + * the session is a Normal operational session. The SendTargets command + * MUST only be sent during the Full Feature Phase of a Normal or + * Discovery session.\n + * A system that contains targets MUST support Discovery sessions on + * each of its iSCSI IP address-port pairs and MUST support the + * SendTargets command on the Discovery session. In a Discovery + * session, a target MUST return all path information (IP address-port + * pairs and Target Portal Group Tags) for the targets on the target + * Network Entity that the requesting initiator is authorized to access.\n + * A target MUST support the SendTargets command on operational + * sessions; these will only return path information about the target to + * which the session is connected and do not need to return information + * about other target names that may be defined in the responding + * system.\n + * An initiator MAY make use of the SendTargets command as it sees fit.\n + * A SendTargets command consists of a single Text Request PDU. This + * PDU contains exactly one text key and value. The text key MUST be + * SendTargets. The expected response depends upon the value, as well + * as whether the session is a Discovery session or an operational + * session.\n + * The value must be one of: + * @verbatim + * All + * The initiator is requesting that information on all relevant + * targets known to the implementation be returned. This value + * MUST be supported on a Discovery session and MUST NOT be + * supported on an operational session. + * + * If an iSCSI Target Name is specified, the session should + * respond with addresses for only the named target, if possible. + * This value MUST be supported on Discovery sessions. A + * Discovery session MUST be capable of returning addresses for + * those targets that would have been returned had value=All been + * designated. + * + * The session should only respond with addresses for the target + * to which the session is logged in. This MUST be supported on + * operational sessions and MUST NOT return targets other than the + * one to which the session is logged in. + * @endverbatim + * The response to this command is a Text Response that contains a list + * of zero or more targets and, optionally, their addresses. Each + * target is returned as a target record. A target record begins with + * the TargetName text key, followed by a list of TargetAddress text + * keys, and bounded by the end of the Text Response or the next + * TargetName key, which begins a new record. No text keys other than + * TargetName and TargetAddress are permitted within a SendTargets + * response.\n + * A Discovery session MAY respond to a SendTargets request with its + * complete list of targets, or with a list of targets that is based on + * the name of the initiator logged in to the session.\n + * A SendTargets response MUST NOT contain target names if there are no + * targets for the requesting initiator to access.\n + * Each target record returned includes zero or more TargetAddress + * fields.\n + * Each target record starts with one text key of the form: + * @verbatim + * TargetName= + * @endverbatim + * followed by zero or more address keys of the form: + * @verbatim + * TargetAddress=[:], + * + * @endverbatim + * The hostname-or-ipaddress contains a domain name, IPv4 address, or + * IPv6 address (RFC4291), as specified for the TargetAddress key.\n + * A hostname-or-ipaddress duplicated in TargetAddress responses for a + * given node (the port is absent or equal) would probably indicate that + * multiple address families are in use at once (IPv6 and IPv4).\n + * Each TargetAddress belongs to a portal group, identified by its + * numeric Target Portal Group Tag. The iSCSI Target Name, together with + * this tag, constitutes the SCSI port identifier; the tag only needs to + * be unique within a given target's name list of addresses.\n + * Multiple-connection sessions can span iSCSI addresses that belong to + * the same portal group.\n + * Multiple-connection sessions cannot span iSCSI addresses that belong + * to different portal groups.\n + * If a SendTargets response reports an iSCSI address for a target, it + * SHOULD also report all other addresses in its portal group in the + * same response.\n + * A SendTargets Text Response can be longer than a single Text Response + * PDU and makes use of the long Text Responses as specified.\n + * After obtaining a list of targets from the Discovery session, an + * iSCSI initiator may initiate new sessions to log in to the discovered + * targets for full operation. The initiator MAY keep the Discovery + * session open and MAY send subsequent SendTargets commands to discover + * new targets.\n + * Examples:\n + * This example is the SendTargets response from a single target that + * has no other interface ports.\n + * The initiator sends a Text Request that contains: + * @verbatim + * SendTargets=All + * @endverbatim + * The target sends a Text Response that contains: + * @verbatim + * TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.8675309 + * @endverbatim + * All the target had to return in this simple case was the target name.\n + * It is assumed by the initiator that the IP address and TCP port for + * this target are the same as those used on the current connection to + * the default iSCSI target.\n + * The next example has two internal iSCSI targets, each accessible via + * two different ports with different IP addresses. The following is + * the Text Response: + * @verbatim + * TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.8675309 + * TargetAddress=10.1.0.45:5300,1 + * TargetAddress=10.1.1.45:5300,2 + * TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.1234567 + * TargetAddress=10.1.0.45:5300,1 + * TargetAddress=10.1.1.45:5300,2 + * @endverbatim + * Both targets share both addresses; the multiple addresses are likely + * used to provide multi-path support. The initiator may connect to + * either target name on either address. Each of the addresses has its + * own Target Portal Group Tag; they do not support spanning multiple- + * connection sessions with each other. Keep in mind that the Target + * Portal Group Tags for the two named targets are independent of one + * another; portal group "1" on the first target is not necessarily the + * same as portal group "1" on the second target.\n + * In the above example, a DNS host name or an IPv6 address could have + * been returned instead of an IPv4 address.\n + * The next Text Response shows a target that supports spanning sessions + * across multiple addresses and further illustrates the use of the + * Target Portal Group Tags: + * @verbatim + * TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.8675309 + * TargetAddress=10.1.0.45:5300,1 + * TargetAddress=10.1.1.46:5300,1 + * TargetAddress=10.1.0.47:5300,2 + * TargetAddress=10.1.1.48:5300,2 + * TargetAddress=10.1.1.49:5300,3 + * @endverbatim + * In this example, any of the target addresses can be used to reach the + * same target. A single-connection session can be established to any + * of these TCP addresses. A multiple-connection session could span + * addresses .45 and .46 or .47 and .48 but cannot span any other + * combination. A TargetAddress with its own tag (.49) cannot be + * combined with any other address within the same session.\n + * This SendTargets response does not indicate whether .49 supports + * 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" + +/** + * @brief Login/Text Operational Session Text Key: Initial Ready To Transfer. + * + * @verbatim + * Use: LO + * Senders: Initiator and target + * Scope: SW + * Irrelevant when: SessionType=Discovery + * InitialR2T= + * Examples: + * I->InitialR2T=No + * T->InitialR2T=No + * Default is Yes. + * @endverbatim + * Result function is OR.\n + * The InitialR2T key is used to turn off the default use of R2T for + * unidirectional operations and the output part of bidirectional + * commands, thus allowing an initiator to start sending data to a + * target as if it has received an initial R2T with Buffer + * Offset=Immediate Data Length and Desired Data Transfer + * Length=(min(FirstBurstLength, Expected Data Transfer Length) - + * Received Immediate Data Length).\n + * The default action is that R2T is required, unless both the initiator + * and the target send this key-pair attribute specifying InitialR2T=No. + * 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" + +/** + * @brief Login/Text Operational Session Text Key: Immediate data. + * + * @verbatim + * Use: LO + * Senders: Initiator and target + * Scope: SW + * Irrelevant when: SessionType=Discovery + * ImmediateData= + * Default is Yes. + * @endverbatim + * Result function is AND.\n + * The initiator and target negotiate support for immediate dataTo + * 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 + * If ImmediateData is set to Yes and InitialR2T is set to Yes + * (default), then only immediate data are accepted in the first burst. + * If ImmediateData is set to No and InitialR2T is set to Yes, then the + * initiator MUST NOT send unsolicited data and the target MUST reject + * unsolicited data with the corresponding response code.\n + * If ImmediateData is set to No and InitialR2T is set to No, then the + * initiator MUST NOT send unsolicited immediate data but MAY send one + * unsolicited burst of Data-OUT PDUs.\n + * If ImmediateData is set to Yes and InitialR2T is set to No, then the + * initiator MAY send unsolicited immediate data and/or one unsolicited + * burst of Data-OUT PDUs.\n + * The following table is a summary of unsolicited data options: + * InitialR2T | ImmediateData | Unsolicited Data-Out PDUs | ImmediateData + * :--------- | :------------ | :------------------------ | :------------ + * | No | No | Yes | No | + * | No | Yes | Yes | Yes | + * | Yes | No | No | No | + * | Yes | Yes | No | Yes | + */ +#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IMMEDIATE_DATA "ImmediateData" + +/** + * @brief Login/Text Operational Session Text Key: Maximum receive DataSegmentLength. + * + * @verbatim + * Use: ALL, Declarative + * Senders: Initiator and target + * Scope: CO + * MaxRecvDataSegmentLength= + * Default is 8192 bytes. + * @endverbatim + * The initiator or target declares the maximum data segment length in + * bytes it can receive in an iSCSI PDU.\n + * The transmitter (initiator or target) is required to send PDUs with a + * data segment that does not exceed MaxRecvDataSegmentLength of the + * receiver.\n + * A target receiver is additionally limited by MaxBurstLength for + * solicited data and FirstBurstLength for unsolicited dataAn + * initiator MUST NOT send solicited PDUs exceeding MaxBurstLength nor + * 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" + +/** + * @brief Login/Text Operational Session Text Key: Maximum burst length. + * + * @verbatim + * Use: LO + * Senders: Initiator and target + * Scope: SW + * Irrelevant when: SessionType=Discovery + * MaxBurstLength= + * Default is 262144 (256 KB). + * @endverbatim + * Result function is Minimum.\n + * The initiator and target negotiate the maximum SCSI data payload in + * bytes in a Data-In or a solicited Data-Out iSCSI sequence. A + * 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" + +/** + * @brief Login/Text Operational Session Text Key: First burst length. + * + * @verbatim + * Use: LO + * Senders: Initiator and target + * Scope: SW + * Irrelevant when: SessionType=Discovery + * Irrelevant when: ( InitialR2T=Yes and ImmediateData=No ) + * FirstBurstLength= + * Default is 65536 (64 KB). + * @endverbatim + * Result function is Minimum.\n + * The initiator and target negotiate the maximum amount in bytes of + * unsolicited data an iSCSI initiator may send to the target during the + * execution of a single SCSI command. This covers the immediate data + * (if any) and the sequence of unsolicited Data-Out PDUs (if any) that + * follow the command.\n + * FirstBurstLength MUST NOT exceed MaxBurstLength. + */ +#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_FIRST_BURST_LEN "FirstBurstLength" + +/** + * @brief Login/Text Operational Session Text Key: Default time to wait. + * + * @verbatim + * Use: LO + * Senders: Initiator and target + * Scope: SW + * DefaultTime2Wait= + * Default is 2. + * @endverbatim + * Result function is Maximum.\n + * The initiator and target negotiate the minimum time, in seconds, to + * wait before attempting an explicit/implicit logout or an active task + * reassignment after an unexpected connection termination or a + * connection reset.\n + * 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" + +/** + * @brief Login/Text Operational Session Text Key: Default time to retain. + * + * @verbatim + * Use: LO + * Senders: Initiator and target + * Scope: SW + * DefaultTime2Retain= + * Default is 20. + * @endverbatim + * Result function is Minimum.\n + * The initiator and target negotiate the maximum time, in seconds, + * after an initial wait (Time2Wait), before which an active task + * reassignment is still possible after an unexpected connection + * termination or a connection reset.\n + * This value is also the session state timeout if the connection in + * question is the last LOGGED_IN connection in the session.\n + * 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" + +/** + * @brief Login/Text Operational Session Text Key: Maximum outstanding Ready To Transfer. + * + * @verbatim + * Use: LO + * Senders: Initiator and target + * Scope: SW + * MaxOutstandingR2T= + * Irrelevant when: SessionType=Discovery + * Default is 1. + * @endverbatim + * Result function is Minimum.\n + * The initiator and target negotiate the maximum number of outstanding + * R2Ts per task, excluding any implied initial R2T that might be part + * of that task. An R2T is considered outstanding until the last data + * 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" + +/** + * @brief Login/Text Operational Session Text Key: Data Protocol Data Unit (PDU) in order. + * + * @verbatim + * Use: LO + * Senders: Initiator and target + * Scope: SW + * Irrelevant when: SessionType=Discovery + * DataPDUInOrder= + * Default is Yes. + * @endverbatim + * Result function is OR.\n + * "No" is used by iSCSI to indicate that the data PDUs within sequences + * can be in any order. "Yes" is used to indicate that data PDUs within + * sequences have to be at continuously increasing addresses and + * overlays are forbidden. + */ +#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_PDU_IN_ORDER "DataPDUInOrder" + +/** + * @brief Login/Text Operational Session Text Key: Data sequence in order. + * + * @verbatim + * Use: LO + * Senders: Initiator and target + * Scope: SW + * Irrelevant when: SessionType=Discovery + * DataSequenceInOrder= + * Default is Yes. + * @endverbatim + * Result function is OR.\n + * A data sequence is a sequence of Data-In or Data-Out PDUs that end + * with a Data-In or Data-Out PDU with the F bit set to 1. A Data-Out + * sequence is sent either unsolicited or in response to an R2T.\n + * Sequences cover an offset-range.\n + * If DataSequenceInOrder is set to No, data PDU sequences may be + * transferred in any order.\n + * If DataSequenceInOrder is set to Yes, data sequences MUST be + * transferred using continuously non-decreasing sequence offsets (R2T + * buffer offset for writes, or the smallest SCSI Data-In buffer offset + * within a read data sequence).\n + * If DataSequenceInOrder is set to Yes, a target may retry at most the + * last R2T, and an initiator may at most request retransmission for the + * last read data sequence. For this reason, if ErrorRecoveryLevel is + * 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" + +/** + * @brief Login/Text Operational Session Text Key: Error recovery level. + * + * @verbatim + * Use: LO + * Senders: Initiator and target + * Scope: SW + * ErrorRecoveryLevel= + * Default is 0. + * @endverbatim + * Result function is Minimum.\n + * The initiator and target negotiate the recovery level supported. + * Recovery levels represent a combination of recovery capabilities. + * Each recovery level includes all the capabilities of the lower + * recovery levels and adds some new ones to them.\n + * In the description of recovery mechanisms, certain recovery classes + * are specified. + */ +#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_ERR_RECOVERY_LEVEL "ErrorRecoveryLevel" + +/** + * @brief Login/Text Operational Session Text Key: X reversed vendor. + * + * @verbatim + * Use: ALL + * Senders: Initiator and target + * Scope: specific key dependent + * X-reversed.vendor.dns_name.do_something= + * @endverbatim + * Keys with this format are used for private extension purposes. These + * keys always start with X- if unregistered with IANA (private). New + * public keys (if registered with IANA via an IETF Review RFC5226) no + * longer have an X# name prefix requirement; implementers may propose + * any intuitive unique name.\n + * For unregistered keys, to identify the vendor we suggest using the + * reversed DNS-name as a prefix to the key-proper.\n + * The part of key-name following X- MUST conform to the format for + * key-name.\n + * 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" + +/** + * @brief Login/Text Operational Session Text Key: Task reporting. + * + * @verbatim + * Use: LO + * Senders: Initiator and target + * Scope: SW + * Irrelevant when: SessionType=Discovery + * TaskReporting= + * Default is RFC3720. + * @endverbatim + * This key is used to negotiate the task completion reporting semantics + * from the SCSI target. The following table describes the semantics + * that an iSCSI target MUST support for respective negotiated key + * values. Whenever this key is negotiated, at least the RFC3720 and + * ResponseFence values MUST be offered as options by the negotiation + * originator. + * Name | Description + * :-------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------ + * | RFC3720 | RFC 3720-compliant semantics. Response fencing is not guaranteed, and fast completion of multi-task aborting is not supported. + * | ResponseFence | Response Fence semantics MUST be supported in reporting task completions. + * | FastAbort | Updated fast multi-task abort semantics defined in MUST be supported. Support for the Response. Fence is implied - i.e., semantics MUST be supported as well. + * + * 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" + +/** + * @brief Login/Text Operational Session Text Key: X Node architecture. + * + * @verbatim + * Use: LO, Declarative + * Senders: Initiator and target + * Scope: SW + * X#NodeArchitecture= + * Default is None. + * Examples: + * X#NodeArchitecture=ExampleOS/v1234,ExampleInc_SW_Initiator/1.05a + * X#NodeArchitecture=ExampleInc_HW_Initiator/4010,Firmware/2.0.0.5 + * X#NodeArchitecture=ExampleInc_SW_Initiator/2.1,CPU_Arch/i686 + * @endverbatim + * This document does not define the structure or content of the list of + * values.\n + * The initiator or target declares the details of its iSCSI node + * architecture to the remote endpoint. These details may include, but + * are not limited to, iSCSI vendor software, firmware, or hardware + * versions; the OS version; or hardware architecture. This key may be + * declared on a Discovery session or a Normal session.\n + * The length of the key value (total length of the list-of-values) MUST + * NOT be greater than 255 bytes.\n + * X#NodeArchitecture MUST NOT be redeclared during the Login Phase.\n + * Functional behavior of the iSCSI node (this includes the iSCSI + * protocol logic - the SCSI, iSCSI, and TCP/IP protocols) MUST NOT + * depend on the presence, absence, or content of the X#NodeArchitecture + * key. The key MUST NOT be used by iSCSI nodes for interoperability or + * for exclusion of other nodes. To ensure proper use, key values + * SHOULD be set by the node itself, and there SHOULD NOT be provisions + * for the key values to contain user-defined text.\n + * Nodes implementing this key MUST choose one of the following + * implementation options:\n + * - only transmit the key, + * - only log the key values received from other nodes, or + * - both transmit and log the key values. + * + * Each node choosing to implement transmission of the key values MUST + * be prepared to handle the response of iSCSI nodes that do not + * understand the key.\n + * Nodes that implement transmission and/or logging of the key values + * may also implement administrative mechanisms that disable and/or + * change the logging and key transmission details.\n + * Thus, a valid behavior for this key may be that a node is completely + * 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" + +/** + * @brief Login/Text Operational Session Text Key: IFMarker (obseleted). + * + * This document obsoletes the following keys defined in RFC3720:\n + * IFMarker, OFMarker, OFMarkInt, and IFMarkInt. However, iSCSI + * mplementations compliant to this document may still receive these + * obsoleted keys - i.e., in a responder role - in a text negotiation.\n + * When an IFMarker or OFMarker key is received, a compliant iSCSI + * implementation SHOULD respond with the constant "Reject" value. The + * implementation MAY alternatively respond with a "No" value.\n + * However, the implementation MUST NOT respond with a "NotUnderstood" + * value for either of these keys.\n + * When an IFMarkInt or OFMarkInt key is received, a compliant iSCSI + * implementation MUST respond with the constant "Reject" value. The + * implementation MUST NOT respond with a "NotUnderstood" value for + * either of these keys. + */ +#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IF_MARKER "IFMarker" + +/** + * @brief Login/Text Operational Session Text Key: OFMarker (obseleted). + * + * This document obsoletes the following keys defined in RFC3720:\n + * IFMarker, OFMarker, OFMarkInt, and IFMarkInt. However, iSCSI + * mplementations compliant to this document may still receive these + * obsoleted keys - i.e., in a responder role - in a text negotiation.\n + * When an IFMarker or OFMarker key is received, a compliant iSCSI + * implementation SHOULD respond with the constant "Reject" value. The + * implementation MAY alternatively respond with a "No" value.\n + * However, the implementation MUST NOT respond with a "NotUnderstood" + * value for either of these keys.\n + * When an IFMarkInt or OFMarkInt key is received, a compliant iSCSI + * implementation MUST respond with the constant "Reject" value. The + * implementation MUST NOT respond with a "NotUnderstood" value for + * either of these keys. + */ +#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_OF_MARKER "OFMarker" + +/** + * @brief Login/Text Operational Session Text Key: OFMarkInt (obseleted). + * + * This document obsoletes the following keys defined in RFC3720:\n + * IFMarker, OFMarker, OFMarkInt, and IFMarkInt. However, iSCSI + * mplementations compliant to this document may still receive these + * obsoleted keys - i.e., in a responder role - in a text negotiation.\n + * When an IFMarker or OFMarker key is received, a compliant iSCSI + * implementation SHOULD respond with the constant "Reject" value. The + * implementation MAY alternatively respond with a "No" value.\n + * However, the implementation MUST NOT respond with a "NotUnderstood" + * value for either of these keys.\n + * When an IFMarkInt or OFMarkInt key is received, a compliant iSCSI + * implementation MUST respond with the constant "Reject" value. The + * implementation MUST NOT respond with a "NotUnderstood" value for + * either of these keys. + */ +#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_OF_MARK_INT "OFMarkInt" + +/** + * @brief Login/Text Operational Session Text Key: IFMarkInt (obseleted). + * + * This document obsoletes the following keys defined in RFC3720:\n + * IFMarker, OFMarker, OFMarkInt, and IFMarkInt. However, iSCSI + * mplementations compliant to this document may still receive these + * obsoleted keys - i.e., in a responder role - in a text negotiation.\n + * When an IFMarker or OFMarker key is received, a compliant iSCSI + * implementation SHOULD respond with the constant "Reject" value. The + * implementation MAY alternatively respond with a "No" value.\n + * However, the implementation MUST NOT respond with a "NotUnderstood" + * value for either of these keys.\n + * When an IFMarkInt or OFMarkInt key is received, a compliant iSCSI + * implementation MUST respond with the constant "Reject" value. The + * implementation MUST NOT respond with a "NotUnderstood" value for + * either of these keys. + */ +#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IF_MARK_INT "IFMarkInt" + + +/// Login request flags: SecurityNegotiation. +#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION 0x0 + +/// Login request flags: LoginOperationalNegotiation. +#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 + +/// Login request flags: FullFeaturePhase. +#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_FULL_FEATURE_PHASE 0x3 + +/** + * @brief Login request flags: Next Stage (NSG): First bit of the two bits. + * + * The Login negotiation requests and responses are associated + * with a specific stage in the session (SecurityNegotiation,\n + * LoginOperationalNegotiation, FullFeaturePhase) and may indicate the + * 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 (1 << 0) + +/// Login request flags: Extracts the Next Stage (NSG) bits. +#define ISCSI_LOGIN_REQS_FLAGS_GET_NEXT_STAGE(x) ((x) & 3) + + +/// Login request flags: SecurityNegotiation. +#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_SECURITY_NEGOTIATION 0x0 + +/// Login request flags: LoginOperationalNegotiation. +#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 + +/// Login request flags: FullFeaturePhase. +#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_FULL_FEATURE_PHASE 0x3 + +/** + * @brief Login request flags: Current Stage (CSG): First 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 (1 << 2) + +/// Login request flags: Extracts the Current Stage (CSG) bits. +#define ISCSI_LOGIN_REQS_FLAGS_GET_CURRENT_STAGE(x) (((x) >> 2) & 3) + + +/** + * @brief Login request flags: Continue. + * + * (C) When set to 1, this bit indicates that the text (set of key=value + * pairs) in this Login Request is not complete (it will be continued on + * subsequent Login Requests); otherwise, it indicates that this Login + * Request ends a set of key=value pairs. A Login Request with the + * C bit set to 1 MUST have the T bit set to 0. + */ +#define ISCSI_LOGIN_REQ_FLAGS_CONTINUE (1 << 6) + +/** + * @brief Login request flags: Transmit. + * + * (T) When set to 1, this bit indicates that the initiator is ready to + * transit to the next stage.\n + * If the T bit is set to 1 and the NSG is set to FullFeaturePhase, then + * this also indicates that the initiator is ready for the Login + * Final-Response. + */ +#define ISCSI_LOGIN_REQ_FLAGS_TRANSMIT (1 << 7) + + +/** + * @brief iSCSI Login Request packet data. + * + * After establishing a TCP connection between an initiator and a + * target, the initiator MUST start a Login Phase to gain further access + * to the target's resources. + * + * The Login Phase consists of a sequence of Login Requests and Login + * Responses that carry the same Initiator Task Tag. + * + * Login Requests are always considered as immediate. + */ +typedef struct __attribute__((packed)) iscsi_login_req_packet { + /// Always 0x03 according to iSCSI specification. + uint8_t opcode; + + /// Login request flags. + int8_t flags; + + /** + * @brief Version-max indicates the maximum version number supported. + * + * All Login Requests within the Login Phase MUST carry the same + * Version-max. Currently, this is always 0.\n + * The target MUST use the value presented with the first Login Request. + */ + uint8_t version_max; + + /** + * @brief Version-min indicates the minimum version number supported. + * + * All Login Requests within the Login Phase MUST carry the same + * Version-min. The target MUST use the value presented with the first + * Login Request. Always 0 for now. + */ + uint8_t version_min; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// Initiator Session ID (ISID). + iscsi_isid isid; + + /** + * @brief Target Session Identifying Handle (TSIH). + * + * The TSIH must be set in the first Login Request. The reserved value + * 0 MUST be used on the first connection for a new session. Otherwise, + * the TSIH sent by the target at the conclusion of the successful login + * of the first connection for this session MUST be used. The TSIH + * identifies to the target the associated existing session for this new + * connection.\n + * All Login Requests within a Login Phase MUST carry the same TSIH. + * The target MUST check the value presented with the first Login + * Request. + */ + uint16_t tsih; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /** + * @brief Connection ID (CID). + * + * The CID provides a unique ID for this connection within the session.\n + * All Login Requests within the Login Phase MUST carry the same CID. + * The target MUST use the value presented with the first Login Request.\n + * A Login Request with a non-zero TSIH and a CID equal to that of an + * existing connection implies a logout of the connection followed by a + * login. + */ + uint16_t cid; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /** + * @brief CmdSN. + * + * The CmdSN is either the initial command sequence number of a session + * (for the first Login Request of a session - the "leading" login) or + * the command sequence number in the command stream if the login is for + * a new connection in an existing session.\n + * Examples: + * - Login on a leading connection: If the leading login carries the + * CmdSN 123, all other Login Requests in the same Login Phase carry + * the CmdSN 123, and the first non-immediate command in the Full + * Feature Phase also carries the CmdSN 123. + * - Login on other than a leading connection: If the current CmdSN at + * the time the first login on the connection is issued is 500, then + * that PDU carries CmdSN=500. Subsequent Login Requests that are + * needed to complete this Login Phase may carry a CmdSN higher than + * 500 if non-immediate requests that were issued on other connections + * in the same session advance the CmdSN. + * + * If the Login Request is a leading Login Request, the target MUST use + * the value presented in the CmdSN as the target value for the + * ExpCmdSN. + */ + uint32_t cmd_sn; + + /** + * @brief ExpStatSN. + * + * For the first Login Request on a connection, this is the ExpStatSN + * for the old connection, and this field is only valid if the Login + * Request restarts a connection.\n + * For subsequent Login Requests, it is used to acknowledge the Login + * Responses with their increasing StatSN values. + */ + uint32_t exp_stat_sn; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2[2]; + + /** + * @brief Data segment - Login Parameters in Text Request Format. + * + * The initiator MUST provide some basic parameters in order + * 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_login_req_packet; + + +/// Login response flags: SecurityNegotiation. +#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION 0x0 + +/// Login response flags: LoginOperationalNegotiation. +#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 + +/// Login response flags: FullFeaturePhase. +#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FULL_FEATURE_PHASE 0x3 + +/** + * @brief Login response flags: Next Stage (NSG): First bit of the two bits. + * + * The Login negotiation requests and responses are associated + * with a specific stage in the session (SecurityNegotiation, + * LoginOperationalNegotiation, FullFeaturePhase) and may indicate the + * 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 (1 << 0) + +/// Login response flags: Extracts the Next Stage (NSG) bits. +#define ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(x) ((x) & 3) + +/// Login response flags: SecurityNegotiation. +#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_SECURITY_NEGOTIATION 0x0 + +/// Login response flags: LoginOperationalNegotiation. +#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 + +/// Login response flags: FullFeaturePhase. +#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FULL_FEATURE_PHASE 0x3 + +/** + * @brief Login response flags: Current Stage (CSG): First 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_RESPONSE_FLAGS_CURRENT_STAGE (1 << 2) + +/// Login response flags: Extracts the Current Stage (CSG) bits. +#define ISCSI_LOGIN_RESPONSE_FLAGS_GET_CURRENT_STAGE(x) (((x) >> 2) & 3) + +/** + * @brief Login response flags: Continue. + * + * (C) When set to 1, this bit indicates that the text (set of key=value + * pairs) in this Login Response is not complete (it will be continued + * on subsequent Login Responses); otherwise, it indicates that this + * Login Response ends a set of key=value pairs. A Login Response with + * the C bit set to 1 MUST have the T bit set to 0. + */ +#define ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE (1 << 6) + +/** + * @brief Login response flags: Transmit. + * + * (T) The T bit is set to 1 as an indicator of the end of the stage. If + * the T bit is set to 1 and the NSG is set to FullFeaturePhase, then + * this is also the Login Final-Response. A T bit of 0 indicates a + * "partial" response, which means "more negotiation needed".\n + * A Login Response with the T bit set to 1 MUST NOT contain key=value + * pairs that may require additional answers from the initiator within + * the same stage.\n + * If the Status-Class is 0, the T bit MUST NOT be set to 1 if the T bit + * in the request was set to 0. + */ +#define ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT (1 << 7) + + +/** + * @brief Login response status class: Success. + * + * Indicates that the iSCSI target successfully received, understood, + * and accepted the request. The numbering fields (StatSN, ExpCmdSN, + * MaxCmdSN) are only valid if Status-Class is 0. + */ +#define ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SUCCESS 0x00 + +/** + * @brief Login response status details: Success. + * + * Login is proceeding OK. If the response T bit is set to 1 in both the + * request and the matching response, and the NSG is set to + * FullFeaturePhase in both the request and the matching response, the + * Login Phase is finished, and the initiator may proceed to issue SCSI + * commands. + */ +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SUCCESS 0x00 + + +/** + * @brief Login response status class: Redirection. + * + * Indicates that the initiator must take further action + * to complete the request. This is usually due to the + * target moving to a different address. All of the redirection + * Status-Class responses MUST return one or more text key + * parameters of the type "TargetAddress", which indicates the + * target's new address. A redirection response MAY be issued by + * a target prior to or after completing a security negotiation if + * a security negotiation is required. A redirection SHOULD be + * accepted by an initiator, even without having the target + * complete a security negotiation if any security negotiation is + * required, and MUST be accepted by the initiator after the + * completion of the security negotiation if any security + * negotiation is required. + */ +#define ISCSI_LOGIN_RESPONSE_STATUS_CLASS_REDIRECT 0x01 + +/** + * @brief Login response status details: Temporarily redirected. + * + * The requested iSCSI Target Name (ITN) has temporarily moved + * to the address provided. + */ +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_REDIRECT_TEMP 0x01 + +/** + * @brief Login response status details: Permanently redirected. + * + * The requested ITN has permanently moved to the address provided. + */ +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_REDIRECT_PERM 0x02 + + +/** + * @brief Login response status class: Initiator Error (not a format error). + * + * Indicates that the initiator most likely caused the error.\n + * This MAY be due to a request for a resource for which the + * initiator does not have permission. The request should + * not be tried again. + */ +#define ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR 0x02 + +/// Login response status details: Miscellaneous iSCSI initiator errors. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC 0x00 + +/// Login response status details: The initiator could not be successfully authenticated or target authentication is not supported. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_ERR 0x01 + +/// Login response status details: The initiator is not allowed access to the given target. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_FAIL 0x02 + +/// Login response status details: The requested iSCSI Target Name (ITN) does not exist at this address. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_NOT_FOUND 0x03 + +/// Login response status details: The requested ITN has been removed, and no forwarding address is provided. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_TARGET_REMOVED 0x04 + +/// Login response status details: The requested iSCSI version range is not supported by the target. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_WRONG_VERSION 0x05 + +/// Login response status details: Too many connections on this Session ID (SSID). +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_TOO_MANY_CONNECTIONS 0x06 + +/// Login response status details: Missing parameters (e.g. iSCSI Initiator Name and/or Target Name). +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISSING_PARAMETER 0x07 + +/// Login response status details: Target does not support session spanning to this connection (address). +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_NO_SESSION_SPANNING 0x08 + +/// Login response status details: Target does not support this type of session or not from this initiator. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_SESSION_NO_SUPPORT 0x09 + +/// Login response status details: Attempt to add a connection to a non-existent session. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_SESSION_NO_EXIST 0x0A + +/// Login response status details: Invalid request type during login. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_INVALID_LOGIN_REQ_TYPE 0x0B + + +/** + * @brief Login response status class: Target Error. + * + * Indicates that the target sees no errors in the + * initiator's Login Request but is currently incapable of + * fulfilling the request. The initiator may retry the same Login + * Request later. + */ +#define ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SERVER_ERR 0x03 + +/// Login response status details: Target hardware or software error. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_TARGET_ERROR 0x00 + +/// Login response status details: The iSCSI service or target is not currently operational. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_SERVICE_UNAVAILABLE 0x01 + +/// The target has insufficient session, connection, or other resources. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_OUT_OF_RESOURCES 0x02 + + +/** + * @brief iSCSI Login Response packet data. + * + * The Login Response indicates the progress and/or end of the Login + * Phase. + */ +typedef struct __attribute__((packed)) iscsi_login_response_packet { + /// Always 0x23 according to iSCSI specification. + uint8_t opcode; + + /// Login response flags. + int8_t flags; + + /** + * @brief This is the highest version number supported by the target. + * + * All Login Responses within the Login Phase MUST carry the same + * Version-max. + */ + uint8_t version_max; + + /** + * @brief Version-active indicates the highest version supported by the target and initiator. + * + * If the target does not support a version within the + * range specified by the initiator, the target rejects the login and + * this field indicates the lowest version supported by the target. + * All Login Responses within the Login Phase MUST carry the same + * Version-active.\n + * The initiator MUST use the value presented as a response to the first + * Login Request. + */ + uint8_t version_active; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// Initiator Session ID (ISID). + iscsi_isid isid; + + /** + * @brief Target Session Identifying Handle (TSIH). + * + * The TSIH is the target-assigned session-identifying handle. Its + * internal format and content are not defined by this protocol, except + * for the value 0, which is reserved. With the exception of the Login + * Final-Response in a new session, this field should be set to the TSIH + * provided by the initiator in the Login Request. For a new session, + * the target MUST generate a non-zero TSIH and ONLY return it in the + * Login Final-Response. + */ + uint16_t tsih; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved; + + /** + * @brief StatSN. + * + * For the first Login Response (the response to the first Login + * Request), this is the starting status sequence number for the + * connection. The next response of any kind - including the next + * Login Response, if any, in the same Login Phase - will carry this + * number + 1. This field is only valid if the Status-Class is 0. + */ + uint32_t stat_sn; + + /// ExpCmdSN. + uint32_t exp_cmd_sn; + + /// MaxCmdSN. + uint32_t max_cmd_sn; + + /** + * @brief Status-class. + * + * Status-class (see above for details). If the Status-Class is + * not 0, the initiator and target MUST close the TCP connection + * If the target wishes to reject the Login Request for more than one + * reason, it should return the primary reason for the rejection. + */ + uint8_t status_class; + + /// Status-detail. + uint8_t status_detail; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved2; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved3; + + /** + * @brief Data segment - Login Parameters in Text Request Format. + * + * The target MUST provide some basic parameters in order to enable the + * initiator to determine if it is connected to the correct port and the + * initial text parameters for the security exchange.\n + * All the rules specified for Text Responses also hold for Login + * Responses. + */ + iscsi_ds_cmd_data ds_cmd_data; +} iscsi_login_response_packet; + + +/// Logout request reason code: Close the session. All commands associated with the session (if any) are terminated. +#define ISCSI_LOGOUT_REQ_REASON_CODE_CLOSE_SESSION 0x00 + +/// Logout request reason code: Close the connection. All commands associated with the connection (if any) are terminated. +#define ISCSI_LOGOUT_REQ_REASON_CODE_CLOSE_CONNECTION 0x01 + +/// Logout request reason code: Remove the connection for recovery. The connection is closed, and all commands associated with it, if any, are to be prepared for a new allegiance. +#define ISCSI_LOGOUT_REQ_REASON_CODE_REMOVE_CONNECTION_RECOVERY 0x02 + + +/** + * @brief Logout request implicit reason code: Session reinstatement. + * + * The entire logout discussion in this section is also applicable for + * an implicit Logout realized by way of a connection reinstatement or + * session reinstatement. When a Login Request performs an implicit + * Logout, the implicit Logout is performed as if having the reason + * codes specified below: + */ +#define ISCSI_LOGOUT_REQ_REASON_CODE_IMPLICIT_SESSION_REINSTATEMENT 0x00 + +/** + * @brief Logout request implicit reason code: Connection reinstatement when the operational ErrorRecoveryLevel < 2. + * + * The entire logout discussion in this section is also applicable for + * an implicit Logout realized by way of a connection reinstatement or + * session reinstatement. When a Login Request performs an implicit + * Logout, the implicit Logout is performed as if having the reason + * codes specified below: + */ +#define ISCSI_LOGOUT_REQ_REASON_CODE_IMPLICIT_CONNECTION_REINSTATEMENT 0x01 + +/** + * @brief Logout request implicit reason code: Connection reinstatement when the operational ErrorRecoveryLevel = 2. + * + * The entire logout discussion in this section is also applicable for + * an implicit Logout realized by way of a connection reinstatement or + * session reinstatement. When a Login Request performs an implicit + * Logout, the implicit Logout is performed as if having the reason + * codes specified below: + */ +#define ISCSI_LOGOUT_REQ_REASON_CODE_IMPLICIT_CONNECTION_REINSTATEMENT_2 0x02 + + +/** + * @brief iSCSI Logout Request packet data. + * + * The Logout Request is used to perform a controlled closing of a + * connection. + * + * An initiator MAY use a Logout Request to remove a connection from a + * session or to close an entire session. + * + * After sending the Logout Request PDU, an initiator MUST NOT send any + * new iSCSI requests on the closing connection. If the Logout Request + * is intended to close the session, new iSCSI requests MUST NOT be sent + * on any of the connections participating in the session. + * + * When receiving a Logout Request with the reason code "close the + * connection" or "close the session", the target MUST terminate all + * pending commands, whether acknowledged via the ExpCmdSN or not, on + * that connection or session, respectively. + * + * When receiving a Logout Request with the reason code "remove the + * connection for recovery", the target MUST discard all requests not + * yet acknowledged via the ExpCmdSN that were issued on the specified + * connection and suspend all data/status/R2T transfers on behalf of + * pending commands on the specified connection. + * + * The target then issues the Logout Response and half-closes the TCP + * connection (sends FIN). After receiving the Logout Response and + * attempting to receive the FIN (if still possible), the initiator MUST + * completely close the logging-out connection. For the terminated + * commands, no additional responses should be expected. + * + * A Logout for a CID may be performed on a different transport + * connection when the TCP connection for the CID has already been + * terminated. In such a case, only a logical "closing" of the iSCSI + * connection for the CID is implied with a Logout. + * + * All commands that were not terminated or not completed (with status) + * and acknowledged when the connection is closed completely can be + * reassigned to a new connection if the target supports connection + * recovery. + * + * If an initiator intends to start recovery for a failing connection, + * it MUST use the Logout Request to "clean up" the target end of a + * failing connection and enable recovery to start, or use the Login + * Request with a non-zero TSIH and the same CID on a new connection for + * the same effect. In sessions with a single connection, the + * connection can be closed and then a new connection reopened. A + * connection reinstatement login can be used for recovery. + * + * A successful completion of a Logout Request with the reason code + * "close the connection" or "remove the connection for recovery" + * results at the target in the discarding of unacknowledged commands + * received on the connection being logged out. These are commands that + * have arrived on the connection being logged out but that have not + * been delivered to SCSI because one or more commands with a smaller + * CmdSN have not been received by iSCSI. The resulting holes in the + * command sequence numbers will have to be handled by appropriate + * recovery, unless the session is also closed. + */ +typedef struct __attribute__((packed)) iscsi_logout_req_packet { + /// Always 6 according to iSCSI specification. + uint8_t opcode; + + /** + * @brief Reason code. + * + * A target implicitly terminates the active tasks due to the iSCSI + * protocol in the following cases: + * -# When a connection is implicitly or explicitly logged out with + * the reason code "close the connection" and there are active + * tasks allegiant to that connection. + * -# When a connection fails and eventually the connection state + * times out and there are active tasks allegiant to that + * connection + * -# When a successful recovery Logout is performed while there are + * active tasks allegiant to that connection and those tasks + * eventually time out after the Time2Wait and Time2Retain periods + * without allegiance reassignment + * -# When a connection is implicitly or explicitly logged out with + * the reason code "close the session" and there are active tasks + * in that session + * + * If the tasks terminated in any of the above cases are SCSI tasks, + * they must be internally terminated as if with CHECK CONDITION status. + * This status is only meaningful for appropriately handling the + * internal SCSI state and SCSI side effects with respect to ordering, + * because this status is never communicated back as a terminating + * status to the initiator. However, additional actions may have to be + * taken at the SCSI level, depending on the SCSI context as defined by + * the SCSI standards (e.g., queued commands and ACA; UA for the next + * command on the I_T nexus in cases a), b), and c) above). After the + * tasks are terminated, the target MUST report a Unit Attention condition + * on the next command processed on any connection for each affected + * I_T_L nexus with the status of CHECK CONDITION, the ASC/ASCQ value + * of 0x47 / 0x7F ("SOME COMMANDS CLEARED BY ISCSI PROTOCOL EVENT"), etc. + */ + int8_t reason_code; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength (MUST be 0 for this PDU). + uint8_t total_ahs_len; + + /// DataSegmentLength (MUST be 0 for this PDU). + uint8_t ds_len[3]; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /** + * @brief Connection ID (CID). + * + * This is the connection ID of the connection to be closed (including + * closing the TCP stream). This field is only valid if the reason code + * is not "close the session". + */ + uint16_t cid; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved3; + + /// CmdSN. + uint32_t cmd_sn; + + /// This is the last ExpStatSN value for the connection to be closed. + uint32_t exp_stat_sn; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved4[2]; + + /// Optional header digest. + iscsi_header_digest hdr_digest; +} iscsi_logout_req_packet; + + +/// Logout response - response code: Connection or session closed successfully. +#define ISCSI_LOGOUT_RESPONSE_CLOSED_SUCCESSFULLY 0x00 + +/// Logout response - response code: Connection ID (CID) not found. +#define ISCSI_LOGOUT_RESPONSE_CID_NOT_FOUND 0x01 + +/// Logout response - response code: Connection recovery is not supported (i.e., the Logout reason code was "remove the connection for recovery" and the target does not support it as indicated by the operational ErrorRecoveryLevel). +#define ISCSI_LOGOUT_RESPONSE_CONNECTION_RECOVERY_NOT_SUPPORTED 0x02 + +/// Logout response - response code: Cleanup failed for various reasons. +#define ISCSI_LOGOUT_RESPONSE_CLEANUP_FAILED 0x03 + +/** + * @brief iSCSI Logout Response packet data. + * + * The Logout Response is used by the target to indicate if the cleanup + * operation for the connection(s) has completed. + * + * After Logout, the TCP connection referred by the CID MUST be closed + * at both ends (or all connections must be closed if the logout reason + * was session close). + */ +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). + int8_t flags; + + /// Response. + uint8_t response; + + /// Reserved for future usage, always MUST be 0. + uint8_t reserved; + + /// TotalAHSLength (MUST be 0 for this PDU). + uint8_t total_ahs_len; + + /// DataSegmentLength (MUST be 0 for this PDU). + uint8_t ds_len[3]; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved3; + + /// StatSN. + uint32_t stat_sn; + + /// ExpCmdSN. + uint32_t exp_cmd_sn; + + /// MaxCmdSN. + uint32_t max_cmd_sn; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved4; + + /** + * @brief Time2Wait. + * + * If the Logout response code is 0 and the operational + * ErrorRecoveryLevel is 2, this is the minimum amount of time, in + * seconds, to wait before attempting task reassignment. If the Logout + * response code is 0 and the operational ErrorRecoveryLevel is less + * than 2, this field is to be ignored.\n + * This field is invalid if the Logout response code is 1.\n + * If the Logout response code is 2 or 3, this field specifies the + * minimum time to wait before attempting a new implicit or explicit + * logout.\n + * If Time2Wait is 0, the reassignment or a new Logout may be attempted + * immediately. + */ + uint16_t time_wait; + + /** + * @brief Time2Retain. + * + * If the Logout response code is 0 and the operational + * ErrorRecoveryLevel is 2, this is the maximum amount of time, in + * seconds, after the initial wait (Time2Wait) that the target waits for + * the allegiance reassignment for any active task, after which the task + * state is discarded. If the Logout response code is 0 and the + * operational ErrorRecoveryLevel is less than 2, this field is to be + * ignored.\n + * This field is invalid if the Logout response code is 1.\n + * If the Logout response code is 2 or 3, this field specifies the + * maximum amount of time, in seconds, after the initial wait + * (Time2Wait) that the target waits for a new implicit or explicit + * logout.\n + * If it is the last connection of a session, the whole session state is + * discarded after Time2Retain.\n + * If Time2Retain is 0, the target has already discarded the connection + * (and possibly the session) state along with the task states. No + * reassignment or Logout is required in this case. + */ + uint16_t time_retain; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved5; + + /// Optional header digest. + iscsi_header_digest hdr_digest; +} iscsi_logout_response_packet; + + +/// Selective Negative / Sequence Number Acknowledgment (SNACK) request: Data/R2T SNACK: requesting retransmission of one or more Data-In or R2T PDUs. +#define ISCSI_SNACK_REQ_TYPE_DATA_R2T_SNACK 0x00 + +/// Selective Negative / Sequence Number Acknowledgment (SNACK) request: +#define ISCSI_SNACK_REQ_TYPE_STATUS_SNACK 0x01 // Status SNACK: requesting retransmission of one or more + // numbered responses + +/** + * @brief Selective Negative / Sequence Number Acknowledgment (SNACK) request: DataACK: positively acknowledges Data-In PDUs. + * + * If an initiator operates at ErrorRecoveryLevel 1 or higher, it MUST + * issue a SNACK of type DataACK after receiving a Data-In PDU with the + * A bit set to 1. However, if the initiator has detected holes in the + * input sequence, it MUST postpone issuing the SNACK of type DataACK + * until the holes are filled. An initiator MAY ignore the A bit if it + * deems that the bit is being set aggressively by the target (i.e., + * before the MaxBurstLength limit is reached).\n + * The DataACK is used to free resources at the target and not to + * request or imply data retransmission.\n + * An initiator MUST NOT request retransmission for any data it had + * already acknowledged + */ +#define ISCSI_SNACK_REQ_TYPE_DATA_ACK 0x02 + +/** + * @brief Selective Negative / Sequence Number Acknowledgment (SNACK) request: R-Data SNACK: requesting retransmission of Data-In PDUs with possible resegmentation and status tagging. + * + * If the initiator MaxRecvDataSegmentLength changed between the + * original transmission and the time the initiator requests + * retransmission, the initiator MUST issue a R-Data SNACK.\n + * With R-Data SNACK, the initiator indicates that it discards all the + * unacknowledged data and expects the target to resend it. It also + * expects resegmentation. In this case, the retransmitted Data-In PDUs + * MAY be different from the ones originally sent in order to reflect + * changes in MaxRecvDataSegmentLength. Their DataSN starts with the + * BegRun of the last DataACK received by the target if any was received; + * otherwise, it starts with 0 and is increased by 1 for each resent + * Data-In PDU.\n + * A target that has received a R-Data SNACK MUST return a SCSI Response + * that contains a copy of the SNACK Tag field from the R-Data SNACK in + * the SCSI Response SNACK Tag field as its last or only Response. For + * example, if it has already sent a response containing another value + * in the SNACK Tag field or had the status included in the last Data-In + * PDU, it must send a new SCSI Response PDU. If a target sends more + * than one SCSI Response PDU due to this rule, all SCSI Response PDUs + * must carry the same StatSN. If an initiator attempts to recover a lost + * SCSI Response when more than one response has been sent, the + * target will send the SCSI Response with the latest content known to + * the target, including the last SNACK Tag for the command.\n + * For considerations in allegiance reassignment of a task to a + * connection with a different MaxRecvDataSegmentLength. + */ +#define ISCSI_SNACK_REQ_TYPE_R_DATA_SNACK 0x03 + + +/** + * @brief iSCSI SNACK Request packet data. + * + * If the implementation supports ErrorRecoveryLevel greater than zero, + * it MUST support all SNACK types. + * + * The SNACK is used by the initiator to request the retransmission of + * numbered responses, data, or R2T PDUs from the target. The SNACK + * Request indicates the numbered responses or data "runs" whose + * retransmission is requested, where the run starts with the first + * StatSN, DataSN, or R2TSN whose retransmission is requested and + * indicates the number of Status, Data, or R2T PDUs requested, + * including the first. 0 has special meaning when used as a starting + * number and length: + * + * - When used in RunLength, it means all PDUs starting with the + * initial. + * + * - When used in both BegRun and RunLength, it means all + * unacknowledged PDUs. + * + * The numbered response(s) or R2T(s) requested by a SNACK MUST be + * delivered as exact replicas of the ones that the target transmitted + * originally, except for the fields ExpCmdSN, MaxCmdSN, and ExpDataSN, + * which MUST carry the current values. R2T(s)requested by SNACK MUST + * also carry the current value of the StatSN. + * + * The numbered Data-In PDUs requested by a Data SNACK MUST be delivered + * as exact replicas of the ones that the target transmitted originally, + * except for the fields ExpCmdSN and MaxCmdSN, which MUST carry the + * current values; and except for resegmentation. + * + * Any SNACK that requests a numbered response, data, or R2T that was + * not sent by the target or was already acknowledged by the initiator + * MUST be rejected with a reason code of "Protocol Error". + */ +typedef struct __attribute__((packed)) iscsi_snack_req_packet { + /// Always 0x10 according to iSCSI specification. + uint8_t opcode; + + /** + * @brief Type. + * + * Data/R2T SNACK, Status SNACK, or R-Data SNACK for a command MUST + * precede status acknowledgment for the given command. + */ + int8_t type; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// LUN or Reserved. + uint64_t lun; + + /** + * @brief Initiator Task Tag (ITT). + * + * For a Status SNACK and DataACK, the Initiator Task Tag MUST be set to + * the reserved value 0xFFFFFFFF. In all other cases, the Initiator + * Task Tag field MUST be set to the Initiator Task Tag of the + * referenced command. + */ + uint32_t init_task_tag; + + /** + * @brief Target Transfer Tag (TTT). + * + * For a R-Data SNACK, this field MUST contain a value that is different + * from 0 or 0xFFFFFFFF and is unique for the task (identified by the + * Initiator Task Tag). This value MUST be copied by the iSCSI target + * in the last or only SCSI Response PDU it issues for the command.\n + * For DataACK, the Target Transfer Tag MUST contain a copy of the + * Target Transfer Tag and LUN provided with the SCSI Data-In PDU with + * the A bit set to 1.\n + * In all other cases, the Target Transfer Tag field MUST be set to the + * reserved value 0xFFFFFFFF. + */ + uint32_t target_xfer_snack_tag; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved2; + + /// ExpStatSN. + uint32_t exp_stat_sn; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved3; + + /** + * @brief BegRun. + * + * This field indicates the DataSN, R2TSN, or StatSN of the first PDU + * whose retransmission is requested (Data/R2T and Status SNACK), or the + * next expected DataSN (DataACK SNACK).\n + * A BegRun of 0, when used in conjunction with a RunLength of 0, means + * "resend all unacknowledged Data-In, R2T or Response PDUs". + * BegRun MUST be 0 for a R-Data SNACK. + */ + uint32_t beg_run; + + /** + * @brief RunLength. + * + * This field indicates the number of PDUs whose retransmission is + * requested.\n + * A RunLength of 0 signals that all Data-In, R2T, or Response PDUs + * carrying the numbers equal to or greater than BegRun have to be + * resent.\n + * The RunLength MUST also be 0 for a DataACK SNACK in addition to a + * R-Data SNACK. + */ + uint32_t run_len; + + /// Optional header digest. + iscsi_header_digest hdr_digest; +} iscsi_snack_req_packet; + + +/// iSCSI Reject packet data: Reserved, original PDU can't be resent. +#define ISCSI_REJECT_REASON_RESERVED 0x01 + +/** + * @brief iSCSI Reject packet data: Data (payload) digest error, original PDU can be resent. + * + * For iSCSI, Data-Out PDU retransmission is only done if the + * target requests retransmission with a recovery R2T. However, + * if this is the data digest error on immediate data, the + * initiator may choose to retransmit the whole PDU, including + * the immediate data. + */ +#define ISCSI_REJECT_REASON_DATA_DIGEST_ERR 0x02 + +/// iSCSI Reject reason packet data: SNACK Reject (original PDU can be resent). +#define ISCSI_REJECT_REASON_SNACK_REJECT 0x03 + +/// iSCSI Reject reason packet data: Protocol Error (e.g., SNACK Request for a status that was already acknowledged). Original PDU can't be resent. +#define ISCSI_REJECT_REASON_PROTOCOL_ERR 0x04 + +/// iSCSI Reject reason packet data: Command not supported (original PDU can't be resent). +#define ISCSI_REJECT_REASON_COMMAND_NOT_SUPPORTED 0x05 + +/// iSCSI Reject reason packet data: Immediate command reject - too many immediate commands (original PDU can be resent). +#define ISCSI_REJECT_REASON_TOO_MANY_IMMEDIATE_COMMANDS 0x06 + +/// iSCSI Reject reason packet data: Task in progress (original PDU can't be resent). +#define ISCSI_REJECT_REASON_TASK_IN_PROGRESS 0x07 + +/// iSCSI Reject reason packet data: Invalid data ack (original PDU can't be resent). +#define ISCSI_REJECT_REASON_INVALID_DATA_ACK 0x08 + +/** + * @brief iSCSI Reject reason packet data: Invalid PDU field, original PDU can't be resent. + * + * A target should use this reason code for all invalid values + * of PDU fields that are meant to describe a task, a response, + * or a data transfer. Some examples are invalid TTT/ITT, + * buffer offset, LUN qualifying a TTT, and an invalid sequence + * number in a SNACK. + */ +#define ISCSI_REJECT_REASON_INVALID_PDU_FIELD 0x09 + +/// iSCSI Reject reason packet data: Long op reject - Can't generate Target Transfer Tag - out of resources. Original PDU can be resent later. +#define ISCSI_REJECT_REASON_OUT_OF_RESOURCES 0x0A + +/** + * @brief iSCSI Reject reason packet data: Deprecated; MUST NOT be used. + * + * Reason code 0x0B is deprecated and MUST NOT be used by + * implementations. An implementation receiving reason code + * 0x0B MUST treat it as a negotiation failure that terminates + * the Login Phase and the TCP connection. + */ +#define ISCSI_REJECT_REASON_DEPRECATED 0x0B + +/// iSCSI Reject reason packet data: Waiting for Logout, original PDU can't be resent. +#define ISCSI_REJECT_REASON_WAITING_FOR_LOGOUT 0x0C + +/** + * @brief iSCSI Reject packet data. + * + * This structure will be received or sent, if an iSCSI + * packet was rejected or has been rejected for some reason. + */ +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). + int8_t flags; + + /** + * @brief Reject reason. + * + * In all the cases in which a pre-instantiated SCSI task is terminated + * because of the reject, the target MUST issue a proper SCSI command + * response with CHECK CONDITION. In these cases in which a status for + * the SCSI task was already sent before the reject, no additional + * status is required. If the error is detected while data from the + * initiator is still expected (i.e., the command PDU did not contain + * all the data and the target has not received a Data-Out PDU with the + * Final bit set to 1 for the unsolicited data, if any, and all + * outstanding R2Ts, if any), the target MUST wait until it receives + * the last expected Data-Out PDUs with the F bit set to 1 before + * sending the Response PDU. + */ + uint8_t reason; + + /// Reserved for future usage, always MUST be 0. + uint8_t reserved; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2; + + /// Always 0xFFFFFFFF for now. + uint32_t tag; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved3; + + /** + * @brief StatSN. + * + * This field carries its usual value and is not related to the + * rejected command. The StatSN is advanced after a Reject. + */ + uint32_t stat_sn; + + /** + * @brief ExpCmdSN. + * + * This field carries its usual value and is not related to the + * rejected command. + */ + uint32_t exp_cmd_sn; + + /** + * @brief MaxCmdSN. + * + * This field carries its usual value and is not related to the + * rejected command. + */ + uint32_t max_cmd_sn; + + /** + * @brief DataSN / Ready To Transfer Sequence Number (R2TSN) or Reserved. + * + * This field is only valid if the rejected PDU is a Data/R2T SNACK and + * the Reject reason code is "Protocol Error". The DataSN/R2TSN is the + * next Data/R2T sequence number that the target would send for the + * task, if any. + */ + uint32_t data_r2tsn_sn; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved4[2]; + + /// Optional header digest. + iscsi_header_digest hdr_digest; + + /** + * @brief Complete Header of Bad PDU. + * + * The target returns the header (not including the digest) of the + * PDU in error as the data of the response. + */ + iscsi_bhs_packet bad_pdu_hdr; + + /// Vendor-specific data (if any). + uint8_t vendor_data[0]; + + /// Optional data digest. + iscsi_data_digest data_digest; +} iscsi_reject_packet; + +/** + * @brief iSCSI NOP-Out packet data. + * + * NOP-Out may be used by an initiator as a "ping request" to verify + * that a connection/session is still active and all its components are + * operational. The NOP-In response is the "ping echo". + * + * A NOP-Out is also sent by an initiator in response to a NOP-In. + * + * A NOP-Out may also be used to confirm a changed ExpStatSN if another + * PDU will not be available for a long time. + * + * Upon receipt of a NOP-In with the Target Transfer Tag set to a valid + * value (not the reserved value 0xffffffff), the initiator MUST respond + * with a NOP-Out. In this case, the NOP-Out Target Transfer Tag MUST + * contain a copy of the NOP-In Target Transfer Tag. The initiator + * + * SHOULD NOT send a NOP-Out in response to any other received NOP-In, + * in order to avoid lengthy sequences of NOP-In and NOP-Out PDUs sent + * in response to each other. + */ +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). + int8_t flags; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// LUN or Reserved. + uint64_t lun; + + /** + * @brief Initiator Task Tag (ITT). + * + * The NOP-Out MUST have the Initiator Task Tag set to a valid value + * only if a response in the form of a NOP-In is requested (i.e., the + * NOP-Out is used as a ping request). Otherwise, the Initiator Task + * Tag MUST be set to 0xFFFFFFFF.\n + * When a target receives the NOP-Out with a valid Initiator Task Tag, + * it MUST respond with a NOP-In Response.\n + * If the Initiator Task Tag contains 0xFFFFFFFF, the I bit MUST be set + * to 1, and the CmdSN is not advanced after this PDU is sent. + */ + uint32_t init_task_tag; + + /** + * @brief Target Transfer Tag (TTT). + * + * The Target Transfer Tag is a target-assigned identifier for the + * operation.\n + * The NOP-Out MUST only have the Target Transfer Tag set if it is + * issued in response to a NOP-In with a valid Target Transfer Tag. In + * this case, it copies the Target Transfer Tag from the NOP-In PDU.\n + * Otherwise, the Target Transfer Tag MUST be set to 0xFFFFFFFF.\n + * When the Target Transfer Tag is set to a value other than 0xFFFFFFFF, + * the LUN field MUST also be copied from the NOP-In. + */ + uint32_t target_xfer_tag; + + /// CmdSN. + uint32_t cmd_sn; + + /// ExpStatSN. + uint32_t exp_stat_sn; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2[2]; + + /// Optional header digest. + iscsi_header_digest hdr_digest; + + /** + * @brief DataSegment - Ping Data (optional). + * + * Ping data is reflected in the NOP-In Response. The length of the + * reflected data is limited to MaxRecvDataSegmentLength. The length of + * 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; + + /// Optional data digest. + iscsi_data_digest data_digest; +} iscsi_nop_out_packet; + + +/** + * @brief iSCSI NOP-In packet data. + * + * NOP-In is sent by a target as either a response to a NOP-Out, a + * "ping" to an initiator, or a means to carry a changed ExpCmdSN and/or + * MaxCmdSN if another PDU will not be available for a long time (as + * determined by the target). + * + * When a target receives the NOP-Out with a valid Initiator Task Tag + * (not the reserved value 0xFFFFFFFF), it MUST respond with a NOP-In + * with the same Initiator Task Tag that was provided in the NOP-Out + * request. It MUST also duplicate up to the first + * MaxRecvDataSegmentLength bytes of the initiator-provided Ping Data. + * For such a response, the Target Transfer Tag MUST be 0xFFFFFFFF. + * + * The target SHOULD NOT send a NOP-In in response to any other received + * NOP-Out in order to avoid lengthy sequences of NOP-In and NOP-Out + * PDUs sent in response to each other. + * + * Otherwise, when a target sends a NOP-In that is not a response to a + * NOP-Out received from the initiator, the Initiator Task Tag MUST be + * set to 0xFFFFFFFF, and the data segment MUST NOT contain any data + * (DataSegmentLength MUST be 0). + */ +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). + int8_t flags; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// A LUN MUST be set to a correct value when the Target Transfer Tag is valid (not the reserved value 0xFFFFFFFF). + uint64_t lun; + + /// Initiator Task Tag (ITT) or 0xFFFFFFFF. + uint32_t init_task_tag; + + /** + * @brief Target Transfer Tag (TTT). + * + * If the target is responding to a NOP-Out, this field is set to the + * reserved value 0xFFFFFFFF.\n + * If the target is sending a NOP-In as a ping (intending to receive a + * corresponding NOP-Out), this field is set to a valid value (not the + * reserved value 0xFFFFFFFF).\n + * If the target is initiating a NOP-In without wanting to receive a + * corresponding NOP-Out, this field MUST hold the reserved value + * 0xFFFFFFFF. + */ + uint32_t target_xfer_tag; + + /** + * @brief StatSN. + * + * The StatSN field will always contain the next StatSN. However, when + * the Initiator Task Tag is set to 0xFFFFFFFF, the StatSN for the + * connection is not advanced after this PDU is sent. + */ + uint32_t stat_sn; + + /// ExpCmdSN. uint32_t exp_cmd_sn; // ExpCmdSN - uint32_t max_cmd_sn; // MaxCmdSN - uint32_t reserved2[3]; // Reserved for future usage - struct iscsi_header_digest hdr_digest; // Optional header digest - struct iscsi_ds_cmd_data ds_ping_data; // DataSegment - Return Ping Data - struct iscsi_data_digest data_digest; // Optional data digest + + /// MaxCmdSN. + uint32_t max_cmd_sn; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved2[3]; + + /// Optional header digest. + iscsi_header_digest hdr_digest; + + /// DataSegment - Return Ping Data. + iscsi_ds_cmd_data ds_ping_data; + + /// Optional data digest. + iscsi_data_digest data_digest; } iscsi_nop_in_packet; -#define ISCSI_VALIDATE_PACKET_RESULT_OK 0L // Validation successful -> iSCSI packet recognized and compliance to protocol specification -#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_NO_DATA -1L // Validation failed -> No packet data specified -#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_SIZE_TOO_SMALL -2L // Validation failed -> Packet size smaller than smallest possible iSCSI packet -#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_SIZE_MISMATCH -3L // Validation failed -> Packet size doesn't match calculated lengths from BHS -#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_UNSUPPORTED_VERSION -4L // Validation failed -> iSCSI protocol version not supported yet -#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS -5L // Validation failed -> Valid opcode but violates iSCSI protocol specification -#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_INVALID_OPCODE -6L // Validation failed -> Invalid opcode according to iSCSI protocol specification -#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_CRC32C_HDR_DIGEST -7L // Validation failed -> CRC32C check failed for header (BHS and/or AHS) -#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_CRC32C_DATA_DIGEST -8L // Validation failed -> CRC32C check failed for data segment + +/// 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 @@ -2868,94 +5558,119 @@ void iscsi_calc_header_digest(const iscsi_bhs_packet *packet_data); // Calculate 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 -static int iscsi_validate_text_key_value_pair(const uint8_t *packet_data, const uint32_t len); // Validates a single text key / value pair according to iSCSI specs -static int iscsi_validate_key_value_pairs(const uint8_t *packet_data, uint len); // Validates all text key / value pairs according to iSCSI specs 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 -#define ISCSI_TEXT_KEY_MAX_LEN 63UL // Maximum length of a key according to iSCSI specs -#define ISCSI_TEXT_VALUE_MAX_SIMPLE_LEN 255UL // Maximum length of value for a simple key type -#define ISCSI_TEXT_VALUE_MAX_LEN 8192UL // Maximum length of value for a normal key +/// Maximum length of a key according to iSCSI specifications. +#define ISCSI_TEXT_KEY_MAX_LEN 63UL -#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_INVALID -1L // Invalid -#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_UNSPECIFIED 0L // Unspecified type -#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST 1L // List -#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN 2L // Numerical minimum -#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MAX 3L // Numerical maximum -#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_DECLARATIVE 4L // Numerical declarative -#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE 5L // Declarative -#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_OR 6L // Boolean OR -#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_AND 7L // Boolean AND +/// Maximum length of value for a simple key type. +#define ISCSI_TEXT_VALUE_MAX_SIMPLE_LEN 255UL +/// Maximum length of value for a normal key. +#define ISCSI_TEXT_VALUE_MAX_LEN 8192UL + +/// iSCSI text key=value pair type: Invalid. +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_INVALID -1L + +/// iSCSI text key=value pair type: Unspecified type. +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_UNSPECIFIED 0L + +/// iSCSI text key=value pair type: List. +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST 1L + +/// iSCSI text key=value pair type: Numerical minimum. +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN 2L + +/// iSCSI text key=value pair type: Numerical maximum. +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MAX 3L + +/// iSCSI text key=value pair type: Numerical declarative. +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_DECLARATIVE 4L + +/// iSCSI text key=value pair type: Declarative. +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE 5L + +/// iSCSI text key=value pair type: Boolean OR. +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_OR 6L + +/// iSCSI text key=value pair type: Boolean AND. +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_AND 7L + + +/** + * @brief iSCSI Text / Login extracted key=value pair. + * + * This structure is used for accessing key and value + * pairs which have been extracted from either the + * Text or Login packet data. + */ typedef struct iscsi_key_value_pair { - int type; // Type of pair (see above) + /// Type of key and value pair. + int type; + + /// State index. int state_index; - uint8_t *value; // Value of key + + /// Value of the key which is stored in the hash map. + uint8_t *value; } iscsi_key_value_pair; +/** + * @brief iSCSI Text / Login key=value packet data construction helper. + * + * This structure is used to store the key=value plus NUL terminator + * pairs for sending as DataSegment packet data to the client. + */ typedef struct iscsi_key_value_pair_packet { - uint8_t *buf; // Current text buffer containing multiple zeroes - uint len; // Current length of buffer including final zero terminator + /// Current text buffer containing multiple key=value + NUL terminator pairs. + uint8_t *buf; + + /// Current length of buffer including final NUL terminator without iSCSI zero padding. + uint len; } iscsi_key_value_pair_packet; -static int iscsi_parse_text_key_value_pair(iscsi_hashmap *pairs, const uint8_t *packet_data, const uint32_t len); // Extracts a single text key / value pairs out of an iSCSI packet into a hash map -int iscsi_parse_key_value_pairs(iscsi_hashmap **pairs, const uint8_t *packet_data, uint len, int cbit, uint8_t **partial_pair); // Extracts all text key / value pairs out of an iSCSI packet into a hash map +int iscsi_parse_key_value_pairs(iscsi_hashmap *pairs, const uint8_t *packet_data, uint len, int cbit, uint8_t **partial_pair); // Extracts all text key / value pairs out of an iSCSI packet into a hash map int iscsi_create_key_value_pair_packet_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Creates a single partial iSCSI packet stream out of a single text key and value pair iscsi_key_value_pair_packet *iscsi_create_key_value_pairs_packet(const iscsi_hashmap *pairs); // Creates a properly aligned iSCSI packet DataSegment out of a hash map containing text key and value pairs +/** + * @brief iSCSI incoming connection. + * + * This structure is used for maintaining incoming iSCSI + * connections. Negiotiated text key=value pairs are + * stored here, status of the connection, session + * and iSCSI portals. + */ typedef struct iscsi_connection { - iscsi_hashmap *key_value_pairs; // Hash map containing text key / value pairs associated to this connection - int header_digest; // iSCSI connection contains a header digest (CRC32), must be 0 or 4 for now - int data_digest; // iSCSI connection contains a data digest (CRC32), must be 0 or 4 for now - uint8_t opcode; // Always 0x03 according to specification (see above) - int8_t flags; // Login request flags (see above) - uint8_t version_max; // Version-max indicates the maximum version number supported. - // All Login Requests within the Login Phase MUST carry the same - // Version-max. Currently, this is always 0 - // The target MUST use the value presented with the first Login Request. - uint8_t version_min; // All Login Requests within the Login Phase MUST carry the same - // Version-min. The target MUST use the value presented with the first - // Login Request. Always 0 for now - struct iscsi_isid isid; // ISID (see above for declaration) - uint16_t tsih; // The TSIH must be set in the first Login Request. The reserved value - // 0 MUST be used on the first connection for a new session. Otherwise, - // the TSIH sent by the target at the conclusion of the successful login - // of the first connection for this session MUST be used. The TSIH - // identifies to the target the associated existing session for this new - // connection. - // All Login Requests within a Login Phase MUST carry the same TSIH. - // The target MUST check the value presented with the first Login - // Request - uint32_t init_task_tag; // Initiator task tag - uint16_t cid; // Connection ID. The CID provides a unique ID for this connection within the session. - // All Login Requests within the Login Phase MUST carry the same CID. - // The target MUST use the value presented with the first Login Request. - // A Login Request with a non-zero TSIH and a CID equal to that of an - // existing connection implies a logout of the connection followed by a - // login - uint32_t cmd_sn; // The CmdSN is either the initial command sequence number of a session - // (for the first Login Request of a session - the "leading" login) or - // the command sequence number in the command stream if the login is for - // a new connection in an existing session. - // Examples: - // - Login on a leading connection: If the leading login carries the - // CmdSN 123, all other Login Requests in the same Login Phase carry - // the CmdSN 123, and the first non-immediate command in the Full - // Feature Phase also carries the CmdSN 123. - // - Login on other than a leading connection: If the current CmdSN at - // the time the first login on the connection is issued is 500, then - // that PDU carries CmdSN=500. Subsequent Login Requests that are - // needed to complete this Login Phase may carry a CmdSN higher than - // 500 if non-immediate requests that were issued on other connections - // in the same session advance the CmdSN. - // If the Login Request is a leading Login Request, the target MUST use - // the value presented in the CmdSN as the target value for the - // ExpCmdSN - uint32_t exp_stat_sn; // For the first Login Request on a connection, this is the ExpStatSN - // for the old connection, and this field is only valid if the Login - // Request restarts a connection - // For subsequent Login Requests, it is used to acknowledge the Login - // Responses with their increasing StatSN values + /// Hash map containing text key / value pairs associated to this connection. + iscsi_hashmap *key_value_pairs; + + /// 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; + + int8_t flags; + + /// Initiator Session ID (ISID). + iscsi_isid isid; + + /// Target Session Identifying Handle (TSIH). + uint16_t tsih; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /// Connection ID (CID). + uint16_t cid; + + /// CmdSN. + uint32_t cmd_sn; + + /// ExpStatSN. + uint32_t exp_stat_sn; } iscsi_connection; iscsi_connection *iscsi_connection_create(const iscsi_login_req_packet *login_req_pkt); // Creates data structure for an iSCSI connection request -- cgit v1.2.3-55-g7522 From 9c7804230ac72105ede52fb26d475934b33024b8 Mon Sep 17 00:00:00 2001 From: Sebastian Vater Date: Thu, 7 Aug 2025 17:13:16 +0200 Subject: [SERVER] iscsi: Implement receive loop, add a lot of new iSCSI structures - globals, portal groups, portals, ports, etc. - Finally, fixed some bugs. --- src/server/iscsi.c | 3262 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/server/iscsi.h | 846 +++++++++++++- 2 files changed, 3972 insertions(+), 136 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 49e0f46..baee49c 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -24,7 +24,9 @@ #include #include #include +#include #include +#include #include "iscsi.h" /** @@ -57,7 +59,7 @@ uint8_t *iscsi_vsprintf_append_realloc(char *buf, const char *format, va_list ar uint orig_size = 0; if ( buf != NULL ) - orig_size = strlen( (char *) buf ); + orig_size = (uint) strlen( (char *) buf ); va_copy( args_copy, args ); uint new_size = vsnprintf( NULL, 0, format, args_copy ); @@ -186,7 +188,7 @@ iscsi_hashmap *iscsi_hashmap_create(const uint capacity) return NULL; } - map->cap_load = (map->capacity * 3UL) >> 2UL; // 75% of capacity + map->cap_load = (uint) ((map->capacity * 3UL) >> 2UL); // 75% of capacity map->count = 0; map->removed_count = 0; map->first = NULL; @@ -273,7 +275,7 @@ static int iscsi_hashmap_resize(iscsi_hashmap *map) return -1; } - map->cap_load = (map->capacity * 3UL) >> 2UL; // 75% of capacity + 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; @@ -455,7 +457,7 @@ int iscsi_hashmap_key_destroy_value_callback(uint8_t *key, const size_t key_size * memory exhaustion). * @retval 0 Key / value pair was added successfully. */ -int iscsi_hashmap_put(iscsi_hashmap *map, const uint8_t *key, const size_t key_size, uint8_t *value) +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) ) return -1; @@ -512,7 +514,7 @@ int iscsi_hashmap_put(iscsi_hashmap *map, const uint8_t *key, const size_t key_s * @retval 0 Key / value pair was added successfully. * @retval 1 Key already existed. */ -int iscsi_hashmap_get_put(iscsi_hashmap *map, const uint8_t *key, const size_t key_size, uint8_t **out_in_value) +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) ) return -1; @@ -580,7 +582,7 @@ int iscsi_hashmap_get_put(iscsi_hashmap *map, const uint8_t *key, const size_t k * the callback function also returned 0, otherwise * the return value got by the callbuck function. */ -int iscsi_hashmap_put_free(iscsi_hashmap *map, const uint8_t *key, const size_t key_size, uint8_t *value, iscsi_hashmap_callback callback, uint8_t *user_data) +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) ) return -1; @@ -751,7 +753,7 @@ void iscsi_hashmap_remove_free(iscsi_hashmap *map, const uint8_t *key, const siz * @return Number of elements currently in use by the * hash map. Buckets marked for removal are not counted. */ -int iscsi_hashmap_size(iscsi_hashmap *map) +uint iscsi_hashmap_size(const iscsi_hashmap *map) { return (map->count - map->removed_count); } @@ -815,16 +817,16 @@ iscsi_bhs_packet *iscsi_create_packet() return bhs_pkt; } - bhs_pkt->opcode = 0; // Initialize everything to zero + 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; + 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) ); @@ -870,10 +872,10 @@ iscsi_bhs_packet *iscsi_append_ahs_packet(iscsi_bhs_packet *packet_data, const u } 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 + iscsi_align(ahs_len, ISCSI_ALIGN_SIZE); + const uint32_t new_pkt_size = (uint32_t) (old_pkt_size + iscsi_align(ahs_len, ISCSI_ALIGN_SIZE)); if ( new_pkt_size > (sizeof(struct iscsi_bhs_packet) + 1020UL) ) { - logadd( LOG_ERROR, "iscsi_append_ahs_packet: Total numer of AHS packets exceeds 255 DWORDs" ); + logadd( LOG_ERROR, "iscsi_append_ahs_packet: Total numer of AHS packet size exceeds 255 DWORDs" ); return NULL; } @@ -887,11 +889,11 @@ iscsi_bhs_packet *iscsi_append_ahs_packet(iscsi_bhs_packet *packet_data, const u } iscsi_ahs_packet *ahs_pkt = (iscsi_ahs_packet *) ((uint8_t *) packet_data + old_pkt_size); - ahs_pkt->len = iscsi_get_be16(ahs_len); + 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 += (ahs_len + (ISCSI_ALIGN_SIZE - 1)) >> 2UL; + packet_data->total_ahs_len += (uint8_t) ((ahs_len + (ISCSI_ALIGN_SIZE - 1)) >> 2UL); return packet_data; } @@ -922,7 +924,7 @@ int iscsi_get_ahs_packets(const iscsi_bhs_packet *packet_data) len = iscsi_align(len, ISCSI_ALIGN_SIZE); ahs_len -= len; - ahs_pkt = ((uint8_t *) ahs_pkt) + (len - offsetof(struct iscsi_ahs_packet, data)); // Advance pointer to next AHS packet + ahs_pkt = (iscsi_ahs_packet *) (((uint8_t *) ahs_pkt) + (len - offsetof(struct iscsi_ahs_packet, data))); // Advance pointer to next AHS packet count++; } @@ -959,7 +961,7 @@ iscsi_ahs_packet *iscsi_get_ahs_packet(const iscsi_bhs_packet *packet_data, cons len = iscsi_align(len, ISCSI_ALIGN_SIZE); ahs_len -= len; - ahs_pkt = ((uint8_t *) ahs_pkt) + (len - offsetof(struct iscsi_ahs_packet, data)); // Advance pointer to next AHS packet + 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_get_ahs_packet: Specified index for AHS packet does not exist" ); @@ -967,6 +969,54 @@ iscsi_ahs_packet *iscsi_get_ahs_packet(const iscsi_bhs_packet *packet_data, cons return NULL; } +/** + * @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(); + + if ( packet_data == NULL ) + return packet_data; + } + + 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" ); + + return NULL; + } + + 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; + + packet_data = (iscsi_bhs_packet *) realloc( packet_data, new_pkt_size ); + + if ( packet_data == NULL ) { + logadd( LOG_ERROR, "iscsi_append_header_digest_packet: Out of memory while allocating iSCSI header digest packet data for appending" ); + + return packet_data; + } + + memset( (((uint8_t *) packet_data) + old_pkt_size), 0, header_digest_size ); + + return packet_data; +} + /** * @brief Allocate and initialize an iSCSI DS packet and append to existing data stream. * @@ -1001,7 +1051,7 @@ iscsi_bhs_packet *iscsi_append_ds_packet(iscsi_bhs_packet *packet_data, const in } 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 = old_pkt_size + header_digest_size + iscsi_align(ds_len, ISCSI_ALIGN_SIZE) + data_digest_size; + const uint32_t new_pkt_size = (uint32_t) (old_pkt_size + header_digest_size + iscsi_align(ds_len, ISCSI_ALIGN_SIZE) + data_digest_size); packet_data = (iscsi_bhs_packet *) realloc( packet_data, new_pkt_size ); @@ -1011,7 +1061,7 @@ iscsi_bhs_packet *iscsi_append_ds_packet(iscsi_bhs_packet *packet_data, const in return packet_data; } - iscsi_put_be24( packet_data->ds_len, ds_len ); + 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) ); return packet_data; @@ -1177,13 +1227,13 @@ int iscsi_validate_data_digest(const iscsi_bhs_packet *packet_data, const int he */ static int iscsi_validate_text_key_value_pair(const uint8_t *packet_data, const uint32_t len) { - const uint key_val_len = strnlen( packet_data, len ); + const uint key_val_len = (uint) strnlen( (char *) packet_data, len ); const uint8_t *key_end = memchr( packet_data, '=', key_val_len ); if ( key_end == NULL ) return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Missing separator '=' for key / value pair -> invalid iSCSI packet data - const uint key_len = (key_end - packet_data); + const uint key_len = (uint) (key_end - packet_data); if ( key_len == 0 ) return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Zero length is not allowed -> invalid iSCSI packet data @@ -1191,13 +1241,13 @@ static int iscsi_validate_text_key_value_pair(const uint8_t *packet_data, const if ( key_len > ISCSI_TEXT_KEY_MAX_LEN ) return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; - const uint val_len = strnlen( key_end + 1UL, key_val_len - key_len - 1UL ); + 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; if ( val_len > max_len ) return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Value exceeds maximum length -> invalid iSCSI packet data - return key_len + 1UL + val_len + 1UL; // Number of bytes for processed key / value pair (+1 for '=' and NUL terminator) + return (int) (key_len + 1UL + val_len + 1UL); // Number of bytes for processed key / value pair (+1 for '=' and NUL terminator) } /** @@ -1220,7 +1270,7 @@ static int iscsi_validate_key_value_pairs(const uint8_t *packet_data, uint len) int offset = 0L; - while ( (offset < len) && (packet_data[offset] != '\0') ) { + while ( ((uint) offset < len) && (packet_data[offset] != '\0') ) { const int rc = iscsi_validate_text_key_value_pair( (packet_data + offset), (len - offset) ); if ( rc < ISCSI_VALIDATE_PACKET_RESULT_OK ) @@ -1319,8 +1369,8 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint 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 ( ((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_TRANSMIT) != 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; // 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 ( (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_TRANSMIT) != 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_TRANSMIT) != 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 return iscsi_validate_key_value_pairs( ((const uint8_t *) login_req_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len ); @@ -1419,8 +1469,8 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint 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; - if ( ((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_TRANSMIT) != 0) || (login_response_pkt->reserved != 0) || (login_response_pkt->reserved2 != 0) || (login_response_pkt->reserved3 != 0) ) - return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // 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 ( (ISCSI_LOGIN_RESPONSES_FLAGS_GET_CURRENT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_RESERVED) || ((ISCSI_LOGIN_RESPONSES_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_RESERVED) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT) != 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_TRANSMIT) != 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 return iscsi_validate_key_value_pairs( ((const uint8_t *) login_response_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len ); @@ -1533,7 +1583,7 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint */ static int iscsi_parse_text_key_value_pair(iscsi_hashmap *pairs, const uint8_t *packet_data, const uint32_t len) { - const uint key_val_len = strnlen( packet_data, len ); + const uint key_val_len = (uint) strnlen( (char *) packet_data, len ); const uint8_t *key_end = memchr( packet_data, '=', key_val_len ); if ( key_end == NULL ) { @@ -1542,7 +1592,7 @@ static int iscsi_parse_text_key_value_pair(iscsi_hashmap *pairs, const uint8_t * return -1L; } - const uint key_len = (key_end - packet_data); + const uint key_len = (uint) (key_end - packet_data); if ( key_len == 0 ) { logadd( LOG_ERROR, "iscsi_parse_text_key_value_pair: Empty key found which is NOT allowed according to iSCSI specs" ); @@ -1572,8 +1622,8 @@ static int iscsi_parse_text_key_value_pair(iscsi_hashmap *pairs, const uint8_t * return -1L; } - const uint val_len = strnlen( key_end + 1UL, key_val_len - key_len - 1UL ); - const uint max_len = (strcmp( hash_key, "CHAP_C" ) == 0) || (strcmp( 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 + 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; if ( val_len > max_len ) { logadd( LOG_ERROR, "iscsi_parse_text_key_value_pair: Value length larger than iSCSI specs allow" ); @@ -1583,7 +1633,7 @@ static int iscsi_parse_text_key_value_pair(iscsi_hashmap *pairs, const uint8_t * return -1L; } - const uint8_t *hash_val = (uint8_t *) malloc( val_len + 1UL ); + uint8_t *hash_val = (uint8_t *) malloc( val_len + 1UL ); if ( hash_val == NULL ) { logadd( LOG_ERROR, "iscsi_parse_text_key_value_pair: Out of memory allocating memory for value string" ); @@ -1600,7 +1650,7 @@ static int iscsi_parse_text_key_value_pair(iscsi_hashmap *pairs, const uint8_t * if ( rc < 0 ) return -1L; - return 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 + 1UL); // Number of bytes for processed key / value pair (+1 for '=' and NUL terminator) } /** @@ -1633,12 +1683,12 @@ int iscsi_parse_key_value_pairs(iscsi_hashmap *pairs, const uint8_t *packet_data for (key_val_pair_len = 0; (key_val_pair_len < len) && packet_data[key_val_pair_len] != '\0'; key_val_pair_len++) { } - const uint8_t *tmp_partial_buf = iscsi_sprintf_alloc( "%s%s", *partial_pairs, (const char *) 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; - const int rc = iscsi_parse_text_key_value_pair( pairs, tmp_partial_buf, (key_val_pair_len + strlen( *partial_pairs )) ); + const int rc = iscsi_parse_text_key_value_pair( pairs, tmp_partial_buf, (uint32_t) (key_val_pair_len + strlen( (char *) *partial_pairs )) ); free( tmp_partial_buf ); if ( rc < 0 ) @@ -1684,7 +1734,7 @@ int iscsi_parse_key_value_pairs(iscsi_hashmap *pairs, const uint8_t *packet_data int offset = 0L; - while ( (offset < len) && (packet_data[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) ); if ( rc < 0 ) @@ -1721,7 +1771,7 @@ int iscsi_parse_key_value_pairs(iscsi_hashmap *pairs, const uint8_t *packet_data int iscsi_create_key_value_pair_packet_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) { iscsi_key_value_pair_packet *packet_data = (iscsi_key_value_pair_packet *) user_data; - const uint8_t *buf = iscsi_sprintf_alloc( "%s=%s", key, ((value != NULL) ? value : "") ); + uint8_t *buf = iscsi_sprintf_alloc( "%s=%s", key, ((value != NULL) ? value : (uint8_t *) "") ); if ( buf == NULL ) { logadd( LOG_ERROR, "iscsi_create_key_value_pair_packet_callback: Out of memory generating text key / value pair DataSegment" ); @@ -1729,7 +1779,7 @@ int iscsi_create_key_value_pair_packet_callback(uint8_t *key, const size_t key_s return -1L; } - const uint len = strlen( buf ) + 1; + const uint len = (uint) strlen( (char *) buf ) + 1; const uint new_len = packet_data->len + len; uint8_t *new_buf = realloc( packet_data->buf, new_len ); @@ -1759,7 +1809,7 @@ int iscsi_create_key_value_pair_packet_callback(uint8_t *key, const size_t key_s * be attached to a BHS/AHS packet and sent * via TCP/IP. * - * @param[in] pairs Pointer to hash map containing + * @param[in] key_value_pairs Pointer to hash map containing * the key and value pairs to construct the * DataSegment from, may NOT be NULL, so be careful. * @return Pointer to iscsi_key_value_pair_packet @@ -1768,7 +1818,7 @@ int iscsi_create_key_value_pair_packet_callback(uint8_t *key, const size_t key_s * NULL in case of an error (most likely due to * memory exhaustion). */ -iscsi_key_value_pair_packet *iscsi_create_key_value_pairs_packet(const iscsi_hashmap *pairs) +iscsi_key_value_pair_packet *iscsi_create_key_value_pairs_packet(iscsi_hashmap *key_value_pairs) { iscsi_key_value_pair_packet *packet_data = (iscsi_key_value_pair_packet *) malloc( sizeof(struct iscsi_key_value_pair_packet) ); @@ -1781,7 +1831,7 @@ iscsi_key_value_pair_packet *iscsi_create_key_value_pairs_packet(const iscsi_has packet_data->buf = NULL; packet_data->len = 0UL; - if ( iscsi_hashmap_iterate( pairs, iscsi_create_key_value_pair_packet_callback, packet_data ) < 0L ) { + if ( iscsi_hashmap_iterate( key_value_pairs, iscsi_create_key_value_pair_packet_callback, (uint8_t *) packet_data ) < 0L ) { if ( packet_data->buf != NULL ) free( packet_data->buf ); @@ -1809,87 +1859,215 @@ iscsi_key_value_pair_packet *iscsi_create_key_value_pairs_packet(const iscsi_has } /** - * @brief Creates data structure for an iSCSI connection request. + * @brief Extracts a string from a key and value pair. * - * Creates a data structure for an incoming iSCSI connection request - * from iSCSI packet data. + * This function calculates the length of the key + * for the hash map function and returns the value + * as string. * - * @param[in] login_req_pkt Pointer to iSCSI packet data to construct iSCSI - * connection request from. May be NULL in which case this function does - * nothing. - * @return Pointer to initialized iSCSI connection structure or NULL in - * case of an error (invalid iSCSI packet data or memory exhaustion). + * @param[in] key_value_pairs The hash map containing the key and value pairs to be extracted. + * @param[in] key The key to retrieve the string value from. + * @param[out] out_value The string value of the key is stored here. + * @retval -1 An error occured during value retrieval. + * 'out value' is unchanged. + * @retval 0 The value of the key has been successfully + * stored in the 'out_value'. */ -iscsi_connection *iscsi_connection_create(const iscsi_login_req_packet *login_req_pkt) +static int iscsi_get_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, uint8_t **out_value) { - iscsi_connection *conn = (iscsi_connection *) malloc( sizeof(struct iscsi_connection) ); + const uint key_len = (uint) strlen( (char *) key ) + 1; - if ( conn == NULL ) { - logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI connection" ); + return iscsi_hashmap_get( key_value_pairs, key, key_len, out_value ); +} - return NULL; +/** + * @brief Allocates and adds a string value to a key / value hash map pair. + * + * This function allocates memory for a string key + * and its string value. + * + * @param[in] key_value_pairs Pointer to the hash map which should + * contain the added string key and value pair. NULL is NOT + * allowed here, so be careful. + * @param[in] key String containing the key name as string. May + * NOT be NULL, so take caution. + * @param[in] value String containing the value to be stored. + * @return 0 on successful operation, or a negative value on + * error (memory exhaustion). + */ +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; + 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; } - conn->key_value_pairs = iscsi_hashmap_create( 32UL ); + const uint val_len = (uint) strlen( (char *) value ) + 1; + uint8_t *hash_val = (uint8_t *) malloc( val_len ); - if ( conn->key_value_pairs == NULL ) { - logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI text key / value pair hash map" ); + if ( hash_val == NULL ) { + logadd( LOG_ERROR, "iscsi_add_key_value_pair: Out of memory allocating string value." ); - free( conn ); + return -1L; + } - return NULL; + memcpy( hash_val, value, val_len ); + + return iscsi_hashmap_put( key_value_pairs, hash_key, key_len, hash_val ); +} + +/** + * @brief Extracts an integer value from a key and value pair. + * + * This function converts a string representation of a + * key and value pair to an integer value. + * + * @param[in] key_value_pairs The hash map containing the key and value pairs to be extracted. + * @param[in] key The key to retrieve the integer value from. + * @param[out] out_value The integer value of the key is stored here + * or 0 in case of an error during string to integer conversion. + * @retval -1 An error occured during value retrieval. + * 'out value' is unchanged. + * @retval 0 The value of the key has been successfully + * stored in the 'out_value'. + */ +static int iscsi_get_int_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, int32_t *out_value) +{ + uint8_t *str_val; + int rc = iscsi_get_key_value_pair( key_value_pairs, key, &str_val ); + + if ( rc == 0 ) + *out_value = (int32_t) atol( (char *) str_val ); + + return rc; +} + +/** + * @brief Allocates and adds an integer value to a key / value hash map pair. + * + * This function allocates memory for a string key + * and its integer representation as string value. + * + * @param[in] key_value_pairs Pointer to the hash map which should + * contain the added integer key and value pair. NULL is NOT + * allowed here, so be careful. + * @param[in] key String containing the key name as string. May + * NOT be NULL, so take caution. + * @param[in] value Integer containing the value to be stored. + * @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) +{ + const uint8_t *hash_val = iscsi_sprintf_alloc( "%ld", value ); + + if ( hash_val == NULL ) { + logadd( LOG_ERROR, "iscsi_add_int_key_value_pair: Out of memory allocating integer value." ); + + return -1L; } - conn->header_digest = 0L; - conn->data_digest = 0L; + return iscsi_add_key_value_pair( key_value_pairs, key, hash_val ); +} - conn->isid.a = login_req_pkt->isid.a; - conn->isid.b = iscsi_get_be16(login_req_pkt->isid.b); - conn->isid.c = login_req_pkt->isid.c; - conn->isid.d = iscsi_get_be16(login_req_pkt->isid.d); +/** + * @brief Extracts a boolean value from a key and value pair. + * + * This function converts a string representation of a + * key and value pair to a boolean value. + * + * @param[in] key_value_pairs The hash map containing the key and value pairs to be extracted. + * @param[in] key The key to retrieve the boolean value from. + * @param[out] out_value The boolean value of the key is stored here. + * 'Yes' represents true and any other string results in false. + * @retval -1 An error occured during value retrieval. + * 'out value' is unchanged. + * @retval 0 The value of the key has been successfully + * stored in the 'out_value'. + */ +static int iscsi_get_bool_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, int32_t *out_value) +{ + uint8_t *value; + int rc = iscsi_get_key_value_pair( key_value_pairs, key, &value ); - conn->tsih = login_req_pkt->tsih; // Identifier, no need for endianess conversion - conn->init_task_tag = login_req_pkt->init_task_tag; // Identifier, no need for endianess conversion - conn->cid = login_req_pkt->cid; // Identifier, no need for endianess conversion - conn->cmd_sn = iscsi_get_be32(login_req_pkt->cmd_sn); - conn->exp_stat_sn = iscsi_get_be32(login_req_pkt->exp_stat_sn); + if ( rc == 0 ) + *out_value = (strcasecmp( (char *) value, "Yes" ) == 0) ? true : false; - return conn; + return rc; } /** - * @brief Deallocates all resources acquired by iscsi_connection_create. + * @brief Allocates and adds an boolean value to a key / value hash map pair. * - * Deallocates a data structure of an iSCSI connection - * request and all allocated hash maps which don't - * require closing of external resources like closing - * TCP/IP socket connections. + * This function allocates memory for a string key + * and its integer value.\n + * The string representation for true is: Yes\n + * The string representation for false is: No * - * @param[in] conn Pointer to iSCSI connection structure to be - * deallocated, TCP/IP connections are NOT closed by this - * function, use iscsi_connection_close for this. This may be - * NULL in which case this function does nothing. + * @param[in] key_value_pairs Pointer to the hash map which should + * contain the added boolean key and value pair. NULL is NOT + * allowed here, so be careful. + * @param[in] key String containing the key name as string. May + * NOT be NULL, so take caution. + * @param[in] value Boolean containing the value to be stored + * as string. + * @return 0 on successful operation, or a negative value on + * error (memory exhaustion). */ -void iscsi_connection_destroy(iscsi_connection *conn) +static int iscsi_add_bool_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int value) { - if ( conn != 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 ); + const uint8_t *hash_val = (uint8_t *) ((value != 0) ? "Yes" : "No"); - conn->key_value_pairs = NULL; - } + return iscsi_add_key_value_pair( key_value_pairs, key, hash_val ); +} - free( conn ); +/** + * @brief Creates and initializes an iSCSI portal group. + * + * Specified tag and flags are used for portal group + * initialization. + * @param[in] tag Tag to associate with the portal group. + * @param[in] flags Flags to set for the portal group. + * @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 *portal_group = (iscsi_portal_group *) malloc( sizeof(struct iscsi_portal_group) ); + + if ( portal_group == NULL ) { + logadd( LOG_ERROR, "iscsi_portal_group_create: Out of memory allocating iSCSI portal group structure" ); + + return NULL; + } + + portal_group->portals = iscsi_hashmap_create( 0UL ); + + if ( portal_group->portals == NULL ) { + logadd( LOG_ERROR, "iscsi_portal_group_create: Out of memory allocating iSCSI portal hash map" ); + + free( portal_group ); + + return NULL; } + + portal_group->ref_count = 0L; + portal_group->tag = tag; + portal_group->flags = flags; + portal_group->chap_group = 0L; + + return portal_group; } /** - * @brief iSCSI connection destructor callback for hash map. + * @brief iSCSI portal destructor callback for hash map. * * Callback function for deallocation of an iSCSI - * connection stored in the hash map managing all - * iSCSI connections. + * 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. @@ -1902,10 +2080,2900 @@ void iscsi_connection_destroy(iscsi_connection *conn) * there is a possibility for future usage. * @return Always returns 0 as this function cannot fail. */ -int iscsi_connection_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) +int iscsi_portal_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) { - iscsi_connection_destroy( (iscsi_connection *) value ); + iscsi_portal_destroy( (iscsi_portal *) value ); iscsi_hashmap_key_destroy( key ); return 0L; } + +/** + * @brief Deallocates resources acquired by iscsi_portal_group_create. + * + * 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. + */ +void iscsi_portal_group_destroy(iscsi_portal_group *portal_group) +{ + if ( portal_group != NULL ) { + if ( portal_group->portals != NULL ) { + iscsi_hashmap_iterate( portal_group->portals, iscsi_portal_destroy_callback, NULL ); + iscsi_hashmap_destroy( portal_group->portals ); + + portal_group->portals = NULL; + } + + free( portal_group ); + } +} + +/** + * @brief Adds an iSCSI portal to the iSCSI portal group hash map. + * + * 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, + * so take caution. + * @param[in] 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 + * @retval 0 The portal has been added successfully to the + * portal group. + */ +int iscsi_portal_group_add_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 ) + return -1L; + + const uint key_len = (uint) strlen( (char *) tmp_buf ) + 1; + 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; + } + + int rc = iscsi_hashmap_put( portal_group->portals, key, key_len, (uint8_t *) portal ); + + if ( rc < 0 ) { + logadd( LOG_ERROR, "iscsi_portal_group_add_portal: Adding portal to hash map containing iSCSI portal group failed" ); + + iscsi_hashmap_key_destroy( key ); + + return rc; + } + + portal->group = portal_group; + + return 0L; +} + +/** + * @brief Allocates and initializes an iSCSI portal structure. + * + * This function makes a copy of the passed host / IP address + * and port, but does NOT initialize the socket. + * + * @param[in] host Host / IP address of the portal. + * @param[in] port Port of the portal. + * @return Pointer to iSCSI portal structure or NULL + * in case of an error (memory exhausted). + */ +iscsi_portal *iscsi_portal_create(const uint8_t *host, const uint8_t *port) +{ + iscsi_portal *portal = (iscsi_portal *) malloc( sizeof(struct iscsi_portal) ); + + if ( portal == NULL ) { + logadd( LOG_ERROR, "iscsi_portal_create: Out of memory allocating iSCSI portal structure" ); + + return NULL; + } + + portal->group = NULL; + + const uint host_len = (uint) strlen( (char *) host ) + 1; + + portal->host = (uint8_t *) malloc( host_len ); + + if ( portal->host == NULL ) { + logadd( LOG_ERROR, "iscsi_portal_create: Out of memory allocating iSCSI portal host name" ); + + return NULL; + } + + memcpy( portal->host, host, host_len ); + + const uint port_len = (uint) strlen( (char *) port ) + 1; + + portal->port = (uint8_t *) malloc( port_len ); + + if ( portal->port == NULL ) { + logadd( LOG_ERROR, "iscsi_portal_create: Out of memory allocating iSCSI portal port" ); + + return NULL; + } + + memcpy( portal->port, port, port_len ); + + portal->sock = -1L; + + return portal; +} + +/** + * @brief Deallocates all resources acquired by iscsi_portal_create. + * + * This function frees the memory acquired for host / IP address + * and port but does NOT remove it from the portal group hash map. + * + * @param[in] portal Pointer to iSCSI portal to be deallocated, + * which may be NULL in which case the function does nothing. + */ +void iscsi_portal_destroy(iscsi_portal *portal) +{ + if ( portal != NULL ) { + if ( portal->port != NULL ) { + free( portal->port ); + + portal->port = NULL; + } + + if ( portal->host != NULL ) { + free( portal->host ); + + portal->host = NULL; + } + + free( portal ); + } +} + +/** + * @brief Allocates and initializes an iSCSI port. + * + * THis function marks the port in use, but does + * NOT set a transport ID. Everything else is + * initialized, however. + * + * @param[in] name Pointer to port name. This + * may NOT be NULL, so be careful. + * @param[in] id Identifier for this port. + * @param[in] index Index number for this port. + * @return Pointer to initialized iSCSI port or NULL + * in case of memory exhaustion. + */ +iscsi_port *iscsi_port_create(const uint8_t *name, const uint64_t id, const uint16_t index) +{ + iscsi_port *port = (iscsi_port *) malloc( sizeof(struct iscsi_port) ); + + if ( port == NULL ) { + logadd( LOG_ERROR, "iscsi_port_create: Out of memory allocating iSCSI port" ); + + return NULL; + } + + const uint name_len = (uint) strlen( (char *) name ) + 1; + + port->name = (uint8_t *) malloc( name_len ); + + if ( port->name == NULL ) { + logadd( LOG_ERROR, "iscsi_port_create: Out of memory allocating iSCSI port name" ); + + free( port ); + + return NULL; + } + + memcpy( port->name, name, name_len ); + + port->transport_id = NULL; + port->id = id; + port->index = index; + port->flags = ISCSI_PORT_FLAGS_IN_USE; + port->transport_id_len = 0U; + + return port; +} + +/** + * @brief Deallocates all resources acquired iscsi_port_create. + * + * This function also frees the port name and transport ID, + * if they exist. + * + * @param[in] port iSCSI port to deallocate. This may + * be NULL in which case nothing happens. + */ +void iscsi_port_destroy(iscsi_port *port) +{ + if ( port != NULL ) { + if ( port->name != NULL ) { + free( port->name ); + + port->name = NULL; + } + + if ( port->transport_id != NULL ) { + free( port->transport_id ); + + port->transport_id = NULL; + } + + free( port ); + } +} + +/** + * @brief Retrieves the name of an iSCSI port. + * + * This function is just a getter. + * + * @param[in] port Pointer to iSCSI port to retrieve + * the name from and may NOT be NULL, so be + * careful. + * @return Pointer to string containing the name + * of the iSCSI port. + */ +uint8_t *iscsi_port_get_name(const iscsi_port *port) +{ + return port->name; +} + +/** + * @brief Sets the SCSI transport ID of the iSCSI port. + * + * This function constructs the SCSI packet data + * for the SCSI transport id by assigning a name + * and the Initiator Session ID (ISID).\n + * Currently, always transport ID format 0x1 will + * be created. + * + * @param[in] Pointer to iSCSI port to assign the + * SCSI transport ID to. May NOT be NULL, so be + * careful. + * @param[in] Pointer to iSCSI name to assign + * along with the ISID as name. + * @param[in] Initiator Session ID (ISID). + * @return 0 if transport ID could be created + * successfully, a negative error code + * otherwise. + */ +int iscsi_port_transport_id_set(iscsi_port *port, const uint8_t *name, const uint64_t isid) +{ + uint8_t *tmp_buf = iscsi_sprintf_alloc( "%s,i,0x%12.12" PRIx64, name, isid ); + + if ( tmp_buf == NULL ) { + logadd( LOG_ERROR, "iscsi_port_transport_id_set: Out of memory allocating SCSI transport ID name for iSCSI port" ); + + 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); + + if ( (len < 20UL) || ((len + offsetof(struct iscsi_transport_id, name)) >= 65536UL) ) { + logadd( LOG_ERROR, "iscsi_port_transport_id_set: Out of memory allocating SCSI transport ID for iSCSI port" ); + + free( tmp_buf ); + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER; + } + + port->transport_id = (iscsi_transport_id *) malloc( sizeof(struct iscsi_transport_id) + len ); + + if ( port->transport_id == NULL ) { + logadd( LOG_ERROR, "iscsi_port_transport_id_set: Out of memory allocating SCSI transport ID for iSCSI port" ); + + free( tmp_buf ); + + 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->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)) ); + + port->transport_id_len = (uint16_t) (offsetof(struct iscsi_transport_id, name) + len); + + return ISCSI_CONNECT_PDU_READ_OK; +} + +/** + * @brief Gets an iSCSI device being in use by portal group identifier. + * + * This function uses the unique portal group + * identifier in order to get the port. + * + * @param[in] device Pointer to iSCSI device to be searched. May + * NOT be NULL, so take caution. + * @param[in] id Portal group ID to be searched for. + * @return Pointer to iSCSI port belonging to the iSCSI + * portal group ID or NULL if either the portal + * group ID does not exist or the port is NOT in use. + */ +iscsi_port *iscsi_device_find_port_by_portal_group_tag(const iscsi_device *device, const uint64_t id) +{ + iscsi_port *port; + + if ( iscsi_hashmap_get( device->ports, (uint8_t *) &id, sizeof(id), (uint8_t **) &port ) < 0 ) + return NULL; + + if ( (port == NULL) || ((port->flags & ISCSI_PORT_FLAGS_IN_USE) == 0) ) + return NULL; + + return port; +} + +/** + * @brief Finds an iSCSI target node by case insensitive name search. + * + * Callback function for each element while iterating + * through the 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, MUST + * be a multiple of 8 bytes which is NOT checked, so + * be careful. + * @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 + * 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; + + if ( strcasecmp( (char *) target->name, (char *) target_find->name ) != 0 ) + return 0L; + + target_find->target = target; + + return -1L; +} + +/** + * @brief Searches an iSCSI target node by name using case insensitive search. + * + * This function searches for an iSCSI target node + * by iterating through the iSCSI global target node + * hash map. + * + * @param[in] target_name Pointer to string containing the name + * of the iSCSI target node to be searched for. + * @return Pointer to found iSCSI target node or NULL + * in case no iSCSI target node has a matching name. + */ +iscsi_target_node *iscsi_target_node_find(uint8_t *target_name) +{ + if ( target_name == NULL ) + return NULL; + + iscsi_target_node_find_name target_find = {NULL, target_name}; + + iscsi_hashmap_iterate( iscsi_globvec->target_nodes, iscsi_target_node_find_callback, (uint8_t *) &target_find ); + + return target_find.target; +} + +/** + * @brief Retrieves target node redirection address. + * + * This function checks whether the target node needs + * a redirect and is used for informing the client + * about a necessary redirection. + * + * @param[in] conn Pointer to iSCSI connection, may NOT + * be NULL, so be careful. + * @param[in] target Pointer to iSCSI target node where + * NULL is NOT allowed, so take caution. + * @return Pointer to redirect target address or NULL + * if no redirection. + */ +uint8_t *iscsi_target_node_get_redirect(iscsi_connection *conn, iscsi_target_node *target) +{ + // TODO: Implement function + + return NULL; +} + +/** + * @brief Checks if target node is accessible. + * + * This function checks whether access is possible + * to a specific iSCSI IQN and IP address. + * + * @param[in] conn Pointer to iSCSI connection which + * may NOT be NULL, so be careful. + * @param[in] target Pointer to iSCSI target node. NULL + * is not allowed here, to take caution. + * @param[in] iqn Pointer to iSCSI IQN string. This + * is not allowed to be NULL, so be careful. + * @param[in] adr Pointer to IP address, NULL is not + * allowed, so take care. + * @return 0 if access is possible, a negative error + * code otherwise. + */ +int iscsi_target_node_access(iscsi_connection *conn, iscsi_target_node *target, const uint8_t *iqn, const uint8_t *adr) +{ + // TODO: Implement access check function + + return 0L; +} + +/** + * @brief Creates and initializes an iSCSI session. + * + * This function creates and initializes all relevant + * data structures of an ISCSI session.\n + * Default key and value pairs are created and + * assigned before they are negotiated at the + * login phase. + * + * @param[in] conn Pointer to iSCSI connection to associate with the session. + * @param[in] target Pointer to iSCSI target node to assign with the session. + * @param[in] type Session type to initialize the session with. + * @return Pointer to initialized iSCSI session or NULL in case an error + * occured (usually due to memory exhaustion). + */ +iscsi_session *iscsi_session_create(iscsi_connection *conn, iscsi_target_node *target, const int type) +{ + iscsi_session *session = (iscsi_session *) malloc( sizeof(struct iscsi_session) ); + + if ( session == NULL ) { + logadd( LOG_ERROR, "iscsi_session_create: Out of memory allocating iSCSI session" ); + + 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 ); + + if ( session->connections == NULL ) { + logadd( LOG_ERROR, "iscsi_session_create: Out of memory allocating iSCSI session connection hash map" ); + + free( session ); + + return NULL; + } + + uint8_t *conn_key = iscsi_hashmap_key_create( (uint8_t *) &conn->cid, sizeof(conn->cid) ); + + if ( conn_key == NULL ) { + logadd( LOG_ERROR, "iscsi_session_create: Out of memory allocating iSCSI session connection hash map" ); + + 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; + + session->key_value_pairs = iscsi_hashmap_create( 0UL ); + + 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; + } + + int rc = iscsi_add_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS, session->max_conns ); + rc |= iscsi_add_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_add_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_add_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_add_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_add_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_add_bool_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IMMEDIATE_DATA, session->immediate_data ); + rc |= iscsi_add_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_add_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_add_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_add_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 ); + + 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; + } + + return session; +} + +/** + * @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. + * + * @param[in] session 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->target = NULL; + session->type = ISCSI_SESSION_TYPE_INVALID; + + if ( session->key_value_pairs != NULL ) { + iscsi_hashmap_iterate( session->key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL ); + iscsi_hashmap_destroy( session->key_value_pairs ); + + session->key_value_pairs = NULL; + } + + if ( session->connections != NULL ) { + iscsi_hashmap_iterate( session->connections, iscsi_connection_destroy_callback, NULL ); + iscsi_hashmap_destroy( session->connections ); + + session->connections = NULL; + } + + if ( session->initiator_port != NULL ) { + iscsi_port_destroy( session->initiator_port ); + + session->initiator_port = NULL; + } + + free( session ); // TODO: Check if potential reusage of session makes sense. + } +} + +/** + * @brief Creates data structure for an iSCSI connection from iSCSI portal and TCP/IP socket. + * + * Creates a data structure for incoming iSCSI connection + * requests from iSCSI packet data. + * + * @param[in] portal Pointer to iSCSI portal to associate the + * connection with. + * @param[in] sock TCP/IP socket to associate the connection with. + * @return Pointer to initialized iSCSI connection structure or NULL in + * case of an error (invalid iSCSI packet data or memory exhaustion). + */ +iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock) +{ + iscsi_connection *conn = (iscsi_connection *) malloc( sizeof(struct iscsi_connection) ); + + if ( conn == NULL ) { + logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI connection" ); + + return NULL; + } + + conn->session = NULL; + conn->key_value_pairs = iscsi_hashmap_create( 32UL ); + + if ( conn->key_value_pairs == NULL ) { + logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI text key / value pair hash map" ); + + free( conn ); + + return NULL; + } + + conn->partial_pairs = NULL; + conn->device = NULL; + conn->init_port = NULL; + conn->init_name = NULL; + conn->init_adr = NULL; + conn->target = NULL; + conn->target_port = NULL; + 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->login_response_pdu = NULL; + 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->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; + + return conn; +} + +/** + * @brief iSCSI connection destructor callback for hash map. + * + * Callback function for deallocation of an iSCSI + * connection stored in the hash map managing all + * iSCSI connections. + * + * @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. + */ +int iscsi_connection_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) +{ + iscsi_connection_destroy( (iscsi_connection *) value ); + iscsi_hashmap_key_destroy( key ); + + return 0L; +} + +/** + * @brief Deallocates all resources acquired by iscsi_connection_create. + * + * Deallocates a data structure of an iSCSI connection + * request and all allocated hash maps which don't + * require closing of external resources like closing + * TCP/IP socket connections. + * + * @param[in] conn Pointer to iSCSI connection structure to be + * deallocated, TCP/IP connections are NOT closed by this + * function, use iscsi_connection_close for this. This may be + * NULL in which case this function does nothing. + */ +void iscsi_connection_destroy(iscsi_connection *conn) +{ + if ( conn != NULL ) { + if ( conn->portal_port != NULL ) { + free ( conn->portal_port ); + + conn->portal_port = NULL; + } + + if ( conn->portal_host != NULL ) { + free ( conn->portal_host ); + + conn->portal_host = NULL; + } + + if ( conn->target_name_short != NULL ) { + free ( conn->target_name_short ); + + conn->target_name_short = NULL; + } + + if ( conn->init_adr != NULL ) { + free ( conn->init_adr ); + + conn->init_adr = NULL; + } + + if ( conn->init_name != NULL ) { + free ( conn->init_name ); + + conn->init_name = NULL; + } + + if ( conn->partial_pairs != NULL ) { + 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 ); + + conn->key_value_pairs = NULL; + } + + free( conn ); + } +} + +/** + * @brief Drops all connections based on matching pattern. + * + * @param[in] conn Pointer to iSCSI connection which may + * NOT be NULL, so be careful. + * @param[in] conn_match Pointer to match string, NULL is + * not allowd here, so take caution. + * @param[in] all Non-zero number indicating removing all + * connections. + * @return 0 on success, a negative error code otherwise. + */ +int iscsi_connection_drop(iscsi_connection *conn, const uint8_t *conn_match, const int all) +{ + // TODO: Implement function. + + return 0; +} + +/** + * @brief Schedules an iSCSI connection. + * + * @param[in] conn Pointer to ISCSI connection to be + * scheduled. May NOT be NULL, so be careful. + */ +void iscsi_connection_schedule(iscsi_connection *conn) +{ + // TODO: Implement function. +} + +/** + * @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. + * + * Returns ISCSI_CONNECT_PDU_READ_ERR_FATAL if the operation + * indicates a fatal error with the TCP connection (including + * if the TCP connection was closed unexpectedly). + * + * Otherwise returns the number of bytes successfully read. + */ +int iscsi_connection_read(const iscsi_connection *conn, uint8_t *buf, const uint len) +{ + if ( len == 0 ) + return 0; + + const int rc = (int) recv( conn->sock, buf, len, MSG_WAITALL ); + + return (rc > 0) ? rc : ISCSI_CONNECT_PDU_READ_ERR_FATAL; +} + +/** + * @brief Writes data for the specified iSCSI connection to its TCP socket. + * + * 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 + * if the TCP connection was closed unexpectedly). + * + * Otherwise returns the number of bytes successfully written. + */ +int iscsi_connection_write(const iscsi_connection *conn, uint8_t *buf, const uint len) +{ + if ( len == 0 ) + return 0; + + const int rc = (int) send( conn->sock, buf, len, 0L ); + + return (rc > 0) ? rc : ISCSI_CONNECT_PDU_READ_ERR_FATAL; +} + +/** + * @brief Copies retrieved key and value pairs into SCSI connection and session structures. + * + * This function converts string representations of + * integer and boolean key and value pairs. + * + * @param[in] conn 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. + * @retval 0 All key and value pairs were copied successfully. + */ +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); + + if ( rc != 0 ) + return rc; + + if ( (int_val <= 0L) || (int_val > (int32_t) ISCSI_DEFAULT_MAX_RECV_DS_LEN) ) + int_val = ISCSI_DEFAULT_MAX_RECV_DS_LEN; + + conn->max_recv_ds_len = int_val; + + uint8_t *value; + + rc = iscsi_get_key_value_pair( conn->key_value_pairs, (uint8_t *) 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; + + rc = iscsi_get_key_value_pair( conn->key_value_pairs, (uint8_t *) 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; + + rc = iscsi_get_int_key_value_pair( conn->key_value_pairs, (uint8_t *) 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); + + 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); + + 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); + + 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); + + if ( rc != 0 ) + return rc; + + conn->session->init_r2t = int_val; + + rc = iscsi_get_bool_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IMMEDIATE_DATA, &int_val); + + if ( rc != 0 ) + return rc; + + conn->session->immediate_data = int_val; + + return 0L; +} + +/** + * @brief Checks buffer sizes of an iSCSI connection with it's associated session for consistency. + * + * This function ensures that, for example, first + * burst length does not exceed maximum burst + * length and that the buffers don't exceed their + * minimum and maximum allowed values. + * + * @param[in] conn Pointer to iSCSI connection which holds the + * values to be checked for consistency. May NOT be NULL, + * so take caution. + * @retval -1 At least one value check failed the consistency check. + * @retval 0 All consistency checks have passed successfully. + */ +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 -1; + + return 0; +} + +/** + * @brief Updates iSCSI connection and session values after being retrieved from the client. + * + * This function copies the key and value pairs into the + * internal connection and session structure and checks + * them for consistency.\n + * The TCP receive buffer will be adjusted to the new + * updated value but is never lower than 4KiB and never + * higher than 8KiB plus header overhead and a factor of + * 4 for receiving four packets at once. + * + * @param[in] conn Pointer to ISCSI connection which should + * be updated. + * @retval -1 An error occured, e.g. socket is already closed. + * @retval 0 All values have been updated successfully and + * the socket is still alive. + */ +static int iscsi_connection_update_key_value_pairs(iscsi_connection *conn) +{ + int rc = iscsi_connection_copy_key_value_pairs( conn ); + + if ( rc < 0 ) { + if ( conn->state < ISCSI_CONNECT_STATE_EXITING ) + conn->state = ISCSI_CONNECT_STATE_EXITING; + + return rc; + } + + rc = iscsi_connection_check_key_value_pairs( conn ); + + if ( (rc < 0) && (conn->state < ISCSI_CONNECT_STATE_EXITING) ) + conn->state = ISCSI_CONNECT_STATE_EXITING; + + if ( conn->sock < 0 ) + return -1L; + + 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; + + recv_buf_len += (uint) (sizeof(struct iscsi_bhs_packet) + 1020UL + 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. + + 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. + + return rc; +} + +/** + * @brief Prepares an iSCSI login response PDU and sends it via TCP/IP. + * + * This function constructs the login response PDU + * to be sent via TCP/IP. + * + * @param[in] conn Pointer to ISCSI connection to send the TCP/IP + * packet with. + * @param[in] login_response_pdu Pointer to login response PDU to + * be sent via TCP/IP. + * @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 + * after sending the TCP/IP packet. + */ +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) +{ + iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; + + 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->stat_sn, conn->stat_sn++ ); + + if ( conn->session != NULL ) { + iscsi_put_be32( (uint8_t *) &login_response_pkt->exp_cmd_sn, conn->session->exp_cmd_sn ); + iscsi_put_be32( (uint8_t *) &login_response_pkt->max_cmd_sn, conn->session->max_cmd_sn ); + } else { + iscsi_put_be32( (uint8_t *) &login_response_pkt->exp_cmd_sn, login_response_pdu->cmd_sn ); + iscsi_put_be32( (uint8_t *) &login_response_pkt->max_cmd_sn, login_response_pdu->cmd_sn ); + } + + if ( login_response_pkt->status_class != ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SUCCESS ) + login_response_pkt->flags &= (int8_t) ~(ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT | 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 ); +} + +/** + * @brief Callback function after login response has been sent. + * + * This function is invoked after the login + * 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_login_err_complete(uint8_t *user_data) +{ + iscsi_connection *conn = (iscsi_connection *) user_data; + + if ( (conn->flags & ISCSI_CONNECT_FLAGS_FULL_FEATURE) != 0 ) + iscsi_connection_update_key_value_pairs( conn ); +} + +/** + * @brief Callback function after login response has been sent. + * + * This function is invoked after the login + * 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_login_ok_complete(uint8_t *user_data) +{ + iscsi_connection *conn = (iscsi_connection *) user_data; + + if ( conn->state >= ISCSI_CONNECT_STATE_EXITING ) + return; + + if ( (conn->flags & ISCSI_CONNECT_FLAGS_FULL_FEATURE) != 0 ) { + iscsi_connection_update_key_value_pairs( conn ); + + iscsi_connection_schedule( conn ); + } +} + +/** + * @brief Initializes an iSCSI login response PDU structure. + * + * This function initializes the internal login + * response data structure which is part of the iSCSI + * login procedure. + * + * @param[in] login_response_pdu Pointer to login response PDU, NULL + * is not an allowed value here, so take caution. + * @param[in] pdu Pointer to login request PDU from client, + * may NOT be NULL, so be careful. + * @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) +{ + 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; + } + + 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_pkt->flags |= (int8_t) (login_req_pkt->flags & (ISCSI_LOGIN_REQ_FLAGS_TRANSMIT | ISCSI_LOGIN_REQ_FLAGS_CONTINUE | ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_MASK)); + + if ( (login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT) != 0 ) + login_response_pkt->flags |= (login_req_pkt->flags & ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_MASK); + + login_response_pkt->isid.a = login_req_pkt->isid.a; + login_response_pkt->isid.b = login_req_pkt->isid.b; // Copying over doesn't change endianess. + login_response_pkt->isid.c = login_req_pkt->isid.c; + 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_pdu->cmd_sn = iscsi_get_be32(login_req_pkt->cmd_sn); + + if ( login_response_pkt->tsih != 0 ) + login_response_pkt->stat_sn = login_req_pkt->exp_stat_sn; // Copying over doesn't change endianess.' + + if ( ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT) != 0) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE) != 0) ) { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } else if ( (ISCSI_VERSION_MIN < login_req_pkt->version_min) || (ISCSI_VERSION_MAX > login_req_pkt->version_max) ) { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_WRONG_VERSION; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } else if ( (ISCSI_LOGIN_RESPONSES_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_RESERVED) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT) != 0) ) { + login_response_pkt->flags &= (int8_t) ~(ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_MASK | ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT | ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_MASK); + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + 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; + } + + return ISCSI_CONNECT_PDU_READ_OK; +} + +/** + * @brief Saves incoming key / value pairs from the client of a login request PDU. + * + * The login response structure has status detail + * invalid login request type set in case of an error. + * + * @param[in] conn Pointer to iSCSI connection + * used for key and value pair extraction. + * @param[out] key_value_pairs Pointer to hash map which + * stores all the parsed key and value pairs. + * @param[in] login_response_pdu Pointer to iSCSI login response + * PDU, may NOT be NULL, so be careful. + * @param[in] pdu Pointer to iSCSI login request packet + * PDU, may NOT be NULL, so be careful. + * @retval -1 An error occured during parse of + * key and value pairs (memory exhaustion). + * @retval 0 All key and value pairs have been parsed successfully. + */ +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) +{ + iscsi_login_req_packet *login_req_pkt = (iscsi_login_req_packet *) pdu->bhs_pkt; + iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; + const int rc = iscsi_parse_key_value_pairs( key_value_pairs, (uint8_t *) pdu->ds_cmd_data, pdu->ds_len, ((login_req_pkt->flags & ISCSI_LOGIN_REQ_FLAGS_CONTINUE) != 0), &conn->partial_pairs ); + + if ( rc < 0 ) { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER; + } + + return ISCSI_CONNECT_PDU_READ_OK; +} + +/** + * @brief Extracts the Initiator Session ID (ISID) from packet data into a 64-bit unsigned integer. + * + * The ISID is constructed by OR'ing and shifting the + * four parts a, b, c and d into their proper places + * with d being in the LSB area.\n + * Since the ISID is only 48 bit wide, the 16 + * MSB bits are always cleared. + * + * @param[in] isid Pointer to the ISID part of packet data. + * May NOT be NULL, so be careful. + * @return The 64-bit unsigned integer value representing + * the Initiator Session ID (ISID). + */ +static inline uint64_t iscsi_connection_get_isid(const iscsi_isid *isid) +{ + return ((uint64_t) isid->a << 40ULL) | ((uint64_t) iscsi_get_be16(isid->b) << 24ULL) | ((uint64_t) isid->c << 16ULL) | (uint64_t) iscsi_get_be16(isid->d); +} + +/** + * @brief Initializes the login response port names. + * + * This function extracts the initiator name from the + * key and value pair and stores the result in + * the iSCSI connection, as well as a full qualified + * initiator port name. + * + * @param[in] conn Pointer to iSCSI connection where to + * store the initiator name. + * @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. + * @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 + * successfully, a negative error code otherwise + * in which case the status class and detail are + * set as well. + */ +static int iscsi_connection_login_init_port(iscsi_connection *conn, iscsi_pdu *response_pdu, iscsi_hashmap *key_value_pairs, uint8_t **init_port_name) +{ + 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 ); + + if ( rc != 0 ) { + 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; + } + + conn->init_name = iscsi_sprintf_alloc( "%s", init_name ); + + if ( conn->init_name == NULL ) { + 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; + } + + *init_port_name = iscsi_sprintf_alloc( "%s,i,0x%12.12" PRIx64, init_name, iscsi_connection_get_isid( &login_response_pkt->isid ) ); + + if ( *init_port_name == NULL ) { + 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; + + free( conn->init_name ); + conn->init_name = NULL; + + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + } + + return ISCSI_CONNECT_PDU_READ_OK; +} + +/** + * @brief Determines the session type of login. + * + * This function is used to retrieve the + * login session type and checks the + * relevant key and value pair for + * errors. + * + * @param[in] login_response_pdu Pointer to login response PDU, + * NULL is not allowed, so take caution. + * @param[in] key_value_pairs Pointer to key and value pairs which + * contain the session type parameter to be evaluated, + * which may NOT be NULL, so take caution. + * @param[out] type Pointer to integer which stores the + * determined session type and is NOT allowed to be + * NULL, so be careful. + * @return 0 on successful operation, a negative error code + * otherwise. The output session 'type' is unchanged, if + * an invalid session type value was retrieved. + */ +static int iscsi_connection_login_session_type(iscsi_pdu *login_response_pdu, iscsi_hashmap *key_value_pairs, int *type ) +{ + 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 ); + + if ( (rc == 0) && (type_str != NULL) ) { + if ( strcasecmp( (char *) type_str, "Discovery" ) == 0 ) { + *type = ISCSI_SESSION_TYPE_DISCOVERY; + } else if ( strcasecmp( (char *) type_str, "Normal" ) == 0 ) { + *type = ISCSI_SESSION_TYPE_NORMAL; + } else { + *type = ISCSI_SESSION_TYPE_INVALID; + + 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; + } + } else { + if ( login_response_pkt->tsih != 0 ) { + *type = ISCSI_SESSION_TYPE_NORMAL; + } else { + 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; + } + } + + return ISCSI_CONNECT_PDU_READ_OK; +} + +/** + * @brief Checks the target node info and sets login response PDU accordingly. + * + * This function also checks if the target node is + * redirected and if so, sets the response to the + * client response to the temporarily redirection + * URL.\n + * THe accessibility of the target node is + * also checked. + * + * @param[in] conn Pointer to iSCSI connection which may NOT be + * NULL, so be careful. + * @param[in] login_response_pdu Pointer to login response PDU + * to set the parameters for. NULL is NOT allowed + * here, so take caution. + * @param[in] target_name Pointer to target node name and may + * NOT be NULL, be careful. + * @param[out] Pointer where to store the target node belonging + * to the target name. May NOT be NULL, so take caution. + * @return 0 if the check was successful or a negative + * error code otherwise. + */ +static int iscsi_connection_login_check_target(iscsi_connection *conn, iscsi_pdu *login_response_pdu, uint8_t *target_name, iscsi_target_node **target) +{ + iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; + + *target = iscsi_target_node_find( target_name ); + + if ( *target == NULL ) { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_NOT_FOUND; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } + + if ( ((*target)->flags & ISCSI_TARGET_NODE_FLAGS_DESTROYED) != 0 ) { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_TARGET_REMOVED; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } + + uint8_t *redirect_adr = iscsi_target_node_get_redirect( conn, *target ); + + if ( redirect_adr != NULL ) { + if ( iscsi_add_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; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } + + 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; + } + + if ( iscsi_target_node_access( conn, *target, conn->init_name, conn->init_adr ) < 0 ) { + 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_FAIL; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } + + return ISCSI_CONNECT_PDU_READ_OK; +} + +/** + * @brief Retrieves iSCSI session by Target Session Identifying Handle (TSIH). + * + * This function checks if the TSIH is valid and if so, + * retrieves the pointer to its iSCSI session structure. + * + * @param[in] tsih Target Session Identifying Handle (TSIH). + * @return Pointer to related iSCSI session or NULL in case + * the TSIH is invalid or not found. + */ +static iscsi_session *iscsi_session_get_by_tsih(const uint16_t tsih) +{ + if ( tsih == 0 ) + return NULL; + + const uint64_t hash_key = tsih; + iscsi_session *session; + int rc = iscsi_hashmap_get( iscsi_globvec->sessions, (uint8_t *) &hash_key, sizeof(hash_key), (uint8_t **) &session ); + + return (rc == 0) ? session : NULL; +} + +/** + * @brief Appends an iSCSI connection to a session. + * + * This function checks if the maximum number of + * connections per session is not exceeded and if + * there is session spanning. + * @param[in] conn Pointer to iSCSI connection, may NOT + * be NULL, so be careful. + * @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) +{ + 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) ) + 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 ) + 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) ); + + 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 ); + + return (ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SUCCESS << 8U) | ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SUCCESS; +} + +/** + * @brief Checks whether the session is valid. + * + * This function also appends the connection + * to a session if it's valid.' + * + * @param[in] conn Pointer to iSCSI connection, + * may NOT be NULL, so take caution. + * @param[in] login_response_pdu Pointer to login response PDU, + * NULL is not allowed, hence be careful. + * @param[in] init_port_name Pointer to initiator port name. + * Non-NULL only, so be careful. + * @param[in] cid Connection ID (CID). + * @return 0 if valid session, a negative error code + * otherwise. + */ +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; + + 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 ( rc != 0 ) { + login_response_pkt->status_class = (uint8_t) (rc >> 8U); + login_response_pkt->status_detail = (uint8_t) rc; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } + } else if ( (iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_ISID_ALLOW_DUPLICATES) != 0 ) { + iscsi_connection_drop( conn, init_port_name, 0 ); + } + + return rc; +} + +/** + * @brief Initializes a rejecting login response packet. + * + * The login response structure has status detail + * invalid login request type set. + * + * @param[in] login_response_pdu Pointer to iSCSI login response PDU, + * NULL is an invalid value here, so take caution. + * @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) +{ + 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; +} + +/** + * @brief Creates an iSCSI PDU structure used by connections. + * + * The PDU structure is used for allowing partial + * reading from the TCP/IP socket and correctly + * 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. + * @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 *pdu = (iscsi_pdu *) malloc( sizeof(struct iscsi_pdu) ); + + if ( pdu == NULL ) { + logadd( LOG_ERROR, "iscsi_pdu_create: Out of memory while allocating iSCSI PDU" ); + + return NULL; + } + + pdu->bhs_pkt = iscsi_create_packet(); + + if ( pdu->bhs_pkt == NULL ) { + free( pdu ); + + return NULL; + } + + 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 ( 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->buf_len = 0UL; + pdu->conn = conn; + pdu->cmd_sn = 0UL; + + return pdu; +} + +/** + * @brief Destroys an iSCSI PDU structure used by connections. + * + * 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. + */ +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->bhs_pkt != NULL ) { + free( pdu->bhs_pkt ); + + pdu->bhs_pkt = NULL; + } + + free( pdu ); + } +} + +/** + * @brief Writes and sends a response PDU to the client. + * + * This function sends a response PDU to the + * client after being processed by the server.\n + * If a header or data digest (CRC32C) needs to + * be calculated, this is done as well. + * + * @param[in] conn iSCSI connection to handle. May + * NOT be NULL, so take caution. + * @param[in] pdu 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. + * May be NULL in case no further action is required. + * @param[in,out] user_data Data for the callback + * function. May be NULL if the callback function + * doesn't require additional data. + */ +void iscsi_connection_pdu_write(iscsi_connection *conn, iscsi_pdu *pdu, iscsi_connection_xfer_complete_callback callback, uint8_t *user_data) +{ + 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 ); + + if ( (conn->data_digest != 0) && (pdu->ds_len != 0) ) + iscsi_calc_data_digest( pdu->bhs_pkt, conn->header_digest ); + } + + 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); + + // TODO: Do the writing in a queue. + iscsi_connection_write( conn, (uint8_t *) pdu->bhs_pkt, len ); + + if ( callback != NULL ) + callback( user_data ); +} + +/** + * @brief Constructs and sends an iSCSI reject response to the client. + * + * This function constructs an reject response PDU with its + * packet data.\n + * The original rejected packet data is appended as DataSegment + * according by iSCSI standard specification. + * + * @param[in] conn Pointer to iSCSI connection for reject packet construction. + * @param[in] pdu Pointer to iSCSI source PDU which contains the rejected packet data. + * @param[in] reason_code Reason code for rejected packet data. + * @retval -1 An error occured during reject packet generation, + * currently only happens on memory exhaustion. + * @retval 0 Reject packet and PDU constructed and sent successfully to the client. + */ +static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu, const int reason_code) +{ + pdu->flags |= ISCSI_PDU_FLAGS_REJECTED; + + iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn ); + + if ( response_pdu == NULL ) { + logadd( LOG_ERROR, "iscsi_pdu_create: Out of memory while allocating iSCSI reject response PDU" ); + + return -1L; + } + + 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( pdu->bhs_pkt, conn->header_digest, ds_len, conn->data_digest ); + + if ( reject_pkt == NULL ) { + logadd( LOG_ERROR, "iscsi_pdu_create: Out of memory while allocating iSCSI reject packet data" ); + + iscsi_connection_pdu_destroy( response_pdu ); + + return -1L; + } + + 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; + 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 ); + } + + memcpy( ((uint8_t *) reject_pkt) + sizeof(struct iscsi_bhs_packet), pdu->bhs_pkt, ds_len ); + + iscsi_connection_pdu_write( conn, response_pdu, NULL, NULL ); + + return 0L; +} + +/** + * @brief Updates Command Sequence Number (CmdSN) of an incoming iSCSI PDU request. + * + * This function updates the Command Sequence + * Number (CmdSN) for incoming data sent by + * the client. + * + * @param[in] conn iSCSI connection to handle. May + * NOT be NULL, so take caution. + * @param[in] pdu 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. + + return 0L; +} + +/** + * @brief Handles an incoming iSCSI header login request PDU. + * + * This function handles login request header + * data sent by the client.\n + * If a response needs to be sent, this will + * be done as well. + * + * @param[in] conn iSCSI connection to handle. May + * NOT be NULL, so take caution. + * @param[in] pdu 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_login_req(iscsi_connection *conn, iscsi_pdu *pdu) +{ + if ( ((conn->flags & ISCSI_CONNECT_FLAGS_FULL_FEATURE) != 0) && (conn->session != NULL) && (conn->session->type == ISCSI_SESSION_TYPE_DISCOVERY) ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + + const iscsi_login_req_packet *login_req_pkt = (iscsi_login_req_packet *) pdu->bhs_pkt; + + pdu->cmd_sn = iscsi_get_be32(login_req_pkt->cmd_sn); + + 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 ); + + if ( login_response_pdu == NULL ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + + const int rc = iscsi_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 ); + + return ISCSI_CONNECT_PDU_READ_OK; + } + + conn->login_response_pdu = login_response_pdu; + + return ISCSI_CONNECT_PDU_READ_OK; +} + +/** + * @brief Handles an incoming iSCSI header NOP-Out request PDU. + * + * This function handles NOP-Out request header + * data sent by the client.\n + * If a response needs to be sent, this will + * be done as well. + * + * @param[in] conn iSCSI connection to handle. May + * NOT be NULL, so take caution. + * @param[in] pdu 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_nop_out(iscsi_connection *conn, iscsi_pdu *pdu) +{ + // TODO: Implement opcode. + + return 0; +} + +/** + * @brief Handles an incoming iSCSI header SCSI command request PDU. + * + * This function handles SCSI command request + * header data sent by the client.\n + * If a response needs to be sent, this will + * be done as well. + * + * @param[in] conn iSCSI connection to handle. May + * NOT be NULL, so take caution. + * @param[in] pdu 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. + + return 0; +} + +/** + * @brief Handles an incoming iSCSI header task management function request PDU. + * + * This function handles task management function + * request header data sent by the client.\n + * If a response needs to be sent, this will + * be done as well. + * + * @param[in] conn iSCSI connection to handle. May + * NOT be NULL, so take caution. + * @param[in] pdu 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_task_func_req(iscsi_connection *conn, iscsi_pdu *pdu) +{ + // TODO: Implement opcode. + + return 0; +} + +/** + * @brief Handles an incoming iSCSI header text request PDU. + * + * This function handles text request header + * data sent by the client.\n + * If a response needs to be sent, this will + * be done as well. + * + * @param[in] conn iSCSI connection to handle. May + * NOT be NULL, so take caution. + * @param[in] pdu 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. + + return 0; +} + +/** + * @brief Handles an incoming iSCSI header SCSI data out PDU. + * + * This function handles header SCSI data out + * sent by the client.\n + * If a response needs to be sent, this will + * be done as well. + * + * @param[in] conn iSCSI connection to handle. May + * NOT be NULL, so take caution. + * @param[in] pdu 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. + + return 0; +} + +/** + * @brief Handles an incoming iSCSI header logout request PDU. + * + * This function handles logout request header + * data sent by the client.\n + * If a response needs to be sent, this will + * be done as well. + * + * @param[in] conn iSCSI connection to handle. May + * NOT be NULL, so take caution. + * @param[in] pdu 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. + + return 0; +} + +/** + * @brief Handles an incoming iSCSI header SNACK request PDU. + * + * This function handles SNACK request header + * data sent by the client.\n + * If a response needs to be sent, this will + * be done as well. + * + * @param[in] conn iSCSI connection to handle. May + * NOT be NULL, so take caution. + * @param[in] pdu 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_snack_req(iscsi_connection *conn, iscsi_pdu *pdu) +{ + // TODO: Implement opcode. + + return 0; +} + +/** + * @brief Handles an incoming iSCSI header PDU. + * + * This function handles all header data sent + * by the client, including authentication.\n + * If a response needs to be sent, this will + * be done as well. + * + * @param[in] conn iSCSI connection to handle. May + * NOT be NULL, so take caution. + * @param[in] pdu 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(iscsi_connection *conn, iscsi_pdu *pdu) +{ + if ( pdu == NULL ) + return -1L; + + const int opcode = ISCSI_GET_OPCODE(pdu->bhs_pkt->opcode); + + if ( opcode == ISCSI_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 ); + + if ( login_response_pdu == NULL ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + + iscsi_login_response_reject_init( login_response_pdu, pdu ); + iscsi_connection_pdu_write( conn, login_response_pdu, NULL, NULL ); + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } else if ( conn->state == ISCSI_CONNECT_STATE_INVALID ) { + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + } + + int rc = iscsi_connection_update_cmd_sn( conn, pdu ); + + if ( rc != 0 ) + return rc; + + switch ( opcode ) { + case ISCSI_CLIENT_NOP_OUT : { + rc = iscsi_connection_pdu_header_handle_nop_out( conn, pdu ); + + break; + } + case ISCSI_CLIENT_SCSI_CMD : { + rc = iscsi_connection_pdu_header_handle_scsi_cmd( conn, pdu ); + + break; + } + case ISCSI_CLIENT_TASK_FUNC_REQ : { + rc = iscsi_connection_pdu_header_handle_task_func_req( conn, pdu ); + + break; + } + case ISCSI_CLIENT_TEXT_REQ : { + rc = iscsi_connection_pdu_header_handle_text_req( conn, pdu ); + + break; + } + case ISCSI_CLIENT_SCSI_DATA_OUT : { + rc = iscsi_connection_pdu_header_handle_scsi_data_out( conn, pdu ); + + break; + } + case ISCSI_CLIENT_LOGOUT_REQ : { + rc = iscsi_connection_pdu_header_handle_logout_req( conn, pdu ); + + break; + } + case ISCSI_CLIENT_SNACK_REQ : { + rc = iscsi_connection_pdu_header_handle_snack_req( conn, pdu ); + + break; + } + default : { + return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); + + break; + } + } + + return rc; +} + +/** + * @brief Handles an incoming iSCSI payload data NOP-Out request PDU. + * + * This function handles NOP-Out request payload + * data sent by the client.\n + * If a response needs to be sent, this will + * be done as well. + * + * @param[in] conn iSCSI connection to handle. May + * NOT be NULL, so take caution. + * @param[in] pdu 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_nop_out(iscsi_connection *conn, iscsi_pdu *pdu) +{ + // TODO: Implement opcode. + + return 0L; +} + +/** + * @brief Handles an incoming iSCSI payload data SCSI command request PDU. + * + * This function handles SCSI 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 iSCSI connection to handle. May + * NOT be NULL, so take caution. + * @param[in] pdu 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. + + return 0L; +} + +/** + * @brief Allocates and replaces a string value to a key / value hash map pair. + * + * This function allocates memory for a string key + * and its string value and replaces a original + * key and value pair if it exists. + * + * @param[in] conn Pointer to iSCSI connection to update the + * login key and value pair for and may NOT + * be NULL, so take care. + * @param[in] key_value_pairs Pointer to the hash map which should + * contain the replaced string key and value pair. NULL is NOT + * allowed here, so be careful. + * @param[in] key String containing the key name as string. May + * NOT be NULL, so take caution. + * @param[in] value String containing the value to be stored. + * @return 0 on successful operation, or a negative value on + * error (memory exhaustion). + */ +static int iscsi_connection_login_update_key_value_pair(iscsi_connection *conn, const uint8_t *key, const uint8_t *value) +{ + const uint key_len = (uint) strlen( (char *) key ) + 1; + uint8_t *hash_key = iscsi_hashmap_key_create( key, key_len ); + + if ( hash_key == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_login_update_key_value_pair: Out of memory allocating key." ); + + return -1L; + } + + const uint val_len = (uint) strlen( (char *) value ) + 1; + uint8_t *hash_val = (uint8_t *) malloc( val_len ); + + if ( hash_val == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_login_update_key_value_pair: Out of memory allocating string value." ); + + return -1L; + } + + memcpy( hash_val, value, val_len ); + + return iscsi_hashmap_put_free( conn->key_value_pairs, hash_key, key_len, hash_val, iscsi_hashmap_key_destroy_value_callback, NULL ); +} + +/** + * @brief Negotiates connection authentication method (none or CHAP). + * + * This function updates the key and value pairs if, and only if + * CHAP is either disabled or required. + * + * @param[in] conn Pointer to iSCSI connection to update the key + * and value pairs for. May NOT be NULL, so be careful. + * @return 0 on successful update, a negative error code otherwise. + */ +static int iscsi_connection_chap_negotiate(iscsi_connection *conn) +{ + int rc = 0; + + if ( (conn->flags & ISCSI_CONNECT_FLAGS_CHAP_DISABLE) != 0 ) + rc = iscsi_connection_login_update_key_value_pair( conn, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD, (uint8_t *) "None" ); + else if ( (conn->flags & ISCSI_CONNECT_FLAGS_CHAP_REQUIRE) != 0 ) + rc = iscsi_connection_login_update_key_value_pair( conn, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD, (uint8_t *) "CHAP" ); + + return rc; +} + +/** + * @brief Discovers iSCSI CHAP authentication session. + * + * This function copies over the global CHAP configuration + * into the iSCSI connection structure and then negotiates. + * + * @param[in] conn Pointer to iSCSI connection for iSCSI + * CHAP authentication discovery. May NOT be + * NULL, so be careful. + * @return 0 on successful negotiation, a negative error + * code otherwise. + */ +static int iscsi_connection_login_session_chap_discovery(iscsi_connection *conn) +{ + conn->flags &= ~(ISCSI_CONNECT_FLAGS_CHAP_DISABLE | ISCSI_CONNECT_FLAGS_CHAP_REQUIRE | ISCSI_CONNECT_FLAGS_CHAP_MUTUAL); + + if ( (iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_CHAP_DISABLE) != 0 ) + conn->flags |= ISCSI_CONNECT_FLAGS_CHAP_DISABLE; + + if ( (iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_CHAP_REQUIRE) != 0 ) + conn->flags |= ISCSI_CONNECT_FLAGS_CHAP_REQUIRE; + + if ( (iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_CHAP_MUTUAL) != 0 ) + conn->flags |= ISCSI_CONNECT_FLAGS_CHAP_MUTUAL; + + conn->chap_group = iscsi_globvec->chap_group; + + return iscsi_connection_chap_negotiate( conn ); +} + +/** + * @brief Negotiates CHAP authentication. + * + * This function updates the key and value pairs if, and only if + * CHAP authentication is either disabled or required in the + * target node. + * + * @param[in] conn Pointer to iSCSI connection to update the key + * and value pairs for. May NOT be NULL, so be careful. + * @param[in] target Pointer to iSCSI target node used to check + * the CHAP authentication. NULL is not allowed here, + * so take caution. + * @return 0 on successful update, a negative error code otherwise. + */ +static int iscsi_connection_login_chap_negotiate(iscsi_connection *conn, const iscsi_target_node *target) +{ + conn->flags &= ~(ISCSI_CONNECT_FLAGS_CHAP_DISABLE | ISCSI_CONNECT_FLAGS_CHAP_REQUIRE | ISCSI_CONNECT_FLAGS_CHAP_MUTUAL); + + if ( (target->flags & ISCSI_TARGET_NODE_FLAGS_CHAP_DISABLE) != 0 ) + conn->flags |= ISCSI_CONNECT_FLAGS_CHAP_DISABLE; + + if ( (target->flags & ISCSI_TARGET_NODE_FLAGS_CHAP_REQUIRE) != 0 ) + conn->flags |= ISCSI_CONNECT_FLAGS_CHAP_REQUIRE; + + if ( (target->flags & ISCSI_TARGET_NODE_FLAGS_CHAP_MUTUAL) != 0 ) + conn->flags |= ISCSI_CONNECT_FLAGS_CHAP_MUTUAL; + + conn->chap_group = target->chap_group; + + return iscsi_connection_chap_negotiate( conn ); +} + +/** + * @brief Negotiates connection header and data digest (CRC32C). + * + * This function updates the key and value pairs if, and only if + * header and data digests are enabled in the target node. + * + * @param[in] conn Pointer to iSCSI connection to update the key + * and value pairs for. May NOT be NULL, so be careful. + * @param[in] target Pointer to iSCSI target node used to check + * the digest status. NULL is not allowed here, so take + * caution. + * @return 0 on successful update, a negative error code otherwise. + */ +static int iscsi_connection_login_digest_negotiate(iscsi_connection *conn, const iscsi_target_node *target) +{ + if ( target->header_digest != 0 ) { + const int rc = iscsi_connection_login_update_key_value_pair( conn, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_HEADER_DIGEST, (uint8_t *) "CRC32C" ); + + return rc; + } + + if ( target->data_digest != 0 ) { + const int rc = iscsi_connection_login_update_key_value_pair( conn, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST, (uint8_t *) "CRC32C" ); + + return rc; + } + + return 0L; +} + +/** + * @brief Determines iSCSI session login steps for normal authentication. + * + * This function also does related validation checks. + * + * @param[in] conn Pointer to iSCSI connection, may NOT be + * NULL, so take caution. + * @param[in] login_response_pdu Pointer to login response PDU where + * NULL is not allowed, so be careful. + * @param[in] key_value_pairs Pointer to hash map containing the login + * request key and value pairs and may NOT be NULL, so + * take caution. + * @param[in] init_port_name Pointer to iSCSI initiator port name. NULL + * is NOT an allowed value, so be careful. + * @param[in] cid Connection ID (CID). + * @return 0 on successful operation, a negative error code + * otherwise. + */ +static int iscsi_connection_login_session_normal(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_hashmap *key_value_pairs, uint8_t *init_port_name, const uint cid) +{ + 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 ); + + if ( (rc < 0) || (target_name == NULL) ) { + 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; + } + + const uint8_t *target_name_short = (uint8_t *) strstr( (char *) target_name, ":" ); + + conn->target_name_short = iscsi_sprintf_alloc( "%s", ((target_name_short != NULL) ? ++target_name_short : target_name) ); + + if ( conn->target_name_short == NULL ) { + 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; + } + + rc = iscsi_connection_login_check_target( conn, login_response_pdu, target_name, &target ); + + if ( rc < 0 ) + return rc; + + conn->device = target->device; + conn->target = target; + conn->target_port = iscsi_device_find_port_by_portal_group_tag( target->device, conn->pg_tag ); + + rc = iscsi_connection_login_check_session( conn, login_response_pdu, init_port_name, cid ); + + if ( rc < 0 ) + return rc; + + rc = iscsi_connection_login_chap_negotiate( conn, target ); + + if ( rc == 0 ) + rc = iscsi_connection_login_digest_negotiate( conn, target ); + + if (rc != 0) { + 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; + } + + return rc; +} + +/* + * This function is used to set the info in the connection data structure + * return + * 0: success + * otherwise: error + */ +static int iscsi_connection_login_set_info(iscsi_connection *conn, iscsi_pdu *login_response_pdu, const uint8_t *init_port_name, const int type, const uint cid) +{ + conn->flags &= ~ISCSI_CONNECT_FLAGS_AUTH; + conn->auth_chap.phase = ISCSI_AUTH_CHAP_PHASE_WAIT_A; + conn->cid = (uint16_t) cid; + + if ( conn->session == NULL ) { + iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; + iscsi_target_node *target = conn->target; + const uint64_t isid = iscsi_connection_get_isid( &login_response_pkt->isid ); + iscsi_port *init_port = iscsi_port_create( init_port_name, isid, 0U ); + + if ( init_port == NULL ) { + 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_LOGIN_RESPONSE; + } + + conn->session = iscsi_session_create( conn, target, type ); + + if ( conn->session == NULL ) { + iscsi_port_destroy( init_port ); + + 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_LOGIN_RESPONSE; + } + + conn->session->initiator_port = init_port; + conn->stat_sn = iscsi_get_be32(login_response_pkt->stat_sn); + conn->session->isid = isid; + + const int rc = iscsi_port_transport_id_set( conn->session->initiator_port, conn->init_name, isid ); + + if ( rc < 0 ) { + iscsi_session_destroy( conn->session ); + conn->session = NULL; + + iscsi_port_destroy( init_port ); + + return rc; + } + + conn->session->queue_depth = (target != NULL) ? target->queue_depth : 1UL; + 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->init_port = conn->session->initiator_port; + + return ISCSI_CONNECT_PDU_READ_OK; +} + +/** + * @brief Sets iSCSI session target info key and value pairs. + * + * This function also sets the login response PDU + * key and value pairs. + * + * @param[in] conn Pointer to iSCSI connection of which + * the target info should be set, may NOT be NULL, + * so take caution. + * @param[in] login_response_pdu Pointer to login response PDU and + * NULL is not allowed here, so be careful. + * @param[in] type iSCSI session type. + * @return 0 on successfully setting target info, a + * negative error code otherwise. + */ +static int iscsi_connection_login_set_target_info(iscsi_connection *conn, iscsi_pdu *login_response_pdu, const int type) +{ + iscsi_target_node *target = conn->target; + int rc; + + if ( target != NULL ) { + rc = iscsi_add_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 *) "") ); + + 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 ); + + if ( tmp_buf == NULL ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + + rc = iscsi_add_key_value_pair( conn->session->key_value_pairs, (uint8_t *) 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_add_int_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG, 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 ); + + if ( (rc == 0) && (strlen( (char *) tmp_buf ) != 0) ) { + rc = iscsi_add_key_value_pair( login_response_pdu->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS, tmp_buf ); + + if ( rc < 0 ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + } + + 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 ); + + if ( (rc == 0) && (strlen( (char *) tmp_buf ) != 0) ) { + rc = iscsi_add_key_value_pair( login_response_pdu->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, tmp_buf ); + + if ( rc < 0 ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + } + } + + 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 ); + + if ( rc == 0 ) { + rc = iscsi_add_key_value_pair( login_response_pdu->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG, tmp_buf ); + + if ( rc < 0 ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + } + } + + return ISCSI_CONNECT_PDU_READ_OK; +} + +/** + * @brief Handles iSCSI connection login phase none. + * + * This function negotiates the login phase + * without a session. + * + * @param[in] conn Pointer to iSCSI connection, + * may NOT be NULL, so be careful. + * @param[in] login_response_pdu Pointer to login response PDU. + * NULL is not allowed here, so take caution. + * @param[in] key_value_pairs Pointer to key and value pairs. + * which may NOT be NULL, so take caution. + * @param[in] cid Connection ID (CID). + * @return 0 on success, a negative error code otherwise. + */ +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; + iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; + + conn->device = NULL; + conn->target = NULL; + + int rc = iscsi_connection_login_init_port( conn, login_response_pdu, key_value_pairs, &init_port_name ); + + if ( rc < 0 ) + return rc; + + int type; + + rc = iscsi_connection_login_session_type( login_response_pdu, key_value_pairs, &type ); + + if ( rc < 0 ) + 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 ); + } else if ( type == ISCSI_SESSION_TYPE_DISCOVERY ) { + login_response_pkt->tsih = 0U; + + rc = iscsi_connection_login_session_chap_discovery( conn ); + } else { + 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; + } + + if ( rc < 0 ) + return rc; + + rc = iscsi_connection_login_set_info( conn, login_response_pdu, init_port_name, type, cid ); + + if ( rc < 0 ) + return rc; + + if ( type == ISCSI_SESSION_TYPE_DISCOVERY ) { + conn->session->max_conns = 1; + + 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 ); + + if ( rc < 0 ) + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER; + } + + return iscsi_connection_login_set_target_info( conn, login_response_pdu, type ); +} + +/** + * @brief Handles iSCSI connection login response. + * + * This function negotiates the login parameters + * and determines the authentication method. + * + * @param[in] conn Pointer to iSCSI connection, + * may NOT be NULL, so be careful. + * @param[in] login_response_pdu Pointer to login response PDU. + * NULL is not allowed here, so take caution. + * @param[in] key_value_pairs Pointer to key and value pairs. + * which may NOT be NULL, so take caution. + * @return 0 on success, a negative error code otherwise. + */ +static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_hashmap *key_value_pairs) +{ + // TODO: Implement function. + + return 0L; +} + +/** + * @brief Handles an incoming iSCSI payload data login request PDU. + * + * This function handles login request payload + * data sent by the client.\n + * If a response needs to be sent, this will + * be done as well. + * + * @param[in] conn iSCSI connection to handle. May + * NOT be NULL, so take caution. + * @param[in] pdu 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_login_req(iscsi_connection *conn, iscsi_pdu *pdu) +{ + iscsi_pdu *login_response_pdu = (iscsi_pdu *) conn->login_response_pdu; + + if ( login_response_pdu == NULL ) + return 0L; + + iscsi_hashmap *key_value_pairs = iscsi_hashmap_create( 32UL ); + + if ( key_value_pairs == NULL ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + + iscsi_login_req_packet *login_req_pkt = (iscsi_login_req_packet *) pdu->bhs_pkt; + uint cid = iscsi_get_be16(login_req_pkt->cid); + int rc = iscsi_connection_save_incoming_key_value_pairs( conn, key_value_pairs, login_response_pdu, pdu ); + + if ( rc < 0 ) { + iscsi_connection_pdu_login_response( conn, login_response_pdu, NULL, iscsi_connection_pdu_login_err_complete ); + + return 0L; + } + + if ( conn->state == ISCSI_CONNECT_STATE_INVALID ) { + rc = iscsi_connection_handle_login_phase_none( conn, login_response_pdu, key_value_pairs, cid ); + + 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; + } + } + + rc = iscsi_connecction_handle_login_response( conn, login_response_pdu, key_value_pairs ); + + 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; + } + + 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; +} + +/** + * @brief Handles an incoming iSCSI payload data text request PDU. + * + * This function handles text request payload + * data sent by the client.\n + * If a response needs to be sent, this will + * be done as well. + * + * @param[in] conn iSCSI connection to handle. May + * NOT be NULL, so take caution. + * @param[in] pdu 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. + + return 0L; +} + +/** + * @brief Handles an incoming iSCSI payload data SCSI data out request PDU. + * + * This function handles SCSI data out request + * payload data sent by the client.\n + * If a response needs to be sent, this will + * be done as well. + * + * @param[in] conn iSCSI connection to handle. May + * NOT be NULL, so take caution. + * @param[in] pdu 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_data_out(iscsi_connection *conn, iscsi_pdu *pdu) +{ + // TODO: Implement opcode. + + return 0L; +} + +/** + * @brief Handles an incoming iSCSI payload data PDU. + * + * This function handles all payload data sent + * by the client.\n + * If a response needs to be sent, this will + * be done as well. + * + * @param[in] conn iSCSI connection to handle. May + * NOT be NULL, so take caution. + * @param[in] pdu 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(iscsi_connection *conn, iscsi_pdu *pdu) +{ + int rc = 0; + + const uint8_t opcode = ISCSI_GET_OPCODE(pdu->bhs_pkt->opcode); + + switch ( opcode ) { + case ISCSI_CLIENT_NOP_OUT : { + rc = iscsi_connection_pdu_data_handle_nop_out( conn, pdu ); + + break; + } + case ISCSI_CLIENT_SCSI_CMD : { + rc = iscsi_connection_pdu_data_handle_scsi_cmd( conn, pdu ); + + break; + } + case ISCSI_CLIENT_LOGIN_REQ : { + rc = iscsi_connection_pdu_data_handle_login_req( conn, pdu ); + + break; + } + case ISCSI_CLIENT_TEXT_REQ : { + rc = iscsi_connection_pdu_data_handle_text_req( conn, pdu ); + + break; + } + case ISCSI_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 : { + break; + } + default : { + return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); + + break; + } + } + + return rc; +} + +/** + * @brief Retrieves and merges splitted iSCSI PDU data read from TCP/IP socket. + * + * This function handles partial reads of data + * packet.\n + * Since iSCSI data can span multiple packets, not + * only by TCP/IP itself, but also by iSCSI protocol + * specifications, multiple calls are needed in order + * 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. + * @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. + + return 0; +} + +/** + * @brief Retrieves and merges splitted iSCSI PDU data read from TCP/IP socket. + * + * This function handles partial reads of BHS, AHS + * and DS packet data.\n + * Since iSCSI data can span multiple packets, not + * only by TCP/IP itself, but also by iSCSI protocol + * specifications, multiple calls are needed in order + * to be sure that all data packets have been + * received. + * + * @param[in] conn 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. + */ +static int iscsi_connection_pdu_read(iscsi_connection *conn) +{ + int prev_recv_state; + + do { + iscsi_pdu *pdu = conn->pdu_processing; + + prev_recv_state = conn->pdu_recv_state; + + switch ( conn->pdu_recv_state ) { + case ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_READY : { + conn->pdu_processing = iscsi_connection_pdu_create( conn ); + + if ( conn->pdu_processing == NULL ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + + conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_HDR; + + 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 ( len < 0 ) { + conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; + + break; + } + + pdu->bhs_read_len += len; + + if ( pdu->bhs_read_len < sizeof(struct iscsi_bhs_packet) ) + return ISCSI_CONNECT_PDU_READ_OK; + } + + if ( (conn->flags & ISCSI_CONNECT_FLAGS_LOGGED_OUT) != 0 ) { + conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; + + break; + } + + pdu->ds_len = iscsi_align(pdu->ds_len, ISCSI_ALIGN_SIZE); + pdu->buf_len = pdu->ds_len; + + const uint ahs_len = (uint) pdu->bhs_pkt->total_ahs_len << 2UL; + + 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 ( pdu->ahs_pkt == NULL ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + + pdu->ahs_pkt = (iscsi_ahs_packet *) (((iscsi_bhs_packet *) pdu->bhs_pkt) + 1); + } + + const int len = iscsi_connection_read( conn, (((uint8_t *) pdu->ahs_pkt) + pdu->ahs_read_len), (ahs_len - pdu->ahs_read_len) ); + + if ( len < 0 ) { + conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; + + break; + } + + pdu->ahs_read_len += len; + + if ( pdu->ahs_read_len < 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 ) + 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 ) { + conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; + + break; + } + + pdu->header_digest_read_len += len; + + if ( pdu->header_digest_read_len < (uint) conn->header_digest ) + return ISCSI_CONNECT_PDU_READ_OK; + } + + if ( iscsi_validate_header_digest( pdu->bhs_pkt ) == 0 ) { + 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; + + break; + } + case ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_DATA : { + if ( pdu->ds_len != 0 ) { + const int len = iscsi_connection_pdu_data_read( conn, pdu ); + + if ( len < 0 ) { + conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; + + break; + } else if ( len > 0 ) { + return ISCSI_CONNECT_PDU_READ_OK; + } + } + + int rc; + + if ( (conn->flags & ISCSI_CONNECT_FLAGS_REJECTED) != 0 ) + rc = 0; + else + rc = iscsi_connection_pdu_data_handle( conn, pdu ); + + if ( rc == 0 ) { + iscsi_connection_pdu_destroy( pdu ); + + conn->pdu_processing = NULL; + conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_READY; + + return ISCSI_CONNECT_PDU_READ_PROCESSED; + } else { + conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; + } + + break; + } + case ISCSI_CONNECT_PDU_RECV_STATE_ERR : { + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + + break; + } + default : { + logadd( LOG_ERROR, "iscsi_connection_pdu_read: Fatal error reading, unknown packet status. Should NEVER happen! Please report this bug to the developer" ); + + break; + } + } + } while ( prev_recv_state != conn->pdu_recv_state ); + + return 0; +} + +#define ISCSI_PDU_HANDLE_COUNT 16 + +/** + * @brief Handle 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. + * @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; + + for ( i = 0; i < ISCSI_PDU_HANDLE_COUNT; i++ ) { + int rc = iscsi_connection_pdu_read(conn); + + if ( rc == 0 ) + break; + else if ( rc < 0 ) + return rc; + + if ( (conn->flags & ISCSI_CONNECT_FLAGS_STOPPED) != 0 ) + break; + } + + return i; +} diff --git a/src/server/iscsi.h b/src/server/iscsi.h index 690166b..56e09f9 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -36,7 +36,7 @@ #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) -#define iscsi_get_be24(x) ((x) & 0xFFFFFFUL) +#define iscsi_get_be24(x) (iscsi_get_be32((*(uint32_t *) ((uint8_t *) x - 1))) & 0xFFFFFFUL) #define iscsi_get_be32(x) (x) #define iscsi_get_be64(x) (x) @@ -64,18 +64,20 @@ static inline void iscsi_put_be64(uint8_t *data, const uint64_t val) #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) // GCC or CLang #define iscsi_get_be16(x) (__builtin_bswap16(x)) -#define iscsi_get_be24(x) (iscsi_get_be32(x) & 0xFFFFFFUL) +#define iscsi_get_be24(x) (iscsi_get_be32((*(uint32_t *) ((uint8_t *) x - 1))) & 0xFFFFFFUL) #define iscsi_get_be32(x) (__builtin_bswap32(x)) #define iscsi_get_be64(x) (__builtin_bswap64(x)) #elif defined(_MSC_VER) #include // MVSC #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)) #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)) +#define iscsi_get_be24(x) (iscsi_get_be32((*(uint32_t *) ((uint8_t *) x - 1))) & 0xFFFFFFUL) #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 @@ -123,6 +125,7 @@ uint8_t *iscsi_sprintf_append_realloc(char *buf, const char *format, ...); // Al 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 + /// Shift factor for default capacity. #define ISCSI_HASHMAP_DEFAULT_CAPACITY_SHIFT 5UL @@ -138,11 +141,13 @@ uint8_t *iscsi_sprintf_alloc(const char *format, ... ); // Allocates a buffer an /// Key data size must be multiple of 8 bytes by now. #define ISCSI_HASHMAP_KEY_ALIGN (1UL << (ISCSI_HASHMAP_KEY_ALIGN_SHIFT)) + /// Initial hash code. #define ISCSI_HASHMAP_HASH_INITIAL 0x811C9DC5UL /// Value to multiply hash code with. -#define ISCSI_HASHMAP_HASH_MUL 0xBF58476D1CE4E5B9ULL +#define ISCSI_HASHMAP_HASH_MUL 0xBF58476D1CE4E5B9ULL + /** * @brief Hash map bucket containing key, value and hash code. @@ -221,25 +226,28 @@ typedef struct iscsi_hashmap { * 0 means successful operation and a positive value * indicates a non-fatal error or a warning. */ -typedef int (*iscsi_hashmap_callback)(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // A Callback for iterating over map, freeing and removing entries. - // user_data is free for personal use +typedef int (*iscsi_hashmap_callback)(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); iscsi_hashmap *iscsi_hashmap_create(const uint capacity); // Creates an empty hash map with either specified or default capacity void iscsi_hashmap_destroy(iscsi_hashmap *map); // Deallocates the hash map objects and buckets, not elements // 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_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, const 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, const 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, const 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 + +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_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. -int iscsi_hashmap_size(iscsi_hashmap *map); // Retrieves the number of elements of the hash map, ignoring elements marked for removal +uint iscsi_hashmap_size(const iscsi_hashmap *map); // Retrieves the number of elements of the hash map, ignoring elements marked for removal + 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 @@ -248,12 +256,24 @@ int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, u /// iSCSI Basic Header Segment size. #define ISCSI_BHS_SIZE 48UL + +/// iSCSI Default receive DataSegment (DS) size in bytes. +#define ISCSI_DEFAULT_RECV_DS_LEN 8192UL + +/// iSCSI default maximum DataSegment receive length in bytes +#define ISCSI_DEFAULT_MAX_RECV_DS_LEN 65536UL + +/// iSCSI default maximum DataSegment receive length in bytes +#define ISCSI_DEFAULT_MAX_DATA_OUT_PER_CONNECTION 16UL + + /// iSCSI header and data digest size (CRC32C). #define ISCSI_DIGEST_SIZE 4UL /// iSCSI packet data alignment (BHS, AHS and DataSegment). #define ISCSI_ALIGN_SIZE 4UL + /// Current minimum iSCSI protocol version supported by this implementation. #define ISCSI_VERSION_MIN 0 @@ -4197,13 +4217,16 @@ typedef struct __attribute__((packed)) iscsi_isid { #define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IF_MARK_INT "IFMarkInt" -/// Login request flags: SecurityNegotiation. +/// Login request Next Stage (NSG) flags: SecurityNegotiation. #define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION 0x0 -/// Login request flags: LoginOperationalNegotiation. +/// Login request Next Stage (NSG) flags: LoginOperationalNegotiation. #define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 -/// Login request flags: FullFeaturePhase. +/// Login request Next Stage (NSG) flags: Reserved for future usage, may NOT be used. +#define ISCSI_LOGIN_REQ_FLAGS_BEXT_STAGE_RESERVED 0x2 + +/// Login request Next Stage (NSG) flags: FullFeaturePhase. #define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_FULL_FEATURE_PHASE 0x3 /** @@ -4215,19 +4238,36 @@ 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 (1 << 0) +#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_FIRST_BIT 0 + +/** + * @brief Login request flags: Next Stage (NSG): Second bit of the two bits. + * + * The Login negotiation requests and responses are associated + * with a specific stage in the session (SecurityNegotiation,\n + * LoginOperationalNegotiation, FullFeaturePhase) and may indicate the + * 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) + +/// 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))) /// Login request flags: Extracts the Next Stage (NSG) bits. -#define ISCSI_LOGIN_REQS_FLAGS_GET_NEXT_STAGE(x) ((x) & 3) +#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) -/// Login request flags: SecurityNegotiation. +/// Login request Current Stage (CSG) flags: SecurityNegotiation. #define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_SECURITY_NEGOTIATION 0x0 -/// Login request flags: LoginOperationalNegotiation. +/// Login request Current Stage (CSG) flags: LoginOperationalNegotiation. #define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 -/// Login request flags: FullFeaturePhase. +/// Login request Current Stage (CSG) flags: Reserved for future usage, may NOT be used. +#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_RESERVED 0x2 + +/// Login request Current Stage (CSG) flags: FullFeaturePhase. #define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_FULL_FEATURE_PHASE 0x3 /** @@ -4238,10 +4278,23 @@ 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 (1 << 2) +#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_FIRST_BIT 2 + +/** + * @brief Login request flags: Current Stage (CSG): Second 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) + +/// 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))) /// Login request flags: Extracts the Current Stage (CSG) bits. -#define ISCSI_LOGIN_REQS_FLAGS_GET_CURRENT_STAGE(x) (((x) >> 2) & 3) +#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) /** @@ -4396,13 +4449,16 @@ typedef struct __attribute__((packed)) iscsi_login_req_packet { } iscsi_login_req_packet; -/// Login response flags: SecurityNegotiation. +/// Login response Next Stage (NSG) flags: SecurityNegotiation. #define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION 0x0 -/// Login response flags: LoginOperationalNegotiation. +/// Login response Next Stage (NSG) flags: LoginOperationalNegotiation. #define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 -/// Login response flags: FullFeaturePhase. +/// Login response Next Stage (NSG) flags: Reserved for future usage, may NOT be used. +#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_RESERVED 0x2 + +/// Login response Next Stage (NSG) flags: FullFeaturePhase. #define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FULL_FEATURE_PHASE 0x3 /** @@ -4414,18 +4470,37 @@ 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 (1 << 0) +#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FIRST_BIT 0 + +/** + * @brief Login response flags: Next Stage (NSG): First bit of the two bits. + * + * The Login negotiation requests and responses are associated + * with a specific stage in the session (SecurityNegotiation, + * LoginOperationalNegotiation, FullFeaturePhase) and may indicate the + * 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) + +/// 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))) /// Login response flags: Extracts the Next Stage (NSG) bits. -#define ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(x) ((x) & 3) +#define ISCSI_LOGIN_RESPONSES_FLAGS_GET_NEXT_STAGE(x) (((x) & ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_MASK) >> ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FIRST_BIT) -/// Login response flags: SecurityNegotiation. + + +/// Login response Current Stage (CSG) flags: SecurityNegotiation. #define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_SECURITY_NEGOTIATION 0x0 -/// Login response flags: LoginOperationalNegotiation. +/// Login response Current Stage (CSG) flags: LoginOperationalNegotiation. #define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 -/// Login response flags: FullFeaturePhase. +/// Login response Current Stage (CSG) flags: Reserved for future usage, may NOT be used. +#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_RESERVED 0x2 + +/// Login response Current Stage (CSG) flags: FullFeaturePhase. #define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FULL_FEATURE_PHASE 0x3 /** @@ -4436,10 +4511,24 @@ 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 (1 << 2) +#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FIRST_BIT 2 + +/** + * @brief Login response flags: Current Stage (CSG): First 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_RESPONSE_FLAGS_CURRENT_STAGE_SECOND_BIT ((ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FIRST_BIT) + 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))) + +/// Login request flags: Extracts the Current Stage (CSG) bits. +#define ISCSI_LOGIN_RESPONSES_FLAGS_GET_CURRENT_STAGE(x) (((x) & ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_MASK) >> ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FIRST_BIT) -/// Login response flags: Extracts the Current Stage (CSG) bits. -#define ISCSI_LOGIN_RESPONSE_FLAGS_GET_CURRENT_STAGE(x) (((x) >> 2) & 3) /** * @brief Login response flags: Continue. @@ -5520,6 +5609,28 @@ typedef struct __attribute__((packed)) iscsi_nop_in_packet { } iscsi_nop_in_packet; +/// iSCSI SCSI transport ID protocol identifier. +#define ISCSI_TRANSPORT_ID_PROTOCOL_ID_ISCSI 0x05 + +/// iSCSI SCSI transport ID format. +#define ISCSI_TRANSPORT_ID_FORMAT 0x01 + + +typedef struct __attribute__((packed)) iscsi_transport_id { + /// First 4 bits are protocol ID and last 2 bits are format. + uint8_t id; + + /// Reserved for future usage (always MUST be 0). + uint8_t reserved; + + /// Additional length of name. + uint16_t add_len; + + /// Name. + uint8_t name[0]; +} 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 @@ -5550,14 +5661,21 @@ typedef struct __attribute__((packed)) iscsi_nop_in_packet { 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 @@ -5630,9 +5748,502 @@ typedef struct iscsi_key_value_pair_packet { uint len; } iscsi_key_value_pair_packet; -int iscsi_parse_key_value_pairs(iscsi_hashmap *pairs, const uint8_t *packet_data, uint len, int cbit, uint8_t **partial_pair); // Extracts all text key / value pairs out of an iSCSI packet into a 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); // Extracts all text key / value pairs out of an iSCSI packet into a hash map int iscsi_create_key_value_pair_packet_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Creates a single partial iSCSI packet stream out of a single text key and value pair -iscsi_key_value_pair_packet *iscsi_create_key_value_pairs_packet(const iscsi_hashmap *pairs); // Creates a properly aligned iSCSI packet DataSegment out of a hash map containing text key and value pairs +iscsi_key_value_pair_packet *iscsi_create_key_value_pairs_packet(iscsi_hashmap *key_value_pairs); // Creates a properly aligned iSCSI packet DataSegment out of a hash map containing text key and value pairs + + +/// iSCSI main global data flags: Allow duplicate ISIDs. +#define ISCSI_GLOBALS_FLAGS_ISID_ALLOW_DUPLICATES (1 << 0L) + +/// iSCSI main global data flags: CHAP authentication is disabled. +#define ISCSI_GLOBALS_FLAGS_CHAP_DISABLE (1 << 1L) + +/// iSCSI main global data flags: CHAP authentication is required. +#define ISCSI_GLOBALS_FLAGS_CHAP_REQUIRE (1 << 2L) + +/// iSCSI main global data flags: CHAP authentication is mutual. +#define ISCSI_GLOBALS_FLAGS_CHAP_MUTUAL (1 << 3L) + + +/** + * @brief This is the main global iSCSI structure which manages all global data. + * + * All iSCSI portal groups, target nodes, sessions and + * connections are stored here for global access. + */ +typedef struct iscsi_globals { + /// Hash map containing all iSCSI devices. + iscsi_hashmap *devices; + + /// Hash map containing all registered iSCSI portal groups. + iscsi_hashmap *portal_groups; + + /// iSCSI target nodes. + iscsi_hashmap *target_nodes; + + /// Hash map containing all iSCSI sessions. + iscsi_hashmap *sessions; + + /// Hash map containing connections not associated with an iSCSI sessions. + iscsi_hashmap *connections; + + /// Global flags. + int flags; + + /// Maximum number of allowed sessions. + uint max_sessions; + + /// CHAP group id. + int32_t chap_group; +} iscsi_globals; + + +/// iSCSI vector for global access. +iscsi_globals *iscsi_globvec = NULL; + + +/** + * @brief iSCSI portal group: Private portal group if set, public otherwise. + * + * When redirecting logins, there are two portal group types: public and + * private.\n + * Public portal groups return their portals during discovery session. + * A redirection private portal may also be specified for non-discovery + * logins.\n + * Private portal groups instead do not return their portals during + * the discovery session. + */ +#define ISCSI_PORTAL_GROUP_PRIVATE (1 << 0L) + +/// iSCSI portal group: CHAP authentication is disabled. +#define ISCSI_PORTAL_GROUP_CHAP_DISABLE (1 << 1L) + +/// iSCSI portal group: CHAP authentication is required. +#define ISCSI_PORTAL_GROUP_CHAP_REQUIRE (1 << 2L) + +/// iSCSI portal group: CHAP authentication is mutual. +#define ISCSI_PORTAL_GROUP_CHAP_MUTUAL (1 << 3L) + + +/** + * @brief iSCSI portal group. + * + * Portal groups are either public or private and also are used + * by CHAP authentication. + */ +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; + + /// Portal group flags. + int flags; + + /// CHAP group id. + int32_t chap_group; +} iscsi_portal_group; + + +/** + * @brief iSCSI portal. + * + * iSCSI portals manage the host / IP address and port, as well + * as the associated connections. + */ +typedef struct iscsi_portal { + /// Group this portal belongs to. + iscsi_portal_group *group; + + /// Hostname / IP address of the portal. + uint8_t *host; + + /// Port of the portal. + uint8_t *port; + + /// TCP/IP socket for the portal. + int sock; +} iscsi_portal; + + +iscsi_portal_group *iscsi_portal_group_create(const int tag, const int flags); // Creates and initializes an iSCSI portal group +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 + +iscsi_portal *iscsi_portal_create(const uint8_t *host, const uint8_t *port); // Allocates and initializes an iSCSI portal structure +void iscsi_portal_destroy(iscsi_portal *portal); + + +/// ISCSI port flags: In use. +#define ISCSI_PORT_FLAGS_IN_USE (1 << 0L) + + +/** + * @brief iSCSI port. + * + * This structure maintains the transport ID, + * name, identifiers and index of an ISCSI + * port. + */ +typedef struct iscsi_port { + /// Transport ID. + iscsi_transport_id *transport_id; + + /// Name. + uint8_t *name; + + /// Identifier. + uint64_t id; + + /// Flags. + int flags; + + /// Index. + uint16_t index; + + /// Transport ID length. + uint16_t transport_id_len; +} 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 +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 device flags: Allocated. +#define ISCSI_DEVICE_FLAGS_ALLOCATED (1 << 0L) + +/// iSCSI device flags: Removed. +#define ISCSI_DEVICE_FLAGS_REMOVED (1 << 1L) + + +/** + * @brief iSCSI device. + * + * This structure managesw the SCSI + * devices and associates the + * disk image files with them. + */ +typedef struct iscsi_device { + /// Name of device. + uint8_t *name; + + /// LUNs associated with this device. + iscsi_hashmap *luns; + + /// Ports associated with this device. + iscsi_hashmap *ports; + + /// Device identifier. + int id; + + /// Flags. + int flags; + + /// Number of ports. + int num_ports; + + /// Portocol 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 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) + +/// iSCSI target node flags: CHAP authentication required. +#define ISCSI_TARGET_NODE_FLAGS_CHAP_REQUIRE (1 << 3L) + +/// iSCSI target node flags: CHAP authentication mutual. +#define ISCSI_TARGET_NODE_FLAGS_CHAP_MUTUAL (1 << 4L) + +/// iSCSI target node flags: Destroyed. +#define ISCSI_TARGET_NODE_FLAGS_DESTROYED (1 << 5L) + + +/** + * @brief iSCSI target node. + * + * This structure maintains the name, alias, + * associated device and connection data + * for a specific iSCSI target node. + */ +typedef struct iscsi_target_node { + /// Name of target node. + uint8_t *name; + + /// Alias name of target node. + uint8_t *alias; + + /// Associated iSCSI device. + iscsi_device *device; + + /// Target node number. + uint num; + + /// Queue depth. + uint queue_depth; + + /// Flags. + int flags; + + /// 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). + int data_digest; + + /// CHAP group ID. + int32_t chap_group; + + /// Number of active connections for this target node. + uint32_t active_conns; +} iscsi_target_node; + + +/** + * @brief iSCSI target node search by name. + * + * This structure is used by iterating through + * all iSCSI target nodes finding by name. + */ +typedef struct iscsi_target_node_find_name { + /// Found iSCSI target node is stored here, should be initialized to NULL. + iscsi_target_node *target; + + /// The name of the target node to search for. + uint8_t *name; +} iscsi_target_node_find_name; + + +/// iSCSI authentication CHAP phase: None. +#define ISCSI_AUTH_CHAP_PHASE_NONE 0L + +/// iSCSI authentication CHAP phase: Wait A. +#define ISCSI_AUTH_CHAP_PHASE_WAIT_A 1L + +/// iSCSI authentication CHAP phase: Wait NR. +#define ISCSI_AUTH_CHAP_PHASE_WAIT_NR 2L + +/// iSCSI authentication CHAP phase: End. +#define ISCSI_AUTH_CHAP_PHASE_END 3L + + +/** + * @brief iSCSI CHAP authentication data structure. + * + * This structure maintains all data required for + * CHAP authentication method. + */ +typedef struct iscsi_auth_chap { + /// CHAP phase. + int phase; +} iscsi_auth_chap; + + +/// iSCSI session: Default maximum number of connections. +#define ISCSI_SESSION_DEFAULT_MAX_CONNECTIONS 2UL + +/// iSCSI session: Default maximum number of outstanding ready to transfers. +#define ISCSI_SESSION_DEFAULT_MAX_OUTSTANDING_R2T 1UL + +/// iSCSI session: Default time to wait in seconds. +#define ISCSI_SESSION_DEFAULT_TIME_TO_WAIT 2UL + +/// 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 type: Invalid. +#define ISCSI_SESSION_TYPE_INVALID 0L + +/// iSCSI session type: Normal. +#define ISCSI_SESSION_TYPE_NORMAL 1L + +/// iSCSI session type: Discovery. +#define ISCSI_SESSION_TYPE_DISCOVERY 2L + + +/** + * @brief iSCSI session. + * + * This structure manages an iSCSI session and + * stores the key / value pairs from the + * login phase. + */ +typedef struct iscsi_session { + /// TCP/IP Connections associated with this session. + iscsi_hashmap *connections; + + /// Initiator port. + iscsi_port *initiator_port; + + /// Login key / value pairs negotiated with this session. + iscsi_hashmap *key_value_pairs; + + /// Portal group tag. + int tag; + + /// Initiator Session ID (ISID). + uint64_t isid; + + /// Target Session Identifying Handle (TSIH). + uint16_t tsih; + + /// iSCSI target node. + iscsi_target_node *target; + + /// Queue depth. + uint queue_depth; + + /// iSCSI session type. + int type; + + /// Maximum number of connections. + uint max_conns; + + /// Ready to transfer maximum outstanding value. + uint max_outstanding_r2t; + + /// Default time to wait. + uint default_time_to_wait; + + /// Default time to retain. + uint default_time_to_retain; + + /// First burst length. + uint 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; + + /// Error recovery level. + uint err_recovery_level; + + /// ExpCmdSN. + uint32_t exp_cmd_sn; + + /// MaxCmdSN. + uint32_t max_cmd_sn; + + /// Current text Initiator Task Tag (ITT). + uint32_t current_text_init_task_tag; +} iscsi_session; + + +/// iSCSI connection read packet data return code from iscsi_connection_pdu_read function: Packet parsed successfully. +#define ISCSI_CONNECT_PDU_READ_OK 0L + +/// iSCSI connection read packet data return code from iscsi_connection_pdu_read function: Packet processed successfully. +#define ISCSI_CONNECT_PDU_READ_PROCESSED 1L + +/// 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 + +/// 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 + +/// 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 + + +/// iSCSI connection flags: Stopped. +#define ISCSI_CONNECT_FLAGS_STOPPED (1 << 0L) + +/// iSCSI connection flags: Rejected. +#define ISCSI_CONNECT_FLAGS_REJECTED (1 << 1L) + +/// iSCSI connection flags: Logged out. +#define ISCSI_CONNECT_FLAGS_LOGGED_OUT (1 << 2L) + +/// iSCSI connection flags: Full feature. +#define ISCSI_CONNECT_FLAGS_FULL_FEATURE (1 << 3L) + +/// iSCSI connection flags: CHAP authentication is disabled. +#define ISCSI_CONNECT_FLAGS_CHAP_DISABLE (1 << 4L) + +/// iSCSI connection flags: CHAP authentication is required. +#define ISCSI_CONNECT_FLAGS_CHAP_REQUIRE (1 << 5L) + +/// iSCSI connection flags: CHAP authentication is mutual. +#define ISCSI_CONNECT_FLAGS_CHAP_MUTUAL (1 << 6L) + +/// iSCSI connection flags: Authenticated. +#define ISCSI_CONNECT_FLAGS_AUTH (1 << 7L) + + +/// Ready to wait for PDU. +#define ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_READY 0L + +/// Active connection waiting for any PDU header. +#define ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_HDR 1L + +/// Active connection waiting for data. +#define ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_DATA 2L + +/// Active connection does not wait for data. +#define ISCSI_CONNECT_PDU_RECV_STATE_ERR 3L + + +/// iSCSI connection state: Invalid. +#define ISCSI_CONNECT_STATE_INVALID 0L + +/// iSCSI connection state: Running. +#define ISCSI_CONNECT_STATE_RUNNING 1L + +/// iSCSI connection state: Exiting. +#define ISCSI_CONNECT_STATE_EXITING 2L + +/// iSCSI connection state: Invalid. +#define ISCSI_CONNECT_STATE_EXITED 3L + /** * @brief iSCSI incoming connection. @@ -5643,16 +6254,71 @@ iscsi_key_value_pair_packet *iscsi_create_key_value_pairs_packet(const iscsi_has * and iSCSI portals. */ typedef struct iscsi_connection { + /// iSCSI session associated with this connection. + iscsi_session *session; + /// Hash map containing text key / value pairs associated to this connection. iscsi_hashmap *key_value_pairs; + /// Temporarily storage for partially received parameter. + uint8_t *partial_pairs; + + /// iSCSI device. + iscsi_device *device; + + /// iSCSI initiator port. + iscsi_port *init_port; + + /// Initiator name. + uint8_t *init_name; + + //// Initiator IP address. + uint8_t *init_adr; + + /// iSCSI target node. + iscsi_target_node *target; + + /// iSCSI target port. + iscsi_port *target_port; + + /// iSCSI target short name. + uint8_t *target_name_short; + + /// iSCSI portal host name. + uint8_t *portal_host; + + /// iSCSI portal host port. + uint8_t *portal_port; + /// 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; - int8_t flags; + /// Current PDU being processed. + struct iscsi_pdu *pdu_processing; + + /// Login response PDU. + struct iscsi_pdu *login_response_pdu; + + /// Connected TCP/IP socket. + int sock; + + /// iSCSI connection receiving state. + int pdu_recv_state; + + /// iSCSI connection flags. + int flags; + + /// iSCSI connection state. + int state; + + /// Maximum receive DataSegment length in bytes. + uint max_recv_ds_len; + + /// Portal group tag. + int pg_tag; /// Initiator Session ID (ISID). iscsi_isid isid; @@ -5660,21 +6326,123 @@ typedef struct iscsi_connection { /// Target Session Identifying Handle (TSIH). uint16_t tsih; + /// Connection ID (CID). + uint16_t cid; + /// Initiator Task Tag (ITT). uint32_t init_task_tag; - /// Connection ID (CID). - uint16_t cid; + /// CHAP authentication. + iscsi_auth_chap auth_chap; - /// CmdSN. - uint32_t cmd_sn; + /// CHAP group id. + int32_t chap_group; + + /// StatSN. + uint32_t stat_sn; /// ExpStatSN. uint32_t exp_stat_sn; } iscsi_connection; -iscsi_connection *iscsi_connection_create(const iscsi_login_req_packet *login_req_pkt); // Creates data structure for an iSCSI connection request -void iscsi_connection_destroy(iscsi_connection *conn); // Deallocates all resources acquired by iscsi_connection_create + +/// iSCSI PDU flags: Rejected. +#define ISCSI_PDU_FLAGS_REJECTED (1 << 0L) + + +/** + * @brief This structure is used to partially read PDU data. + * + * Since TCP/IP packets can be fragmented, this + * structure is needed which maintains reading + * and filling the BHS, AHS and DS properly. + */ +typedef struct iscsi_pdu { + /// iSCSI Basic Header Segment (BHS) packet data. + iscsi_bhs_packet *bhs_pkt; + + /// iSCSI Advanced Header Segment (AHS) packet data for fast access and is straight after BHS packet in memory. + iscsi_ahs_packet *ahs_pkt; + + /// Header digest (CRC32C) packet data for fast access and is straight after BHS and AHS packet in memory. + 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; + + /// 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; + + /// Flags. + int flags; + + /// 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; + + /// 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; + + /// Bytes of Basic Header Segment (BHS) already read. + uint bhs_read_len; + + /// AHSLength. + uint ahs_len; + + /// Bytes of Advanced Header Segment (AHS) already read. + uint ahs_read_len; + + /// DataSegmentLength. + uint ds_len; + + /// Remaining bytes of DataSegment buffer to read. + uint buf_len; + + /// Associated connection. + iscsi_connection *conn; + + /// CmdSN. + uint32_t cmd_sn; +} iscsi_pdu; + +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 +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 + +uint8_t *iscsi_target_node_get_redirect(iscsi_connection *conn, iscsi_target_node *target); // Retrieves target node redirection address +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 +void iscsi_session_destroy(iscsi_session *session); // Deallocates all resources acquired by iscsi_session_create + +iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock); // Creates data structure for an iSCSI connection from iSCSI portal and TCP/IP socket int iscsi_connection_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // iSCSI connection destructor callback for hash map +void iscsi_connection_destroy(iscsi_connection *conn); // Deallocates all resources acquired by iscsi_connection_create + +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 + +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 + +typedef void (*iscsi_connection_xfer_complete_callback)(uint8_t *user_data); // iSCSI transfer completed callback function. + +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 + +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); #endif /* DNBD3_ISCSI_H_ */ -- cgit v1.2.3-55-g7522 From 02d2cd49b4a944021969646653d099e006c2ee4f Mon Sep 17 00:00:00 2001 From: Sebastian Vater Date: Thu, 14 Aug 2025 16:27:10 +0200 Subject: [SERVER] iscsi: Finish login handling, add NOP-In/Out handling Also a couple bug fixes and other minor improvements --- src/server/iscsi.c | 1536 ++++++++++++++++++++++++++++++++++++++++++++-------- src/server/iscsi.h | 179 ++++-- 2 files changed, 1451 insertions(+), 264 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index baee49c..bfda85b 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,310 @@ * @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. * @@ -874,7 +1179,7 @@ iscsi_bhs_packet *iscsi_append_ahs_packet(iscsi_bhs_packet *packet_data, const u 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 ( new_pkt_size > (sizeof(struct iscsi_bhs_packet) + 1020UL) ) { + 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" ); return NULL; @@ -1369,7 +1674,7 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint 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 ( (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_TRANSMIT) != 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_TRANSMIT) != 0) || (login_req_pkt->reserved != 0) || (login_req_pkt->reserved2[0] != 0) || (login_req_pkt->reserved2[1] != 0) ) + 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 return iscsi_validate_key_value_pairs( ((const uint8_t *) login_req_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len ); @@ -1469,7 +1774,7 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint 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; - if ( (ISCSI_LOGIN_RESPONSES_FLAGS_GET_CURRENT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_RESERVED) || ((ISCSI_LOGIN_RESPONSES_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_RESERVED) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT) != 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_TRANSMIT) != 0) || (login_response_pkt->reserved != 0) || (login_response_pkt->reserved2 != 0) || (login_response_pkt->reserved3 != 0) ) + 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 return iscsi_validate_key_value_pairs( ((const uint8_t *) login_response_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len ); @@ -1633,7 +1938,7 @@ static int iscsi_parse_text_key_value_pair(iscsi_hashmap *pairs, const uint8_t * return -1L; } - uint8_t *hash_val = (uint8_t *) malloc( val_len + 1UL ); + uint8_t *hash_val = (uint8_t *) malloc( iscsi_align(val_len + 1UL, ISCSI_HASHMAP_VALUE_ALIGN) ); if ( hash_val == NULL ) { logadd( LOG_ERROR, "iscsi_parse_text_key_value_pair: Out of memory allocating memory for value string" ); @@ -1746,118 +2051,6 @@ int iscsi_parse_key_value_pairs(iscsi_hashmap *pairs, const uint8_t *packet_data return 0L; } -/** - * @brief Creates a single partial iSCSI packet stream out of a single text key and value pair. - * - * Callback function for constructing an iSCSI packet data - * stream for a single key=value text pair. - * - * @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 creates an - * empty key assignment. - * @param[in,out] user_data Pointer to a data structure - * containing the memory buffer and length for the - * iSCSI packet data (iscsi_key_value_pair_packet - * structure), may NOT be NULL, so be careful. - * @retval 0 The NUL terminated key=value pair has been - * generated successfully. - * @retval -1 The NUL terminated key=value pair could - * NOT be created (probably due to memory exhaustion). - */ -int iscsi_create_key_value_pair_packet_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) -{ - iscsi_key_value_pair_packet *packet_data = (iscsi_key_value_pair_packet *) user_data; - uint8_t *buf = iscsi_sprintf_alloc( "%s=%s", key, ((value != NULL) ? value : (uint8_t *) "") ); - - if ( buf == NULL ) { - logadd( LOG_ERROR, "iscsi_create_key_value_pair_packet_callback: Out of memory generating text key / value pair DataSegment" ); - - return -1L; - } - - const uint len = (uint) strlen( (char *) buf ) + 1; - const uint new_len = packet_data->len + len; - - uint8_t *new_buf = realloc( packet_data->buf, new_len ); - - if ( new_buf == NULL ) { - logadd( LOG_ERROR, "iscsi_create_key_value_pair_packet_callback: Out of memory generating text key / value pair DataSegment" ); - - free( buf ); - - return -1L; - } - - memcpy( (new_buf + packet_data->len), buf, len ); - - packet_data->buf = new_buf; - packet_data->len = new_len; - - return 0L; -} - -/** - * @brief Creates a properly aligned iSCSI packet DataSegment out of a hash map containing text key and value pairs. - * - * Creates a properly aligned iSCSI DataSegment - * containing NUL terminated key=value pairs - * out of an hash map. The result can directly - * be attached to a BHS/AHS packet and sent - * via TCP/IP. - * - * @param[in] key_value_pairs Pointer to hash map containing - * the key and value pairs to construct the - * DataSegment from, may NOT be NULL, so be careful. - * @return Pointer to iscsi_key_value_pair_packet - * structure containing the properly aligned - * DataSegment buffer and its unaligned length or - * NULL in case of an error (most likely due to - * memory exhaustion). - */ -iscsi_key_value_pair_packet *iscsi_create_key_value_pairs_packet(iscsi_hashmap *key_value_pairs) -{ - iscsi_key_value_pair_packet *packet_data = (iscsi_key_value_pair_packet *) malloc( sizeof(struct iscsi_key_value_pair_packet) ); - - if ( packet_data == NULL ) { - logadd( LOG_ERROR, "iscsi_create_key_value_pairs: Out of memory generating text key / value pair DataSegment" ); - - return NULL; - } - - packet_data->buf = NULL; - packet_data->len = 0UL; - - if ( iscsi_hashmap_iterate( key_value_pairs, iscsi_create_key_value_pair_packet_callback, (uint8_t *) packet_data ) < 0L ) { - if ( packet_data->buf != NULL ) - free( packet_data->buf ); - - free( packet_data ); - - return NULL; - } - - if ( (packet_data->len & (ISCSI_ALIGN_SIZE - 1UL)) != 0 ) { - uint8_t *new_buf = realloc( packet_data->buf, iscsi_align(packet_data->len, ISCSI_ALIGN_SIZE) ); - - if ( new_buf == NULL ) { - logadd( LOG_ERROR, "iscsi_create_key_value_pairs: Out of memory generating text key / value pair DataSegment" ); - - free( packet_data->buf ); - free( packet_data ); - - return NULL; - } - - memset( (packet_data->buf + packet_data->len), 0, (packet_data->len & (ISCSI_ALIGN_SIZE - 1UL)) ); - } - - return packet_data; -} - /** * @brief Extracts a string from a key and value pair. * @@ -1887,8 +2080,8 @@ static int iscsi_get_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_ * and its string value. * * @param[in] key_value_pairs Pointer to the hash map which should - * contain the added string key and value pair. NULL is NOT - * allowed here, so be careful. + * contain the added string key and value pair. + * NULL is NOT allowed here, so be careful. * @param[in] key String containing the key name as string. May * NOT be NULL, so take caution. * @param[in] value String containing the value to be stored. @@ -1901,16 +2094,18 @@ static int iscsi_add_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_ 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." ); + logadd( LOG_ERROR, "iscsi_add_key_value_pair: Out of memory allocating key" ); return -1L; } const uint val_len = (uint) strlen( (char *) value ) + 1; - uint8_t *hash_val = (uint8_t *) malloc( val_len ); + uint8_t *hash_val = (uint8_t *) malloc( iscsi_align(val_len, ISCSI_HASHMAP_VALUE_ALIGN) ); if ( hash_val == NULL ) { - logadd( LOG_ERROR, "iscsi_add_key_value_pair: Out of memory allocating string value." ); + logadd( LOG_ERROR, "iscsi_add_key_value_pair: Out of memory allocating string value" ); + + iscsi_hashmap_key_destroy( hash_key ); return -1L; } @@ -1920,6 +2115,50 @@ static int iscsi_add_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_ return iscsi_hashmap_put( key_value_pairs, hash_key, key_len, hash_val ); } +/** + * @brief Allocates and updates a string value of a key / value hash map pair. + * + * This function allocates memory for a string key + * and its string value.\n + * If the key does not exist, it will be added as + * a new one. + * + * @param[in] key_value_pairs Pointer to the hash map which should + * contain the updated string key and value pair. + * NULL is NOT allowed here, so be careful. + * @param[in] key String containing the key name as string. May + * NOT be NULL, so take caution. + * @param[in] value String containing the value to be stored. + * @return 0 on successful operation, or a negative value on + * error (memory exhaustion). + */ +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; + 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; + } + + const uint val_len = (uint) strlen( (char *) value ) + 1; + uint8_t *hash_val = (uint8_t *) malloc( iscsi_align(val_len, ISCSI_HASHMAP_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; + } + + memcpy( hash_val, value, val_len ); + + return iscsi_hashmap_put_free( key_value_pairs, hash_key, key_len, hash_val, iscsi_hashmap_key_destroy_value_callback, NULL ); +} + /** * @brief Extracts an integer value from a key and value pair. * @@ -1953,8 +2192,8 @@ static int iscsi_get_int_key_value_pair(iscsi_hashmap *key_value_pairs, const ui * and its integer representation as string value. * * @param[in] key_value_pairs Pointer to the hash map which should - * contain the added integer key and value pair. NULL is NOT - * allowed here, so be careful. + * contain the added integer key and value pair. + * NULL is NOT allowed here, so be careful. * @param[in] key String containing the key name as string. May * NOT be NULL, so take caution. * @param[in] value Integer containing the value to be stored. @@ -1963,7 +2202,7 @@ static int iscsi_get_int_key_value_pair(iscsi_hashmap *key_value_pairs, const ui */ static int iscsi_add_int_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int value) { - const uint8_t *hash_val = iscsi_sprintf_alloc( "%ld", value ); + const uint8_t *hash_val = iscsi_sprintf_alloc( "%d", value ); if ( hash_val == NULL ) { logadd( LOG_ERROR, "iscsi_add_int_key_value_pair: Out of memory allocating integer value." ); @@ -1974,6 +2213,36 @@ static int iscsi_add_int_key_value_pair(iscsi_hashmap *key_value_pairs, const ui return iscsi_add_key_value_pair( key_value_pairs, key, hash_val ); } +/** + * @brief Allocates and updates an integer value of a key / value hash map pair. + * + * This function allocates memory for a string key + * and its integer representation as string value.\n + * If the key does not exist, it will be added as + * a new one. + * + * @param[in] key_value_pairs Pointer to the hash map which should + * contain the updated integer key and value pair. + * NULL is NOT allowed here, so be careful. + * @param[in] key String containing the key name as string. May + * NOT be NULL, so take caution. + * @param[in] value Integer containing the value to be stored. + * @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) +{ + const uint8_t *hash_val = iscsi_sprintf_alloc( "%d", value ); + + if ( hash_val == NULL ) { + logadd( LOG_ERROR, "iscsi_update_int_key_value_pair: Out of memory allocating integer value." ); + + return -1L; + } + + return iscsi_update_key_value_pair( key_value_pairs, key, hash_val ); +} + /** * @brief Extracts a boolean value from a key and value pair. * @@ -2009,8 +2278,8 @@ static int iscsi_get_bool_key_value_pair(iscsi_hashmap *key_value_pairs, const u * The string representation for false is: No * * @param[in] key_value_pairs Pointer to the hash map which should - * contain the added boolean key and value pair. NULL is NOT - * allowed here, so be careful. + * contain the added boolean key and value pair. + * NULL is NOT allowed here, so be careful. * @param[in] key String containing the key name as string. May * NOT be NULL, so take caution. * @param[in] value Boolean containing the value to be stored @@ -2025,6 +2294,33 @@ static int iscsi_add_bool_key_value_pair(iscsi_hashmap *key_value_pairs, const u return iscsi_add_key_value_pair( key_value_pairs, key, hash_val ); } +/** + * @brief Allocates and updates an boolean value of a key / value hash map pair. + * + * This function allocates memory for a string key + * and its integer value.\n + * The string representation for true is: Yes\n + * The string representation for false is: No\n + * If the key does not exist, it will be added as + * a new one. + * + * @param[in] key_value_pairs Pointer to the hash map which should + * contain the updated boolean key and value pair. + * NULL is NOT allowed here, so be careful. + * @param[in] key String containing the key name as string. May + * NOT be NULL, so take caution. + * @param[in] value Boolean containing the value to be stored + * as string. + * @return 0 on successful operation, or a negative value on + * error (memory exhaustion). + */ +static int iscsi_update_bool_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int value) +{ + const uint8_t *hash_val = (uint8_t *) ((value != 0) ? "Yes" : "No"); + + return iscsi_update_key_value_pair( key_value_pairs, key, hash_val ); +} + /** * @brief Creates and initializes an iSCSI portal group. * @@ -2586,7 +2882,7 @@ iscsi_session *iscsi_session_create(iscsi_connection *conn, iscsi_target_node *t session->type = type; session->current_text_init_task_tag = 0xFFFFFFFFUL; - session->key_value_pairs = iscsi_hashmap_create( 0UL ); + session->key_value_pairs = iscsi_hashmap_create( 32UL ); if ( session->key_value_pairs == NULL ) { logadd( LOG_ERROR, "iscsi_session_create: Out of memory allocating iSCSI session key and value pairs hash map" ); @@ -2598,17 +2894,18 @@ iscsi_session *iscsi_session_create(iscsi_connection *conn, iscsi_target_node *t return NULL; } - int rc = iscsi_add_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS, session->max_conns ); - rc |= iscsi_add_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_add_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_add_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_add_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_add_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_add_bool_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IMMEDIATE_DATA, session->immediate_data ); - rc |= iscsi_add_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_add_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_add_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_add_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 ); + 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 ); if ( rc != 0 ) { logadd( LOG_ERROR, "iscsi_session_create: Out of memory adding iSCSI session key and integer value pair" ); @@ -2666,9 +2963,53 @@ void iscsi_session_destroy(iscsi_session *session) } /** - * @brief Creates data structure for an iSCSI connection from iSCSI portal and TCP/IP socket. - * - * Creates a data structure for incoming iSCSI connection + * @brief Initializes a key and value pair hash table with default values. + * + * This function is used by iSCSI connections and + * sessions with default values for required keys.\n + * The iSCSI global key and value pair allowed + * values and ranges for fast + * access + * + * @param[in] key_value_pairs Pointer to key and value pair hash map + * which should store all the default values for + * its keys and 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_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++ ) { + const int rc = iscsi_add_key_value_pair( key_value_pairs, lut[i].key, lut[i].value ); + + if ( rc < 0 ) + return rc; + } + + return 0L; +} + +/** + * @brief Initializes a key and value pair hash table with default values for an iSCSI session. + * + * This function only initializes the default key + * and value pairs used by iSCSI sessions. + * + * @param[in] key_value_pairs Pointer to key and value pair hash map + * which should store all the default values for + * its keys and may NOT be NULL, so take caution. + * @return 0 on success, a negative error code otherwise. + */ +int iscsi_session_init_key_value_pairs(iscsi_hashmap *key_value_pairs) +{ + return iscsi_init_key_value_pairs( key_value_pairs, &iscsi_session_key_value_pair_lut[0] ); +} + +/** + * @brief Creates data structure for an iSCSI connection from iSCSI portal and TCP/IP socket. + * + * Creates a data structure for incoming iSCSI connection * requests from iSCSI packet data. * * @param[in] portal Pointer to iSCSI portal to associate the @@ -2698,6 +3039,16 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock) return NULL; } + const int rc = iscsi_connection_init_key_value_pairs( conn->key_value_pairs ); + + if ( rc < 0 ) { + 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->partial_pairs = NULL; conn->device = NULL; conn->init_port = NULL; @@ -2712,10 +3063,12 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock) conn->data_digest = 0L; conn->pdu_processing = NULL; 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; @@ -2896,6 +3249,477 @@ int iscsi_connection_write(const iscsi_connection *conn, uint8_t *buf, const uin return (rc > 0) ? rc : ISCSI_CONNECT_PDU_READ_ERR_FATAL; } +/** + * @brief Initializes a key and value pair hash table with default values for an iSCSI connection. + * + * This function only initializes the default key + * and value pairs used by iSCSI connectionss. + * + * @param[in] key_value_pairs Pointer to key and value pair hash map + * which should store all the default values for + * its keys and may NOT be NULL, so take caution. + * @return 0 on success, a negative error code otherwise. + */ +int iscsi_connection_init_key_value_pairs(iscsi_hashmap *key_value_pairs) +{ + return iscsi_init_key_value_pairs( key_value_pairs, &iscsi_connection_key_value_pair_lut[0] ); +} + +/** + * @brief Appends a special key and value pair to DataSegment packet data. + * + * This function adds MaxRecvDataSegmentLength, + * FirstBurstLength and MaxBurstLength, which + * require special handling to an output + * DataSegment buffer and truncates if + * necessary. + * + * @param[in] conn Pointer to iSCSI connection to handle the + * 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. + * @param[in] key Pointer to special key to be written to + * output buffer. NULL is NOT allowed, + * take caution. + * @param[in] buf Pointer to output buffer to write the + * special key and value pair to. NULL is + * prohibited, so be careful. + * @param[in] pos Position of buffer in bytes to start + * writing to. + * @param[in] len Total length of buffer in bytes. + * @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) +{ + if ( (key_value_pair->flags & ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_OVERRIDE_DEFAULT) != 0 ) { + if ( (int) (len - pos) < 1L ) + return -1L; + + pos += snprintf( (char *) (buf + pos), (len - pos), "%s=%ld", key, 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 ) + 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 ); + + 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 ); + + 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 ); + } + } + + pos += snprintf( (char *) (buf + pos), (len - pos), "%s=%d", key, first_burst_len ) + 1; + } + + return pos; + +} + +/** + * @brief Appends a key and value pair to DataSegment packet data. + * + * This function adds any non-declarative key + * and value pair to an output DataSegment + * 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. + * @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 + * be written to output buffer which may + * NOT be NULL, so take caution. + * @param[in] buf Pointer to output buffer to write the + * key and value pair to. NULL is + * prohibited, so be careful. + * @param[in] pos Position of buffer in bytes to start + * writing to. + * @param[in] len Total length of buffer in bytes. + * @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) +{ + 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 ) + return -1L; + + pos += snprintf( (char *) (buf + pos), (len - pos), "%s=%s", key, value ) + 1; + } + + return pos; +} + +/** + * @brief Negotiates key and value pair of list type. + * + * This function checks if a key of list type has a + * valid value according to the iSCSI specification. + * + * @param[in] key_value_pair Pointer to key and value pair. May NOT + * be NULL, so be careful. + * @param[in] old_value Pointer to string containing the old + * value. NULL is not allowed, so take caution. + * @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) +{ + const uint8_t *list = key_value_pair->list_range; + + do { + if ( strcasecmp( (char *) list, (char *) old_value ) == 0 ) + return old_value; + + list += (strlen( (char *) list ) + 1); + } while ( list[0] != '\0' ); + + return NULL; +} + +/** + * @brief Negotiates key and value pair of numeric type. + * + * This function checks if a key of numeric type has + * a valid interger value and clamps within the + * allowed minimum and maximum range according to + * the iSCSI specification. + * + * @param[in] key_value_pair Pointer to key and value pair. May NOT + * be NULL, so be careful. + * @param[in] old_value Pointer to string containing the old + * value. NULL is not allowed, take caution. + * @param[in] value Pointer to string containing the current + * value which may NOT be NULL, so be careful. + * @return Pointer to original value, if the value is + * allowed or NULL otherwise. + */ +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 ); + + if ( (key_value_pair->flags & ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_USE_PREVIOUS_VALUE) != 0 ) + old_int_val = (int) atol( (char *) old_value ); + + int int_val = (int) 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) ); + + if ( (old_int_val < range_min) || (old_int_val > range_max) ) + return NULL; + + switch ( key_value_pair->type ) { + case ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN : { + if ( old_int_val > int_val ) + old_int_val = int_val; + + break; + } + case ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MAX : { + if ( old_int_val < int_val ) + old_int_val = int_val; + + break; + } + default : { + return old_value; + + break; + } + } + + sprintf( (char *) old_value, "%d", old_int_val ); + + return old_value; +} + +/** + * @brief Negotiates key and value pair of boolean type. + * + * This function checks if a key of boolean type + * has a valid value according to the iSCSI + * specification and also applies the boolean + * OR / AND function to the current value. + * + * @param[in] key_value_pair Pointer to key and value pair. May NOT + * be NULL, so be careful. + * @param[in] old_value Pointer to string containing the old + * value. NULL is not allowed, so take caution. + * @param[in] value Pointer to string containing the + * current value which may NOT be NULL, so be + * careful. + * @param[in] bool_value Pointer to string containing the + * boolean OR / AND value where NULL is + * prohibited, so take caution. + * @param[out] update_key_value_pair Pointer to integer which + * marks if the key and value pair should be + * updated. May NOT be NULL, so be careful. + * @return Pointer to either boolean AND / OR result, + * default value or NULL in case of invalid + * boolean value. + */ +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; + + if ( (strcasecmp( (char *) old_value, (char *) list_bool_true ) == 0) || (strcasecmp( (char *) old_value, (char *) list_bool_false ) == 0) ) { + *update_key_value_pair = 0L; + + return (uint8_t *) "Reject"; + } + + if ( strcasecmp( (char *) value, (char *) bool_value ) == 0 ) + return bool_value; + + return key_value_pair->value; +} + +/** + * @brief Negotiates key and value pair of all types. + * + * This function determines the key type and + * calls the suitable negotation handler + * for checking iSCSI standard compliance. + * + * @param[in] key_value_pair Pointer to key and value pair. May NOT + * be NULL, so be careful. + * @param[in] old_value Pointer to string containing the old + * value. NULL is not allowed, so take caution. + * @param[in] value Pointer to string containing the + * current value which may NOT be NULL, so be + * careful. + * @param[out] update_key_value_pair Pointer to integer which + * marks if the key and value pair should be + * updated. NULL is not allowed, take caution. + * @return Pointer to new negotiated value or NULL + * in case of an invalid negation status. + */ +static uint8_t *iscsi_negotiate_key_value_pair_all(const iscsi_key_value_pair *key_value_pair, uint8_t *old_value, uint8_t *value, int *update_key_value_pair) +{ + switch ( key_value_pair->type ) { + case ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST : { + return iscsi_negotiate_key_value_pair_list( key_value_pair, old_value ); + + break; + } + case ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN : + case ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MAX : + case ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_DECLARATIVE : { + return iscsi_negotiate_key_value_pair_num( key_value_pair, old_value, value ); + + break; + } + case ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_AND : { + return iscsi_negotiate_key_value_pair_bool( key_value_pair, old_value, value, key_value_pair->list_range, update_key_value_pair ); + + break; + } + 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; + + return iscsi_negotiate_key_value_pair_bool( key_value_pair, old_value, value, list_bool_false, update_key_value_pair ); + + break; + } + default : { + break; + } + } + + return key_value_pair->value; +} + +/** + * @brief Negotiates either iSCSI session or connection state. + * + * This function checks and sets the state mask + * of either an iSCSI connection or session + * key and value pair. + * + * @param[in] conn Pointer to iSCSI connection of which to + * determine the key type. NULL is NOT allowed, + * so be careful. + * @param[in] key_value_pair Pointer to key and value pair + * containing the key and value pair attributes. + * NULL is NOT allowed, so be careful. + * @param[in] type Type of key and value pair to negotiate. + * 0 is iSCSI connection key and value pair, + * any other value indicates an iSCSI session + * key and value pair. + * @return 0 on successful state negotation, a + * negative error code otherwise. + */ +static int iscsi_negotiate_key_value_pairs_state(iscsi_connection *conn, const iscsi_key_value_pair *key_value_pair, const int type) +{ + if ( type != 0 ) { + const uint32_t state_mask = (uint32_t) key_value_pair->state_mask; + + if ( ((conn->session_state_negotiated & state_mask) != 0) && ((key_value_pair->flags & ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_TARGET_DECLARATIVE) == 0) ) + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER_XCHG_NOT_ONCE; + + conn->session_state_negotiated |= state_mask; + } else { + const uint16_t state_mask = (uint16_t) key_value_pair->state_mask; + + if ( ((conn->state_negotiated & state_mask) != 0) && ((key_value_pair->flags & ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_MULTI_NEGOTIATION) == 0) ) + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER_XCHG_NOT_ONCE; + + conn->state_negotiated |= state_mask; + } + + return ISCSI_CONNECT_PDU_READ_OK; +} + +/** + * @brief Callback which negotiates a single key and value pairs required for session authentication. + * + * This function is called for each key and value + * pair which needs connection or session + * authentication. + * + * @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 Pointer to integer value which is + * 1 is this is discovery, or 0 if not. + * @return Always returns 0 as this function cannot fail. + */ +int iscsi_negotiate_key_value_pair_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) +{ + iscsi_key_value_pair_packet *key_value_pair_packet = (iscsi_key_value_pair_packet *) user_data; + 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 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; + + 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; + + int update_key_value_pair = 1L; + uint8_t *conn_sess_val; + + if ( rc < 0 ) { + conn_sess_val = (uint8_t *) "NotUnderstood"; + + update_key_value_pair = 0L; + } 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; + } else { + rc = iscsi_negotiate_key_value_pairs_state( conn, key_value_pair, type ); + + if ( rc < 0 ) + return rc; + + rc = iscsi_hashmap_get( key_value_pairs, key, key_size, &conn_sess_val ); + } + + 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; + + rc = iscsi_get_key_value_pair( key_value_pairs, (uint8_t *) 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 ); + + if ( (first_burst_len < ISCSI_MAX_DS_SIZE) && (first_burst_len > max_burst_len) ) + sprintf( (char *) value, "%d", first_burst_len ); + } + + if ( (key_value_pair->flags & ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_TARGET_DECLARATIVE) != 0 ) + update_key_value_pair = 0L; + + conn_sess_val = iscsi_negotiate_key_value_pair_all( key_value_pair, value, conn_sess_val, &update_key_value_pair ); + } + + if ( conn_sess_val != NULL ) { + if ( update_key_value_pair != 0 ) + iscsi_update_key_value_pair( key_value_pairs, key, conn_sess_val ); + + 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 ) + 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 ) + return key_value_pair_packet->pos; + } + + return 0L; +} + +/** + * @brief Negotiates all key and value pairs required for session authentication. + * + * @param[in] conn Pointer to iSCSI connection to be + * negotiated, may NOT be NULL, so be careful. + * @param[in] key_value_pairs Pointer to key and value pair hash map + * which contains the negotiation pairs. NULL + * is prohibited, so take caution. + * @param[in] buf Pointer to output buffer which may NOT + * be NULL, so be careful. + * @param[in] pos Number of bytes already written. + * @param[in] len Length of DataSegment in bytes. + * @return New buffer length in bytes if all keys + * 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) +{ + if ( pos > len ) { + buf[len - 1] = '\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 ); + + 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 ); + + const int discovery = ((rc == 0) && (strcasecmp( (char *) type, "Discovery" ) == 0)) ? 1L : 0L; + + 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 ); + + return key_value_pair_packet.pos; +} + /** * @brief Copies retrieved key and value pairs into SCSI connection and session structures. * @@ -2983,6 +3807,36 @@ int iscsi_connection_copy_key_value_pairs(iscsi_connection *conn) return 0L; } +/** + * @brief Handles stages of CHAP and other authentication methods. + * + * This function handles the various stages of the + * various authentication methods supported by the + * iSCSI protocol.\n + * Currently, only CHAP is implemented. + * + * @param[in] conn Pointer to iSCSI connection which may NOT + * be NULL, so be careful. + * @param[in] key_value_pairs Pointer to hash map containing the + * authentication key and value pairs. NULL + * is NOT allowed here, so take caution. + * @param[in] auth_method Pointer to string containing the + * authentication method. NULL is forbidden, + * so be careful. + * @param[in] buf Pointer to DataSegment buffer. May NOT be + * NULL, so take caution. + * @param[in] pos Remaining number of bytes to read. + * @param[in] len Total number of bytes of DataSegment buffer. + * @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) +{ + // TODO: Implement CHAP and other authentication methods. + + return 0L; +} + /** * @brief Checks buffer sizes of an iSCSI connection with it's associated session for consistency. * @@ -3000,9 +3854,9 @@ int iscsi_connection_copy_key_value_pairs(iscsi_connection *conn) 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 -1; + return -1L; - return 0; + return 0L; } /** @@ -3048,7 +3902,7 @@ static int iscsi_connection_update_key_value_pairs(iscsi_connection *conn) else if ( recv_buf_len > 8192 ) recv_buf_len = 8192UL; - recv_buf_len += (uint) (sizeof(struct iscsi_bhs_packet) + 1020UL + conn->header_digest + conn->data_digest); // BHS + maximum AHS size + header and data digest overhead + 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. 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. @@ -3090,7 +3944,7 @@ 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_TRANSMIT | ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_MASK | ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_MASK ); + 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 ); @@ -3174,9 +4028,9 @@ static int iscsi_login_response_init(iscsi_pdu *login_response_pdu, const iscsi_ 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_pkt->flags |= (int8_t) (login_req_pkt->flags & (ISCSI_LOGIN_REQ_FLAGS_TRANSMIT | ISCSI_LOGIN_REQ_FLAGS_CONTINUE | ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_MASK)); + 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_TRANSMIT) != 0 ) + 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); login_response_pkt->isid.a = login_req_pkt->isid.a; @@ -3190,7 +4044,7 @@ static int iscsi_login_response_init(iscsi_pdu *login_response_pdu, const iscsi_ if ( login_response_pkt->tsih != 0 ) login_response_pkt->stat_sn = login_req_pkt->exp_stat_sn; // Copying over doesn't change endianess.' - if ( ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT) != 0) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE) != 0) ) { + 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; login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; @@ -3200,8 +4054,8 @@ 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_WRONG_VERSION; return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - } else if ( (ISCSI_LOGIN_RESPONSES_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_RESERVED) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT) != 0) ) { - login_response_pkt->flags &= (int8_t) ~(ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_MASK | ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT | ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_MASK); + } else if ( (ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_RESERVED) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0) ) { + login_response_pkt->flags &= (int8_t) ~(ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_MASK | ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT | ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_MASK); login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; @@ -3424,7 +4278,7 @@ 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_add_key_value_pair( login_response_pdu->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, redirect_adr ) == 0 ) { + 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; @@ -3618,7 +4472,7 @@ iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn) pdu->ahs_len = 0UL; pdu->ahs_read_len = 0UL; pdu->ds_len = 0UL; - pdu->buf_len = 0UL; + pdu->pos = 0UL; pdu->conn = conn; pdu->cmd_sn = 0UL; @@ -3717,20 +4571,20 @@ static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn ); if ( response_pdu == NULL ) { - logadd( LOG_ERROR, "iscsi_pdu_create: Out of memory while allocating iSCSI reject response PDU" ); + logadd( LOG_ERROR, "iscsi_connection_handle_reject: Out of memory while allocating iSCSI reject response PDU" ); - return -1L; + 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( pdu->bhs_pkt, conn->header_digest, ds_len, conn->data_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_pdu_create: Out of memory while allocating iSCSI reject packet data" ); + logadd( LOG_ERROR, "iscsi_connection_handle_reject: Out of memory while allocating iSCSI reject packet data" ); iscsi_connection_pdu_destroy( response_pdu ); - return -1L; + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } response_pdu->bhs_pkt = (iscsi_bhs_packet *) reject_pkt; @@ -3767,7 +4621,7 @@ static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu iscsi_connection_pdu_write( conn, response_pdu, NULL, NULL ); - return 0L; + return ISCSI_CONNECT_PDU_READ_OK; } /** @@ -3853,9 +4707,23 @@ static int iscsi_connection_pdu_header_handle_login_req(iscsi_connection *conn, */ static int iscsi_connection_pdu_header_handle_nop_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_nop_out_packet *nop_out_pkt = (iscsi_nop_out_packet *) pdu->bhs_pkt; + 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) ) + 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 ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + + return 0L; } /** @@ -4097,9 +4965,72 @@ 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) { - // TODO: Implement opcode. + iscsi_nop_out_packet *nop_out_pkt = (iscsi_nop_out_packet *) pdu->bhs_pkt; + uint32_t ds_len = pdu->ds_len; - return 0L; + if ( ds_len > conn->max_recv_ds_len ) + ds_len = conn->max_recv_ds_len; + + const uint64_t lun = iscsi_get_be64(nop_out_pkt->lun); + const uint32_t init_task_tag = iscsi_get_be32(nop_out_pkt->init_task_tag); + + conn->flags &= ~ISCSI_CONNECT_FLAGS_NOP_OUTSTANDING; + + if ( init_task_tag == 0xFFFFFFFFUL ) + return ISCSI_CONNECT_PDU_READ_OK; + + iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn ); + + if ( response_pdu == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_pdu_data_handle_nop_out: Out of memory while allocating iSCSI NOP-In response PDU" ); + + 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; + } + + nop_in_pkt->opcode = ISCSI_SERVER_NOP_IN; + nop_in_pkt->flags = -0x80; + iscsi_put_be24( (uint8_t *) &nop_in_pkt->ds_len, ds_len ); + 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 ); + iscsi_put_be32( (uint8_t *) &nop_in_pkt->stat_sn, conn->stat_sn++ ); + + if ( (nop_out_pkt->opcode & 0x40) == 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 ); + + iscsi_connection_pdu_write( conn, response_pdu, NULL, NULL ); + + // conn->nop_in_last = iscsi_get_ticks(); + + return ISCSI_CONNECT_PDU_READ_OK; } /** @@ -4124,50 +5055,6 @@ static int iscsi_connection_pdu_data_handle_scsi_cmd(iscsi_connection *conn, isc return 0L; } -/** - * @brief Allocates and replaces a string value to a key / value hash map pair. - * - * This function allocates memory for a string key - * and its string value and replaces a original - * key and value pair if it exists. - * - * @param[in] conn Pointer to iSCSI connection to update the - * login key and value pair for and may NOT - * be NULL, so take care. - * @param[in] key_value_pairs Pointer to the hash map which should - * contain the replaced string key and value pair. NULL is NOT - * allowed here, so be careful. - * @param[in] key String containing the key name as string. May - * NOT be NULL, so take caution. - * @param[in] value String containing the value to be stored. - * @return 0 on successful operation, or a negative value on - * error (memory exhaustion). - */ -static int iscsi_connection_login_update_key_value_pair(iscsi_connection *conn, const uint8_t *key, const uint8_t *value) -{ - const uint key_len = (uint) strlen( (char *) key ) + 1; - uint8_t *hash_key = iscsi_hashmap_key_create( key, key_len ); - - if ( hash_key == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_login_update_key_value_pair: Out of memory allocating key." ); - - return -1L; - } - - const uint val_len = (uint) strlen( (char *) value ) + 1; - uint8_t *hash_val = (uint8_t *) malloc( val_len ); - - if ( hash_val == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_login_update_key_value_pair: Out of memory allocating string value." ); - - return -1L; - } - - memcpy( hash_val, value, val_len ); - - return iscsi_hashmap_put_free( conn->key_value_pairs, hash_key, key_len, hash_val, iscsi_hashmap_key_destroy_value_callback, NULL ); -} - /** * @brief Negotiates connection authentication method (none or CHAP). * @@ -4180,12 +5067,12 @@ static int iscsi_connection_login_update_key_value_pair(iscsi_connection *conn, */ static int iscsi_connection_chap_negotiate(iscsi_connection *conn) { - int rc = 0; + int rc = 0L; if ( (conn->flags & ISCSI_CONNECT_FLAGS_CHAP_DISABLE) != 0 ) - rc = iscsi_connection_login_update_key_value_pair( conn, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD, (uint8_t *) "None" ); + rc = iscsi_update_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD, (uint8_t *) "None" ); else if ( (conn->flags & ISCSI_CONNECT_FLAGS_CHAP_REQUIRE) != 0 ) - rc = iscsi_connection_login_update_key_value_pair( conn, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD, (uint8_t *) "CHAP" ); + rc = iscsi_update_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD, (uint8_t *) "CHAP" ); return rc; } @@ -4267,19 +5154,15 @@ 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) { - if ( target->header_digest != 0 ) { - const int rc = iscsi_connection_login_update_key_value_pair( conn, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_HEADER_DIGEST, (uint8_t *) "CRC32C" ); - - return rc; - } + int rc = 0L; - if ( target->data_digest != 0 ) { - const int rc = iscsi_connection_login_update_key_value_pair( conn, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST, (uint8_t *) "CRC32C" ); + 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" ); - return rc; - } + 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" ); - return 0L; + return rc; } /** @@ -4434,7 +5317,7 @@ static int iscsi_connection_login_set_target_info(iscsi_connection *conn, iscsi_ int rc; if ( target != NULL ) { - rc = iscsi_add_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, (uint8_t *) 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; @@ -4445,14 +5328,14 @@ static int iscsi_connection_login_set_target_info(iscsi_connection *conn, iscsi_ if ( tmp_buf == NULL ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - rc = iscsi_add_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, (uint8_t *) 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_add_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, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG, conn->pg_tag ); if ( rc < 0 ) return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER; @@ -4461,7 +5344,7 @@ static int iscsi_connection_login_set_target_info(iscsi_connection *conn, iscsi_ rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS, &tmp_buf ); if ( (rc == 0) && (strlen( (char *) tmp_buf ) != 0) ) { - rc = iscsi_add_key_value_pair( login_response_pdu->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS, tmp_buf ); + rc = iscsi_update_key_value_pair( login_response_pdu->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS, tmp_buf ); if ( rc < 0 ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; @@ -4471,7 +5354,7 @@ static int iscsi_connection_login_set_target_info(iscsi_connection *conn, iscsi_ rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, &tmp_buf ); if ( (rc == 0) && (strlen( (char *) tmp_buf ) != 0) ) { - rc = iscsi_add_key_value_pair( login_response_pdu->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, tmp_buf ); + rc = iscsi_update_key_value_pair( login_response_pdu->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, tmp_buf ); if ( rc < 0 ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; @@ -4481,7 +5364,7 @@ static int iscsi_connection_login_set_target_info(iscsi_connection *conn, iscsi_ 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 ); if ( rc == 0 ) { - rc = iscsi_add_key_value_pair( login_response_pdu->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG, tmp_buf ); + 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 ); if ( rc < 0 ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; @@ -4559,6 +5442,185 @@ static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscs return iscsi_connection_login_set_target_info( conn, login_response_pdu, type ); } +/** + * @brief Handles the Current Stage (CSG) bit of login response. + * + * This function determines the authentication method + * and processes the various authentication stages. + * + * @param[in] conn Pointer to iSCSI connection, may NOT be + * NULL, so take caution. + * @param[in] login_response_pdu Pointer to login response PDU. + * NULL is NOT an allowed value here, so be careful. + * @param[in] key_value_pairs Pointer to key and value pairs to + * retrieve authentication details from. This + * is NOT allowed to be NULL. Be careful! + * @return 0 if authentication has been handled successfully, + * a negative error code otherwise. + */ +static int iscsi_connecction_handle_login_response_csg_bit(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; + + switch ( ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_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 ); + + if ( rc < 0 ) { + 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; + } + + 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 ); + + if ( ds_len < 0 ) { + 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 = ds_len; + + if ( (conn->flags & ISCSI_CONNECT_FLAGS_AUTH) == 0 ) + login_response_pkt->flags &= (int8_t) ~ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT; + } + + break; + } + case ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION : { + if ( conn->state == ISCSI_CONNECT_STATE_INVALID ) { + if ( conn->flags & ISCSI_CONNECT_FLAGS_CHAP_REQUIRE ) { + 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; + } else { + conn->flags |= ISCSI_CONNECT_FLAGS_AUTH; + } + } + + if ( (conn->flags & ISCSI_CONNECT_FLAGS_AUTH) == 0 ) { + 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; + } + + break; + } + case ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FULL_FEATURE_PHASE : { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + + break; + } + default : { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + + break; + } + } + + return ISCSI_CONNECT_PDU_READ_OK; +} + +/** + * + * @brief Checks whether the session type is valid. + * + * This function also can be used to output + * debugging info about the session, which + * is currently not implemented. + * + * @param[in] conn Pointer to iSCSI connection to be checked for + * validity. May NOT be NULL, so be careful. + * @param[in] login_response_pdu Pointer to login response PDU to + * set status class and detail in case of an error. + * NULL is not an allowed value here, take caution. + * @return 0 if the session type is valid, a negative error code + * otherwise. + */ +static int iscsi_connection_login_session_info_notify(iscsi_connection *conn, iscsi_pdu *login_response_pdu) +{ + if ( (conn->session->type != ISCSI_SESSION_TYPE_NORMAL) && (conn->session->type != ISCSI_SESSION_TYPE_DISCOVERY) ) { + iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; + + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } + + return ISCSI_CONNECT_PDU_READ_OK; +} + +/** + * @brief Handles the Transit (T) bit of login response. + * + * This function handles the transitional stages + * of the authentication process. + * + * @param[in] conn Pointer to iSCSI connection, may NOT be + * NULL, so take caution. + * @param[in] login_response_pdu Pointer to login response PDU. + * NULL is NOT an allowed value here, so be careful. + * @return 0 if transition has been handled successfully, + * a negative error code otherwise. + */ +static int iscsi_connecction_handle_login_response_t_bit(iscsi_connection *conn, iscsi_pdu *login_response_pdu) +{ + 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) ) { + case ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION : { + conn->login_phase = ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION; + + break; + } + case ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION : { + conn->login_phase = ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION; + + break; + } + 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 ); + + const int rc = iscsi_connection_login_session_info_notify( conn, login_response_pdu ); + + if ( rc < 0 ) + return rc; + + conn->flags |= ISCSI_CONNECT_FLAGS_FULL_FEATURE; + + break; + } + default : { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + + break; + } + } + + return ISCSI_CONNECT_PDU_READ_OK; +} + /** * @brief Handles iSCSI connection login response. * @@ -4575,9 +5637,27 @@ static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscs */ static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_hashmap *key_value_pairs) { - // TODO: Implement function. + 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 ); - return 0L; + if ( ds_len < 0 ) { + 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; + + 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) + rc = iscsi_connecction_handle_login_response_t_bit( conn, login_response_pdu ); + + return rc; } /** @@ -4831,7 +5911,7 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn) } pdu->ds_len = iscsi_align(pdu->ds_len, ISCSI_ALIGN_SIZE); - pdu->buf_len = pdu->ds_len; + pdu->pos = pdu->ds_len; const uint ahs_len = (uint) pdu->bhs_pkt->total_ahs_len << 2UL; diff --git a/src/server/iscsi.h b/src/server/iscsi.h index 56e09f9..5990d69 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -141,6 +141,12 @@ uint8_t *iscsi_sprintf_alloc(const char *format, ... ); // Allocates a buffer an /// 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 @@ -253,9 +259,21 @@ int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, u /* iSCSI protocol stuff (all WORD/DWORD/QWORD values are big endian by default unless specified otherwise). */ -/// iSCSI Basic Header Segment size. +/// iSCSI Basic Header Segment (BHS) size. #define ISCSI_BHS_SIZE 48UL +/// iSCSI Advanced Header Segment (AHS) maximum allowed size. +#define ISCSI_MAX_AHS_SIZE (255UL << 2UL) + +/// iSCSI DataSegment maximum allowed size. +#define ISCSI_MAX_DS_SIZE 16777215UL + +/// iSCSI packet data alignment (BHS, AHS and DataSegment). +#define ISCSI_ALIGN_SIZE 4UL + +/// iSCSI header and data digest size (CRC32C). +#define ISCSI_DIGEST_SIZE 4UL + /// iSCSI Default receive DataSegment (DS) size in bytes. #define ISCSI_DEFAULT_RECV_DS_LEN 8192UL @@ -267,13 +285,6 @@ int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, u #define ISCSI_DEFAULT_MAX_DATA_OUT_PER_CONNECTION 16UL -/// iSCSI header and data digest size (CRC32C). -#define ISCSI_DIGEST_SIZE 4UL - -/// iSCSI packet data alignment (BHS, AHS and DataSegment). -#define ISCSI_ALIGN_SIZE 4UL - - /// Current minimum iSCSI protocol version supported by this implementation. #define ISCSI_VERSION_MIN 0 @@ -381,6 +392,7 @@ int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, u /// Macro which extracts iSCSI packet data opcode out of opcode byte #define ISCSI_GET_OPCODE(x) ((x) & ISCSI_OPCODE_MASK) + /** * @brief iSCSI Basic Header Segment packet data. * @@ -4317,7 +4329,7 @@ typedef struct __attribute__((packed)) iscsi_isid { * this also indicates that the initiator is ready for the Login * Final-Response. */ -#define ISCSI_LOGIN_REQ_FLAGS_TRANSMIT (1 << 7) +#define ISCSI_LOGIN_REQ_FLAGS_TRANSIT (1 << 7) /** @@ -4487,7 +4499,7 @@ typedef struct __attribute__((packed)) iscsi_login_req_packet { #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))) /// Login response flags: Extracts the Next Stage (NSG) bits. -#define ISCSI_LOGIN_RESPONSES_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) (((x) & ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_MASK) >> ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FIRST_BIT) @@ -4527,7 +4539,7 @@ typedef struct __attribute__((packed)) iscsi_login_req_packet { #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))) /// Login request flags: Extracts the Current Stage (CSG) bits. -#define ISCSI_LOGIN_RESPONSES_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) (((x) & ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_MASK) >> ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FIRST_BIT) /** @@ -4554,7 +4566,7 @@ typedef struct __attribute__((packed)) iscsi_login_req_packet { * If the Status-Class is 0, the T bit MUST NOT be set to 1 if the T bit * in the request was set to 0. */ -#define ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT (1 << 7) +#define ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT (1 << 7) /** @@ -5688,6 +5700,7 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint /// Maximum length of value for a normal key. #define ISCSI_TEXT_VALUE_MAX_LEN 8192UL + /// iSCSI text key=value pair type: Invalid. #define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_INVALID -1L @@ -5716,6 +5729,55 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint #define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_AND 7L +/// iSCSI key value pair flags: Discovery ignored. +#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE (1 << 0L) + +/// iSCSI key value pair flags: Multi negotiation. +#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_MULTI_NEGOTIATION (1 << 1L) + +/// iSCSI key value pair flags: Target declarative. +#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_TARGET_DECLARATIVE (1 << 2L) + +/// iSCSI key value pair flags: CHAP type. +#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_CHAP_TYPE (1 << 3L) + +/// iSCSI key value pair flags: Requires special handling. +#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_SPECIAL_HANDLING (1 << 4L) + +/// iSCSI key value pair flags: Use previous default value. +#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_USE_PREVIOUS_VALUE (1 << 5L) + +/// iSCSI key value pair flags: Override with default value. +#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_OVERRIDE_DEFAULT (1 << 6L) + +/// 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) + + +/** + * @brief iSCSI connection and session lookup table entry, used for allowed key values and determining key type. + * + * This structure is shared by the iSCSI session + * and the iSCSI connection lookup table. + */ +typedef struct iscsi_key_value_pair_lut_entry { + /// Name of key. + uint8_t *key; + + /// Default value of the key, always in string representation. + uint8_t *value; + + /// NUL separated list of allowed string values. If key type is numeric: NUL separated minimum and maximum integer range. End is marked with another NUL. + uint8_t *list_range; + + /// Type of key and value pair. + const int type; + + /// Flags indicating special key attributes. + const int flags; +} iscsi_key_value_pair_lut_entry; + + /** * @brief iSCSI Text / Login extracted key=value pair. * @@ -5724,14 +5786,20 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint * Text or Login packet data. */ typedef struct iscsi_key_value_pair { + /// Value of the key which is stored in the hash map. + uint8_t *value; + + /// NUL separated list of allowed string values. If key type is numeric: NUL separated minimum and maximum integer range. End is marked with another NUL. + uint8_t *list_range; + /// Type of key and value pair. - int type; + int type; - /// State index. - int state_index; + /// Flags indicating special key attributes. + int flags; - /// Value of the key which is stored in the hash map. - uint8_t *value; + /// State bit mask. + uint state_mask; } iscsi_key_value_pair; /** @@ -5741,16 +5809,23 @@ typedef struct iscsi_key_value_pair { * pairs for sending as DataSegment packet data to the client. */ typedef struct iscsi_key_value_pair_packet { + /// Associated iSCSI connection. + struct 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; + /// Current length of buffer including final NUL terminator without iSCSI zero padding. uint 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_create_key_value_pair_packet_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Creates a single partial iSCSI packet stream out of a single text key and value pair -iscsi_key_value_pair_packet *iscsi_create_key_value_pairs_packet(iscsi_hashmap *key_value_pairs); // Creates a properly aligned iSCSI packet DataSegment out of a hash map containing text key and value pairs /// iSCSI main global data flags: Allow duplicate ISIDs. @@ -5785,9 +5860,15 @@ typedef struct iscsi_globals { /// Hash map containing all iSCSI sessions. iscsi_hashmap *sessions; + /// 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; + /// Global flags. int flags; @@ -5799,10 +5880,14 @@ typedef struct iscsi_globals { } iscsi_globals; -/// iSCSI vector for global access. +/// iSCSI global vector. MUST be initialized with iscsi_create before any iSCSI functions are used. iscsi_globals *iscsi_globvec = NULL; +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 + /** * @brief iSCSI portal group: Private portal group if set, public otherwise. * @@ -6179,44 +6264,50 @@ typedef struct iscsi_session { /// 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 0L /// 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 1L /// 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 -1L /// 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 -2L /// 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 -3L + +/// 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 /// iSCSI connection flags: Stopped. -#define ISCSI_CONNECT_FLAGS_STOPPED (1 << 0L) +#define ISCSI_CONNECT_FLAGS_STOPPED (1 << 0L) /// iSCSI connection flags: Rejected. -#define ISCSI_CONNECT_FLAGS_REJECTED (1 << 1L) +#define ISCSI_CONNECT_FLAGS_REJECTED (1 << 1L) /// iSCSI connection flags: Logged out. -#define ISCSI_CONNECT_FLAGS_LOGGED_OUT (1 << 2L) +#define ISCSI_CONNECT_FLAGS_LOGGED_OUT (1 << 2L) /// iSCSI connection flags: Full feature. -#define ISCSI_CONNECT_FLAGS_FULL_FEATURE (1 << 3L) +#define ISCSI_CONNECT_FLAGS_FULL_FEATURE (1 << 3L) /// iSCSI connection flags: CHAP authentication is disabled. -#define ISCSI_CONNECT_FLAGS_CHAP_DISABLE (1 << 4L) +#define ISCSI_CONNECT_FLAGS_CHAP_DISABLE (1 << 4L) /// iSCSI connection flags: CHAP authentication is required. -#define ISCSI_CONNECT_FLAGS_CHAP_REQUIRE (1 << 5L) +#define ISCSI_CONNECT_FLAGS_CHAP_REQUIRE (1 << 5L) /// iSCSI connection flags: CHAP authentication is mutual. -#define ISCSI_CONNECT_FLAGS_CHAP_MUTUAL (1 << 6L) +#define ISCSI_CONNECT_FLAGS_CHAP_MUTUAL (1 << 6L) /// iSCSI connection flags: Authenticated. -#define ISCSI_CONNECT_FLAGS_AUTH (1 << 7L) +#define ISCSI_CONNECT_FLAGS_AUTH (1 << 7L) + +/// iSCSI connection flags: Oustanding NOP. +#define ISCSI_CONNECT_FLAGS_NOP_OUTSTANDING (1 << 8L) /// Ready to wait for PDU. @@ -6302,6 +6393,9 @@ typedef struct iscsi_connection { /// Login response PDU. struct iscsi_pdu *login_response_pdu; + /// Internal connection identifier (key of iSCSI global vector hash map). + int id; + /// Connected TCP/IP socket. int sock; @@ -6314,6 +6408,9 @@ typedef struct iscsi_connection { /// iSCSI connection state. int state; + /// iSCSI connection login phase. + int login_phase; + /// Maximum receive DataSegment length in bytes. uint max_recv_ds_len; @@ -6329,6 +6426,12 @@ typedef struct iscsi_connection { /// Connection ID (CID). uint16_t cid; + /// Bit mask for connection state key negotiation. + uint16_t state_negotiated; + + /// Bit mask for session state key negotiation. + uint32_t session_state_negotiated; + /// Initiator Task Tag (ITT). uint32_t init_task_tag; @@ -6403,10 +6506,10 @@ typedef struct iscsi_pdu { /// DataSegmentLength. uint ds_len; - /// Remaining bytes of DataSegment buffer to read. - uint buf_len; + /// Position of DataSegment buffer for next read. + uint pos; - /// Associated connection. + /// Associated iSCSI connection. iscsi_connection *conn; /// CmdSN. @@ -6423,6 +6526,8 @@ int iscsi_target_node_access(iscsi_connection *conn, iscsi_target_node *target, iscsi_session *iscsi_session_create(iscsi_connection *conn, iscsi_target_node *target, const int type); // Creates and initializes an iSCSI session 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 + iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock); // Creates data structure for an iSCSI connection from iSCSI portal and TCP/IP socket int iscsi_connection_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // iSCSI connection destructor callback for hash map void iscsi_connection_destroy(iscsi_connection *conn); // Deallocates all resources acquired by iscsi_connection_create @@ -6433,6 +6538,8 @@ void iscsi_connection_schedule(iscsi_connection *conn); // Schedules an iSCSI co 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 +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 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 -- cgit v1.2.3-55-g7522 From 197f25c9c913940c87774c15bded85aa920be14b Mon Sep 17 00:00:00 2001 From: Sebastian Vater Date: Tue, 19 Aug 2025 16:53:14 +0200 Subject: [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. --- Doxyfile | 1 + pkg/config/CMakeLists.txt | 1 + pkg/config/iscsi.conf | 249 + pkg/config/server.conf | 199 + src/server/globals.c | 3 + src/server/globals.h | 7 + src/server/image.c | 74 +- src/server/image.h | 2 + src/server/iscsi.c | 12619 ++++++++++++++++++++++++++++++------- src/server/iscsi.h | 14677 ++++++++++++++++++++++++++++++-------------- src/server/net.c | 2 + src/server/server.c | 11 + 12 files changed, 20840 insertions(+), 7005 deletions(-) create mode 100644 pkg/config/iscsi.conf (limited to 'src') diff --git a/Doxyfile b/Doxyfile index 75de69e..ad61abb 100644 --- a/Doxyfile +++ b/Doxyfile @@ -51,5 +51,6 @@ GENERATE_XML = NO # Misc EXTRACT_ALL = YES EXTRACT_PRIVATE = NO +EXTRACT_INLINE = YES EXTRACT_STATIC = YES QUIET = NO diff --git a/pkg/config/CMakeLists.txt b/pkg/config/CMakeLists.txt index efbd2bf..b39bbac 100644 --- a/pkg/config/CMakeLists.txt +++ b/pkg/config/CMakeLists.txt @@ -6,6 +6,7 @@ project(dnbd3-config # define all configuration files set(DNBD3_CONFIG_FILES ${CMAKE_CURRENT_SOURCE_DIR}/alt-servers + ${CMAKE_CURRENT_SOURCE_DIR}/iscsi.conf ${CMAKE_CURRENT_SOURCE_DIR}/rpc.acl ${CMAKE_CURRENT_SOURCE_DIR}/server.conf) diff --git a/pkg/config/iscsi.conf b/pkg/config/iscsi.conf new file mode 100644 index 0000000..a3b1e8d --- /dev/null +++ b/pkg/config/iscsi.conf @@ -0,0 +1,249 @@ +; Using this file is recommended for actual iSCSI and SCSI emulation settings. This file will be +; parsed after server.conf and all values here will override the ones in server.conf. +; Also it is strictly recommended to set SCSI device specific details here. +; All SCSI device specific details start with a section scsi-device- following a pattern which +; can either be the full image name or a matching pattern like debian*.iso, e.g. +; [scsi-device-debian*.iso] will match all images starting with debian and end with the .iso +; file extension. +; scsi-device- sections will be processed in order, always inherit from the [iscsi] and [scsi] +; sections and are matched case sensitive + +[iscsi] +; Target name check level (warning) specifying invalid check levels will default to full +; validation!). Default is Full +; Full Target name is checked for full standaard compliance (recommended) +; Relaxed All invalid characters according to standard will be allowed if not an IQN, NAA or EUI. +; None No checking at all (be careful) +TargetNameCheck=Full + +; Header digest (warning) specifying invalid digest type will default to None!). +; Default is None +; CRC32C Use CRC32C for header validation +; None No digest and therefore no checking will be done (be careful) +HeaderDigest=None + +; Data digest (warning) specifying invalid digest type will default to None!). +; Default is None +; CRC32C Use CRC32C for data validation +; None No digest and therefore no checking will be done (be careful) +DataDigest=None + +; Login/Text Operational Session Text Key: Maximum receive DataSegmentLength. +; +* The initiator or target declares the maximum data segment length in bytes it can receive in an +; iSCSI PDU. +* The transmitter (initiator or target) is required to send PDUs with a data segment that does not +; exceed MaxRecvDataSegmentLength of the receiver. A target receiver is additionally limited by +; MaxBurstLength for solicited data and FirstBurstLength for unsolicited dataAn initiator MUST NOT +; send solicited PDUs exceeding MaxBurstLength nor unsolicited PDUs exceeding FirstBurstLength (or +; FirstBurstLength-Immediate Data Length if immediate data were sent). Valid values range from 512 +; to 16777215 bytes. Default is 8192 bytes (8 KiB) +MaxRecvDataSegmentLength=8192 + +; Maximum number of connections allowed per session. Default is 1 +MaxConnectionsPerSession=1 + +; Login/Text Operational Session Text Key: Maximum outstanding Ready To Transfer. +; +; The initiator and target negotiate the maximum number of outstanding R2Ts per task, excluding any +; implied initial R2T that might be part of that task. An R2T is considered outstanding until the last +; data PDU (with the F bit set to 1) is transferred or a sequence reception timeout is encountered for +; that data sequence. Allowed values range from 1 to 65536. Default is 1 +MaxOutstandingR2T=1 + +; Login/Text Operational Session Text Key: Default time to wait. +; +; The initiator and target negotiate the minimum time, in seconds, to wait before attempting an +; explicit/implicit logout or an active task reassignment after an unexpected connection termination +; or a connection reset. +; A value of 0 indicates that logout or active task reassignment can be attempted immediately. +; Valid values are from 0 to 3600 seconds (one hour). Default is 2 seconds +DefaultTime2Wait=2 + +; Login/Text Operational Session Text Key: Default time to retain. +; +; The initiator and target negotiate the maximum time, in seconds, after an initial wait (Time2Wait), +; before which an active task reassignment is still possible after an unexpected connection +; termination or a connection reset. +; This value is also the session state timeout if the connection in question is the last LOGGED_IN +; connection in the session. +; A value of 0 indicates that connection/task state is immediately discarded by the target. +; Valid values are from 0 to 3600 seconds (one hour). Default is 20 seconds +DefaultTime2Retain=20 + +; Login/Text Operational Session Text Key: First burst length. +; +; The initiator and target negotiate the maximum amount in bytes of unsolicited data an iSCSI +; initiator may send to the target during the execution of a single SCSI command. This covers the +; immediate data (if any) and the sequence of unsolicited Data-Out PDUs (if any) that follow the +; command. +; FirstBurstLength MUST NOT exceed MaxBurstLength. Valid lengths range from 512 to 16777215 bytes. +; Default is 65536 bytes (64 KiB) +FirstBurstLength=65536 + +; Login/Text Operational Session Text Key: Maximum burst length. +; +; The initiator and target negotiate the maximum SCSI data payload in bytes in a Data-In or a +; solicited Data-Out iSCSI sequence. A 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. Valid lengths +; range from 512 to 16777215 bytes. Default is 262144 bytes (256 KiB) +MaxBurstLength=262144 + +; Login/Text Operational Session Text Key: Initial Ready To Transfer. +; +; The InitialR2T key is used to turn off the default use of R2T for unidirectional operations +; and the output part of bidirectional commands, thus allowing an initiator to start sending data +; to a target as if it has received an initial R2T with Buffer Offset=Immediate Data Length and +; Desired Data Transfer Length=(min(FirstBurstLength, Expected Data Transfer Length) - Received +; Immediate Data Length). +; The default action is that R2T is required, unless both the initiator and the target send this +; key-pair attribute specifying InitialR2T=No. Only the first outgoing data burst (immediate data +; and/or separate PDUs) can be sent unsolicited (i.e., not requiring an explicit R2T). Default is +; true +InitialR2T=true + +; Login/Text Operational Session Text Key: Immediate data. +; +; 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. +; If ImmediateData is set to Yes and InitialR2T is set to Yes (default), then only immediate data +; are accepted in the first burst. If ImmediateData is set to No and InitialR2T is set to Yes, +; then the initiator MUST NOT send unsolicited data and the target MUST reject unsolicited data +; with the corresponding response code. +; If ImmediateData is set to No and InitialR2T is set to No, then the initiator MUST NOT send +; unsolicited immediate data but MAY send one unsolicited burst of Data-OUT PDUs. +; If ImmediateData is set to Yes and InitialR2T is set to No, then the initiator MAY send +; unsolicited immediate data and/or one unsolicited burst of Data-OUT PDUs. +; The following table is a summary of unsolicited data options: +; +; InitialR2T | ImmediateData | Unsolicited Data-Out PDUs | ImmediateData +; +----------+---------------+---------------------------+-------------+ +; | No | No | Yes | No | +; | No | Yes | Yes | Yes | +; | Yes | No | No | No | +; | Yes | Yes | No | Yes | +; +; Default is true +ImmediateData=true + +; Login/Text Operational Session Text Key: Data Protocol Data Unit (PDU) in order. +; +; "No" is used by iSCSI to indicate that the data PDUs within sequences can be in any order. +; "Yes" is used to indicate that data PDUs within sequences have to be at continuously +; increasing addresses and overlays are forbidden. Default is true +DataPDUInOrder=true + +; Login/Text Operational Session Text Key: Data sequence in order. +; +; A data sequence is a sequence of Data-In or Data-Out PDUs that end with a Data-In or Data-Out +; PDU with the F bit set to 1. A Data-Out sequence is sent either unsolicited or in response to +; an R2T. +; Sequences cover an offset-range. +; If DataSequenceInOrder is set to No, data PDU sequences may be transferred in any order. +; If DataSequenceInOrder is set to Yes, data sequences MUST be transferred using continuously +; non-decreasing sequence offsets (R2T buffer offset for writes, or the smallest SCSI Data-In +; buffer offset within a read data sequence). +; If DataSequenceInOrder is set to Yes, a target may retry at most the last R2T, and an +; initiator may at most request retransmission for the last read data sequence. For this reason, +; if ErrorRecoveryLevel is not 0 and DataSequenceInOrder is set to Yes, then MaxOutstandingR2T +; MUST be set to 1. Default is true +DataSequenceInOrder=true + +; Login/Text Operational Session Text Key: Error recovery level. +; +; The initiator and target negotiate the recovery level supported. Recovery levels represent a +; combination of recovery capabilities. Each recovery level includes all the capabilities of the +; lower recovery levels and adds some new ones to them. +; In the description of recovery mechanisms, certain recovery classes are specified. Allowed +; error levels are 0, 1 and 2. Default is 0 +ErrorRecoveryLevel=0 + +[scsi] +; Valid devices types for emulated device (warning) specifying invalid types will default to direct +; access device!). Default is Direct +; Direct Direct access device (e.g. hard drive) +; Sequential Sequential access device (e.g. tape) +; WriteOnce Write once device (WORM) +; ReadOnlyDirect Read only direct access device +; OpticalMemory Optical memory device (e.g. CD-ROM, DVD, BluRay) +; MediaChanger Media changer device +DeviceType=Direct + +; Physical block size of emulated device, rounded up to nearest power of two). Default is 512 for +; supporting ancient systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed +; physical block sizes range from 256 to 32768 bytes (32 KiB). Default is 512 bytes +PhysicalBlockSize=512 + +; Logical block size of emulated device, rounded up to nearest power of two). Default is 4096 for +; supporting modern systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed +; logical block sizes range from 256 to 32768 bytes (32 KiB). Default is 4096 bytes (4 KiB) +LogicalBlockSize=4096 + +; Device is emulated as removable. Should be enabled for CD-ROM, DVD and BluRay and disabled for +; hard drives. Default is true +Removable=true + +; Device is emulated as supporting UNMAP. Should alwas be disabled, since writing is NOT supported +; yet. Default is false +UNMAP=false + +; Device is emulated as non-rotating (e.g. SSD or NVMe), recommended to enable if image is stoed +; on a non-rotating device like a SSD or NVMe. Default is false +NoRotation=false + +; Device is emulated as physically read only. No possibility to enable writes in any case. Use +; for CD-ROM, DVD and BluRay devices. Default is false +PhysicalReadOnly=false + +; Device is emulated as write protected. Should always be enabled, since writing is NOT supported +; yet. Unlike physical read only, write protect state can be changed. Default is true +WriteProtect=true + +; Device is emulated supporting write cache. Should always be disabled, since writing is NOT +; supported yet. Default is false +WriteCache=false + +; SCSI device specific configuration for *.iso (case sensitive), i.e. all image files which +; end with .iso extension. Block sizes are set to 2 KiB for CD-ROM, DVD and BluRay type devices +; and the physical read only emulation flag is enabled +[scsi-device-*.iso] +; Physical block size of emulated device, rounded up to nearest power of two). Default is 512 for +; supporting ancient systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed +; physical block sizes range from 256 to 32768 bytes (32 KiB). Default is 512 bytes +PhysicalBlockSize=2048 + +; Logical block size of emulated device, rounded up to nearest power of two). Default is 4096 for +; supporting modern systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed +; logical block sizes range from 256 to 32768 bytes (32 KiB). Default is 4096 bytes (4 KiB) +LogicalBlockSize=2048 + +; Device is emulated as removable. Should be enabled for CD-ROM, DVD and BluRay and disabled for +; hard drives. Default is true +Removable=true + +; Device is emulated as physically read only. No possibility to enable writes in any case. Use +; for CD-ROM, DVD and BluRay devices. Default is false +PhysicalReadOnly=true + +; SCSI device specific configuration for *.ISO (case sensitive), i.e. all image files which +; end with .ISO extension. Block sizes are set to 2 KiB for CD-ROM, DVD and BluRay type devices +; and the physical read only emulation flag is enabled +[scsi-device-*.ISO] +; Physical block size of emulated device, rounded up to nearest power of two). Default is 512 for +; supporting ancient systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed +; physical block sizes range from 256 to 32768 bytes (32 KiB). Default is 512 bytes +PhysicalBlockSize=2048 + +; Logical block size of emulated device, rounded up to nearest power of two). Default is 4096 for +; supporting modern systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed +; logical block sizes range from 256 to 32768 bytes (32 KiB). Default is 4096 bytes (4 KiB) +LogicalBlockSize=2048 + +; Device is emulated as removable. Should be enabled for CD-ROM, DVD and BluRay and disabled for +; hard drives. Default is true +Removable=true + +; Device is emulated as physically read only. No possibility to enable writes in any case. Use +; for CD-ROM, DVD and BluRay devices. Default is false +PhysicalReadOnly=true diff --git a/pkg/config/server.conf b/pkg/config/server.conf index 22bd14a..78ec3e7 100644 --- a/pkg/config/server.conf +++ b/pkg/config/server.conf @@ -58,6 +58,9 @@ pretendClient=false ; be replicated unless you manually free up more disk space. autoFreeDiskSpaceDelay=10h +; Specifies whether the iSCSI server should be initialized, enabled and used upon start of DNBD3 server. +iSCSIServer=true + [limits] maxClients=2000 maxImages=1000 @@ -89,3 +92,199 @@ consoleMask=ERROR WARNING MINOR INFO ; Whether timestamps should be output to console too (or just to file if false) consoleTimestamps=false + +[iscsi] +; Target name check level (warning) specifying invalid check levels will default to full +; validation!). Default is Full +; Full Target name is checked for full standaard compliance (recommended) +; Relaxed All invalid characters according to standard will be allowed if not an IQN, NAA or EUI. +; None No checking at all (be careful) +TargetNameCheck=Full + +; Header digest (warning) specifying invalid digest type will default to None!). +; Default is None +; CRC32C Use CRC32C for header validation +; None No digest and therefore no checking will be done (be careful) +HeaderDigest=None + +; Data digest (warning) specifying invalid digest type will default to None!). +; Default is None +; CRC32C Use CRC32C for data validation +; None No digest and therefore no checking will be done (be careful) +DataDigest=None + +; Login/Text Operational Session Text Key: Maximum receive DataSegmentLength. +; +* The initiator or target declares the maximum data segment length in bytes it can receive in an +; iSCSI PDU. +* The transmitter (initiator or target) is required to send PDUs with a data segment that does not +; exceed MaxRecvDataSegmentLength of the receiver. A target receiver is additionally limited by +; MaxBurstLength for solicited data and FirstBurstLength for unsolicited dataAn initiator MUST NOT +; send solicited PDUs exceeding MaxBurstLength nor unsolicited PDUs exceeding FirstBurstLength (or +; FirstBurstLength-Immediate Data Length if immediate data were sent). Valid values range from 512 +; to 16777215 bytes. Default is 8192 bytes (8 KiB) +MaxRecvDataSegmentLength=8192 + +; Maximum number of connections allowed per session. Default is 1 +MaxConnectionsPerSession=1 + +; Login/Text Operational Session Text Key: Maximum outstanding Ready To Transfer. +; +; The initiator and target negotiate the maximum number of outstanding R2Ts per task, excluding any +; implied initial R2T that might be part of that task. An R2T is considered outstanding until the last +; data PDU (with the F bit set to 1) is transferred or a sequence reception timeout is encountered for +; that data sequence. Allowed values range from 1 to 65536. Default is 1 +MaxOutstandingR2T=1 + +; Login/Text Operational Session Text Key: Default time to wait. +; +; The initiator and target negotiate the minimum time, in seconds, to wait before attempting an +; explicit/implicit logout or an active task reassignment after an unexpected connection termination +; or a connection reset. +; A value of 0 indicates that logout or active task reassignment can be attempted immediately. +; Valid values are from 0 to 3600 seconds (one hour). Default is 2 seconds +DefaultTime2Wait=2 + +; Login/Text Operational Session Text Key: Default time to retain. +; +; The initiator and target negotiate the maximum time, in seconds, after an initial wait (Time2Wait), +; before which an active task reassignment is still possible after an unexpected connection +; termination or a connection reset. +; This value is also the session state timeout if the connection in question is the last LOGGED_IN +; connection in the session. +; A value of 0 indicates that connection/task state is immediately discarded by the target. +; Valid values are from 0 to 3600 seconds (one hour). Default is 20 seconds +DefaultTime2Retain=20 + +; Login/Text Operational Session Text Key: First burst length. +; +; The initiator and target negotiate the maximum amount in bytes of unsolicited data an iSCSI +; initiator may send to the target during the execution of a single SCSI command. This covers the +; immediate data (if any) and the sequence of unsolicited Data-Out PDUs (if any) that follow the +; command. +; FirstBurstLength MUST NOT exceed MaxBurstLength. Valid lengths range from 512 to 16777215 bytes. +; Default is 65536 bytes (64 KiB) +FirstBurstLength=65536 + +; Login/Text Operational Session Text Key: Maximum burst length. +; +; The initiator and target negotiate the maximum SCSI data payload in bytes in a Data-In or a +; solicited Data-Out iSCSI sequence. A 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. Valid lengths +; range from 512 to 16777215 bytes. Default is 262144 bytes (256 KiB) +MaxBurstLength=262144 + +; Login/Text Operational Session Text Key: Initial Ready To Transfer. +; +; The InitialR2T key is used to turn off the default use of R2T for unidirectional operations +; and the output part of bidirectional commands, thus allowing an initiator to start sending data +; to a target as if it has received an initial R2T with Buffer Offset=Immediate Data Length and +; Desired Data Transfer Length=(min(FirstBurstLength, Expected Data Transfer Length) - Received +; Immediate Data Length). +; The default action is that R2T is required, unless both the initiator and the target send this +; key-pair attribute specifying InitialR2T=No. Only the first outgoing data burst (immediate data +; and/or separate PDUs) can be sent unsolicited (i.e., not requiring an explicit R2T). Default is +; true +InitialR2T=true + +; Login/Text Operational Session Text Key: Immediate data. +; +; 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. +; If ImmediateData is set to Yes and InitialR2T is set to Yes (default), then only immediate data +; are accepted in the first burst. If ImmediateData is set to No and InitialR2T is set to Yes, +; then the initiator MUST NOT send unsolicited data and the target MUST reject unsolicited data +; with the corresponding response code. +; If ImmediateData is set to No and InitialR2T is set to No, then the initiator MUST NOT send +; unsolicited immediate data but MAY send one unsolicited burst of Data-OUT PDUs. +; If ImmediateData is set to Yes and InitialR2T is set to No, then the initiator MAY send +; unsolicited immediate data and/or one unsolicited burst of Data-OUT PDUs. +; The following table is a summary of unsolicited data options: +; +; InitialR2T | ImmediateData | Unsolicited Data-Out PDUs | ImmediateData +; +----------+---------------+---------------------------+-------------+ +; | No | No | Yes | No | +; | No | Yes | Yes | Yes | +; | Yes | No | No | No | +; | Yes | Yes | No | Yes | +; +; Default is true +ImmediateData=true + +; Login/Text Operational Session Text Key: Data Protocol Data Unit (PDU) in order. +; +; "No" is used by iSCSI to indicate that the data PDUs within sequences can be in any order. +; "Yes" is used to indicate that data PDUs within sequences have to be at continuously +; increasing addresses and overlays are forbidden. Default is true +DataPDUInOrder=true + +; Login/Text Operational Session Text Key: Data sequence in order. +; +; A data sequence is a sequence of Data-In or Data-Out PDUs that end with a Data-In or Data-Out +; PDU with the F bit set to 1. A Data-Out sequence is sent either unsolicited or in response to +; an R2T. +; Sequences cover an offset-range. +; If DataSequenceInOrder is set to No, data PDU sequences may be transferred in any order. +; If DataSequenceInOrder is set to Yes, data sequences MUST be transferred using continuously +; non-decreasing sequence offsets (R2T buffer offset for writes, or the smallest SCSI Data-In +; buffer offset within a read data sequence). +; If DataSequenceInOrder is set to Yes, a target may retry at most the last R2T, and an +; initiator may at most request retransmission for the last read data sequence. For this reason, +; if ErrorRecoveryLevel is not 0 and DataSequenceInOrder is set to Yes, then MaxOutstandingR2T +; MUST be set to 1. Default is true +DataSequenceInOrder=true + +; Login/Text Operational Session Text Key: Error recovery level. +; +; The initiator and target negotiate the recovery level supported. Recovery levels represent a +; combination of recovery capabilities. Each recovery level includes all the capabilities of the +; lower recovery levels and adds some new ones to them. +; In the description of recovery mechanisms, certain recovery classes are specified. Allowed +; error levels are 0, 1 and 2. Default is 0 +ErrorRecoveryLevel=0 + +[scsi] +; Valid devices types for emulated device (warning) specifying invalid types will default to direct +; access device!). Default is Direct +; Direct Direct access device (e.g. hard drive) +; Sequential Sequential access device (e.g. tape) +; WriteOnce Write once device (WORM) +; ReadOnlyDirect Read only direct access device +; OpticalMemory Optical memory device (e.g. CD-ROM, DVD, BluRay) +; MediaChanger Media changer device +DeviceType=Direct + +; Physical block size of emulated device, rounded up to nearest power of two). Default is 512 for +; supporting ancient systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed +; physical block sizes range from 256 to 32768 bytes (32 KiB). Default is 512 bytes +PhysicalBlockSize=512 + +; Logical block size of emulated device, rounded up to nearest power of two). Default is 4096 for +; supporting modern systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed +; logical block sizes range from 256 to 32768 bytes (32 KiB). Default is 4096 bytes (4 KiB) +LogicalBlockSize=4096 + +; Device is emulated as removable. Should be enabled for CD-ROM, DVD and BluRay and disabled for +; hard drives. Default is true +Removable=true + +; Device is emulated as supporting UNMAP. Should alwas be disabled, since writing is NOT supported +; yet. Default is false +UNMAP=false + +; Device is emulated as non-rotating (e.g. SSD or NVMe), recommended to enable if image is stoed +; on a non-rotating device like a SSD or NVMe. Default is false +NoRotation=false + +; Device is emulated as physically read only. No possibility to enable writes in any case. Use +; for CD-ROM, DVD and BluRay devices. Default is false +PhysicalReadOnly=false + +; Device is emulated as write protected. Should always be enabled, since writing is NOT supported +; yet. Unlike physical read only, write protect state can be changed. Default is true +WriteProtect=true + +; Device is emulated supporting write cache. Should always be disabled, since writing is NOT +; supported yet. Default is false +WriteCache=false 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 @@ -332,6 +333,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. 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) { @@ -333,6 +335,38 @@ dnbd3_image_t* image_byId(int imgId) return NULL; } +/** + * 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 @@ -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 @@ -18,7 +18,11 @@ * */ +#include +#include +#include #include +#include #include #include #include @@ -26,9 +30,23 @@ #include #include #include +#include +#include #include +#include +#include +#include #include +#include + +#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; } @@ -443,20 +158,51 @@ uint8_t *iscsi_sprintf_alloc(const char *format, ... ) return buf; } +/** + * @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; + iscsi_list_enqueue( &map->list, &entry->node ); - map->count++; - - 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,1793 +765,7966 @@ 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" ); - return bhs_pkt; - } +/// iSCSI global vector. MUST be initialized with iscsi_create before any iSCSI functions are used. +iscsi_globals *iscsi_globvec = NULL; - 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; +/// 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; - memset( bhs_pkt->opcode_spec_fields, 0, sizeof(bhs_pkt->opcode_spec_fields) ); - return bhs_pkt; -} +/// 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 } +}; -/** - * @brief Free resources allocated by iscsi_create_packet. - * - * Deallocates all aquired resources by iscsi_create_packet. - * - * @param[in] packet_data Pointer to packet data to deallocate. If this is - * NULL, this function does nothing. - */ -void iscsi_destroy_packet(iscsi_bhs_packet *packet_data) -{ - if (packet_data != NULL) - free( packet_data ); -} +/// 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 Allocate and initialize an iSCSI AHS packet and append to existing data stream. + * @brief Initializes a global key and value pair with type and list / range informations for fast access. * - * 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 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 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. + * @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. */ -iscsi_bhs_packet *iscsi_append_ahs_packet(iscsi_bhs_packet *packet_data, const uint32_t ahs_len) +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 ) { - packet_data = iscsi_create_packet(); + 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 ( packet_data == NULL ) - return packet_data; - } + if ( key_value_pair == NULL ) { + logadd( LOG_ERROR, "iscsi_global_key_value_pair_init: Out of memory allocating key value pair" ); - 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)); + return -1; + } - 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" ); + const uint key_len = (uint) (strlen( (char *) lut[i].key ) + 1U); - return NULL; - } + 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; - packet_data = (iscsi_bhs_packet *) realloc( packet_data, new_pkt_size ); + const int rc = iscsi_hashmap_put( key_value_pairs, (uint8_t *) lut[i].key, key_len, (uint8_t *) key_value_pair ); - if ( packet_data == NULL ) { - logadd( LOG_ERROR, "iscsi_append_ahs_packet: Out of memory while allocating iSCSI AHS packet data for appending" ); + if ( rc < 0 ) { + free( key_value_pair ); - return packet_data; + return rc; + } } - 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); - - return packet_data; + return 0; } /** - * @brief Counts number of AHS packets in an iSCSI data packet stream. + * @brief Allocates and initializes the iSCSI global vector structure. * - * Gets the total number of AHS packets. + * 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 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. + * @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_get_ahs_packets(const iscsi_bhs_packet *packet_data) +int iscsi_create() { - 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; + if ( iscsi_globvec == NULL ) { + iscsi_globals *globvec = (iscsi_globals *) malloc( sizeof(struct iscsi_globals) ); - 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 ) { + logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector" ); - 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_rwlock_unlock( &iscsi_globvec_rwlock ); - return count; -} + return -1; + } -/** - * @brief Retrieves the pointer to an specific AHS packet by index. - * - * Gets the pointer of an AHS packet by specified index. - * - * @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. - */ -iscsi_ahs_packet *iscsi_get_ahs_packet(const iscsi_bhs_packet *packet_data, const int index) -{ - if ( packet_data == NULL || (packet_data->total_ahs_len == 0) ) - return NULL; + globvec->devices = iscsi_hashmap_create( _maxImages ); - 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 ( globvec->devices == NULL ) { + logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector devices hash map" ); - while ( (int32_t) ahs_len > 0L ) { - if ( count-- < 0L ) - return ahs_pkt; + free( globvec ); + pthread_rwlock_unlock( &iscsi_globvec_rwlock ); - 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); + return -1; + } - 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 - } + 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" ); - logadd( LOG_ERROR, "iscsi_get_ahs_packet: Specified index for AHS packet does not exist" ); + iscsi_hashmap_destroy( globvec->devices ); + free( globvec ); + pthread_rwlock_unlock( &iscsi_globvec_rwlock ); - return NULL; -} + return -1; + } -/** - * @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(); + globvec->portal_groups = iscsi_hashmap_create( 1U ); - if ( packet_data == NULL ) - return packet_data; - } + if ( globvec->portal_groups == NULL ) { + logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector portal groups hash map" ); - 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" ); + pthread_rwlock_destroy( &globvec->devices_rwlock ); + iscsi_hashmap_destroy( globvec->devices ); + free( globvec ); + pthread_rwlock_unlock( &iscsi_globvec_rwlock ); - return NULL; - } + return -1; + } - 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; + 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" ); - packet_data = (iscsi_bhs_packet *) realloc( packet_data, new_pkt_size ); + 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 ); - if ( packet_data == NULL ) { - logadd( LOG_ERROR, "iscsi_append_header_digest_packet: Out of memory while allocating iSCSI header digest packet data for appending" ); + return -1; + } - return packet_data; - } + globvec->target_nodes = iscsi_hashmap_create( _maxImages ); - memset( (((uint8_t *) packet_data) + old_pkt_size), 0, header_digest_size ); + if ( globvec->target_nodes == NULL ) { + logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector target nodes hash map" ); - return packet_data; -} + 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 ); -/** - * @brief Allocate and initialize an iSCSI DS packet and append to existing data stream. - * - * 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. - * - * @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; + return -1; + } - if ( packet_data == NULL ) { - packet_data = iscsi_create_packet(); + 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" ); - if ( packet_data == NULL ) - return packet_data; - } + 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 ); - 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); + return -1; + } - packet_data = (iscsi_bhs_packet *) realloc( packet_data, new_pkt_size ); + globvec->sessions = iscsi_hashmap_create( _maxClients ); - if ( packet_data == NULL ) { - logadd( LOG_ERROR, "iscsi_append_ds_packet: Out of memory while allocating iSCSI DS packet data for appending" ); + if ( globvec->sessions == NULL ) { + logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector sessions hash map" ); - return packet_data; - } + 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 ); - 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) ); + return -1; + } - return packet_data; -} + 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" ); -/// 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_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 ); -/** - * @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 -1; + } - return crc32c; -} + globvec->session_key_value_pairs = iscsi_hashmap_create( ((sizeof(iscsi_session_key_value_pair_lut) / sizeof(struct iscsi_key_value_pair_lut_entry)) - 1) ); -/** - * @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->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_put_be32( hdr_digest, crc32c ); -} + 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 ); -/** - * @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; + return -1; + } - return iscsi_get_be32(pkt_crc32c) == crc32c; -} + int rc = iscsi_global_key_value_pair_init( globvec->session_key_value_pairs, &iscsi_session_key_value_pair_lut[0] ); -/** - * @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 ( rc < 0 ) { + logadd( LOG_ERROR, "iscsi_create: Out of memory while initializing iSCSI global vector session key and value pairs hash map" ); - iscsi_put_be32( data_digest, crc32c ); -} + 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 ); -/** - * @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; + return -1; + } - return iscsi_get_be32(pkt_crc32c) == crc32c; -} + globvec->connection_key_value_pairs = iscsi_hashmap_create( ((sizeof(iscsi_connection_key_value_pair_lut) / sizeof(struct iscsi_key_value_pair_lut_entry)) - 1) ); -/** - * @brief Validates a single text key / value pair according to iSCSI specs. - * - * Validates an iSCSI protocol key and value pair for compliance - * with the iSCSI specs. - * - * @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. - */ -static int iscsi_validate_text_key_value_pair(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 ); + 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" ); - if ( key_end == NULL ) - return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Missing separator '=' for key / value pair -> invalid iSCSI packet data + 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 ); - const uint key_len = (uint) (key_end - packet_data); + return -1; + } + + rc = iscsi_global_key_value_pair_init( globvec->connection_key_value_pairs, &iscsi_connection_key_value_pair_lut[0] ); - if ( key_len == 0 ) - return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Zero length is not allowed -> invalid iSCSI packet data + if ( rc < 0 ) { + logadd( LOG_ERROR, "iscsi_create: Out of memory while initializing iSCSI global vector connection key and value pairs hash map" ); - if ( key_len > ISCSI_TEXT_KEY_MAX_LEN ) - return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; + 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 ); - 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; + return -1; + } - if ( val_len > max_len ) - return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Value exceeds maximum length -> invalid iSCSI packet data + globvec->scsi_device_config = iscsi_hashmap_create( 0U ); - return (int) (key_len + 1UL + val_len + 1UL); // Number of bytes for processed key / value pair (+1 for '=' and NUL terminator) -} + if ( globvec->scsi_device_config == NULL ) { + logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector SCSI device configuration" ); -/** - * @brief Validates all text key / value pairs according to iSCSI specs. - * - * Validates all iSCSI protocol key and value pairs for - * compliance with the iSCSI specs. - * - * @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. - */ -static int iscsi_validate_key_value_pairs(const uint8_t *packet_data, uint len) -{ - if ( len == 0 ) - return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Zero length is not allowed -> invalid iSCSI packet data + 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 ); - int offset = 0L; + return -1; + } - while ( ((uint) offset < len) && (packet_data[offset] != '\0') ) { - const int rc = iscsi_validate_text_key_value_pair( (packet_data + offset), (len - offset) ); + 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" ); - if ( rc < ISCSI_VALIDATE_PACKET_RESULT_OK ) - return rc; + 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 ); - offset += rc; + 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; } - 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; + pthread_rwlock_unlock( &iscsi_globvec_rwlock ); + + return 0; } /** - * @brief Check if valid iSCSI packet and validate if necessarily. - * - * 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. + * @brief Deallocates all resources acquired by iscsi_create. * - * @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. + * 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_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint32_t len, const int header_digest_size, const int data_digest_size) +void iscsi_destroy() { - 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; + pthread_rwlock_wrlock( &iscsi_globvec_rwlock ); - 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 ( 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; + iscsi_globals *globvec = iscsi_globvec; - const uint8_t opcode = ISCSI_GET_OPCODE(packet_data->opcode); + if ( globvec != NULL ) { + iscsi_globvec = NULL; - 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 + 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; - const iscsi_nop_out_packet *nop_out_pkt = (const iscsi_nop_out_packet *) packet_data; + 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; - 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 + 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; - 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 + pthread_rwlock_destroy( &globvec->sessions_rwlock ); + iscsi_hashmap_iterate( globvec->sessions, iscsi_session_destroy_callback, NULL ); + iscsi_hashmap_destroy( globvec->sessions ); + globvec->sessions = NULL; - const iscsi_scsi_cmd_packet *scsi_cmd_pkt = (const iscsi_scsi_cmd_packet *) packet_data; + 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; - 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 + 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; - 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_rwlock_destroy( &globvec->devices_rwlock ); + iscsi_hashmap_iterate( globvec->devices, iscsi_device_destroy_callback, NULL ); + iscsi_hashmap_destroy( globvec->devices ); + globvec->devices = NULL; - const iscsi_task_mgmt_func_req_packet *task_mgmt_func_req_pkt = (const iscsi_task_mgmt_func_req_packet *) packet_data; + free( globvec ); + } - 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 + pthread_rwlock_unlock( &iscsi_globvec_rwlock ); +} - break; - } - 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 +/** + * @brief Parses an INI configuration value and returns an integer representation of string with special boolean and suffixes handling. + * + * 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] value Pointer to string for parsing. May + * NOT be NULL, so be careful. + * @return Parsed integer value or -2147483648 + * in case of an error. + */ +static int32_t iscsi_config_parse_int(const uint8_t *value) +{ + if ( *value == '\0' ) + return -2147483648L; - const iscsi_login_req_packet *login_req_pkt = (const iscsi_login_req_packet *) packet_data; + 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; - 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; + uint8_t *val_end; + int64_t rc = (int64_t) strtoll( (char *) value, (char **) &val_end, 10 ); - 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 ( value == val_end ) + return -2147483648L; - return iscsi_validate_key_value_pairs( ((const uint8_t *) login_req_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len ); + while ( (*val_end == '\t') || (*val_end == ' ') ) { + val_end++; + } + switch ( *val_end ) { + case '\0' : { 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 - - const iscsi_text_req_packet *text_req_pkt = (const iscsi_text_req_packet *) packet_data; - - 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 - - return iscsi_validate_key_value_pairs( ((const uint8_t *) text_req_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len ); + case 'm' : { + rc *= 60LL; + val_end++; 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 - - const iscsi_scsi_data_out_req_packet *scsi_data_out_req_pkt = (const iscsi_scsi_data_out_req_packet *) packet_data; - - 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 + case 'h' : { + rc *= 3600LL; + val_end++; 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 - - const iscsi_logout_req_packet *logout_req_pkt = (const iscsi_logout_req_packet *) packet_data; - - 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 + case 'd' : { + rc *= 86400LL; + val_end++; 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 + default : { + const uint8_t c = (*val_end++ | ('a' - 'A')); + const bool ten = ((*val_end | ('a' - 'A')) == 'b'); - const iscsi_snack_req_packet *snack_req_pkt = (const iscsi_snack_req_packet *) packet_data; + switch ( c ) { + case 'k' : { + rc = (ten ? (rc * 1000LL) : (rc << 10LL)); - 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 'm' : { + rc = (ten ? (rc * 1000000LL) : (rc << 20LL)); - 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 'g' : { + rc = (ten ? (rc * 1000000000LL) : (rc << 30LL)); - const iscsi_nop_in_packet *nop_in_pkt = (const iscsi_nop_in_packet *) packet_data; + break; + } + case 't' : { + rc = (ten ? (rc * 1000000000000LL) : (rc << 40LL)); - 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 'p' : { + rc = (ten ? (rc * 1000000000000000LL) : (rc << 50LL)); - 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 'e' : { + rc = (ten ? (rc * 1000000000000000000LL) : (rc << 60LL)); + + break; + } + default : { + return -2147483648L; - const iscsi_scsi_response_packet *scsi_response_pkt = (iscsi_scsi_response_packet *) packet_data; + break; + } + } - 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 + if ( ten ) + val_end++; 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 + } - const iscsi_task_mgmt_func_response_packet *task_mgmt_func_response_pkt = (const iscsi_task_mgmt_func_response_packet *) packet_data; + if ( *val_end != '\0' ) + return -2147483648L; - 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 + if ( rc < -2147483647LL ) + rc = -2147483647LL; + else if ( rc > 2147483647LL ) + rc = 2147483647LL; - break; + return (int32_t) rc; +} + +/** + * @brief Callback function from DNBD3 INI parser invoked for handling a specific key=value pair in a specified INI section. + * + * 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] 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; } - 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 + } 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 ); - const iscsi_login_response_packet *login_response_pkt = (const iscsi_login_response_packet *) packet_data; + 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 ( (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; + if ( key_len == 0U ) + return 0; - 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 + uint8_t *hash_key = iscsi_hashmap_key_create( pattern, key_len ); - return iscsi_validate_key_value_pairs( ((const uint8_t *) login_response_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len ); + 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" ); - break; + return 0; } - 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 - const iscsi_text_response_packet *text_response_pkt = (const iscsi_text_response_packet *) 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 ); - 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 + if ( scsi_device_config == NULL ) { + scsi_device_config = (iscsi_scsi_device_config *) malloc( sizeof(struct iscsi_scsi_device_config) ); - return iscsi_validate_key_value_pairs( ((const uint8_t *) text_response_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len ); + if ( scsi_device_config == NULL ) { + logadd( LOG_ERROR, "iscsi_config_load_from_ini: Out of memory allocating memory for iSCSI SCSI device configuration" ); - 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 + iscsi_hashmap_key_destroy( hash_key ); - const iscsi_scsi_data_in_response_packet *scsi_data_in_pkt = (const iscsi_scsi_data_in_response_packet *) packet_data; + return 0; + } - 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 + scsi_device_config->flags = 0; - 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 + if ( (globvec->flags & ISCSI_GLOBALS_FLAGS_INIT_R2T) != 0 ) + scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_INIT_R2T; - const iscsi_logout_response_packet *logout_response_pkt = (const iscsi_logout_response_packet *) packet_data; + if ( (globvec->flags & ISCSI_GLOBALS_FLAGS_IMMEDIATE_DATA) != 0 ) + scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_IMMEDIATE_DATA; - 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 + 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; - 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 + 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; - const iscsi_r2t_packet *r2t_pkt = (const iscsi_r2t_packet *) packet_data; + if ( (globvec->flags & ISCSI_GLOBALS_FLAGS_SCSI_IO_REMOVABLE) != 0 ) + scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_REMOVABLE; - 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 + if ( (globvec->flags & ISCSI_GLOBALS_FLAGS_SCSI_IO_UNMAP) != 0 ) + scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_UNMAP; - 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 + 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; - const iscsi_async_msg_packet *async_msg_pkt = (const iscsi_async_msg_packet *) packet_data; + 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 ( (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 + 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; - break; - } - case ISCSI_SERVER_VENDOR_CODE1 : - case ISCSI_SERVER_VENDOR_CODE2 : - case ISCSI_SERVER_VENDOR_CODE3 : { - 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 + 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; - const iscsi_reject_packet *reject_pkt = (const iscsi_reject_packet *) packet_data; + 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; - 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 + rc = iscsi_hashmap_put( globvec->scsi_device_config, hash_key, key_len, (uint8_t *) scsi_device_config ); - break; - } - default : { - return ISCSI_VALIDATE_PACKET_RESULT_ERROR_INVALID_OPCODE; + if ( rc < 0 ) { + free( scsi_device_config ); + iscsi_hashmap_key_destroy( hash_key ); - break; + return 0; + } + } else { + iscsi_hashmap_key_destroy( hash_key ); } - } - if ( (header_digest_size != 0) && (iscsi_validate_header_digest( packet_data ) == 0) ) - return ISCSI_VALIDATE_PACKET_RESULT_ERROR_CRC32C_HDR_DIGEST; + 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 ); - if ( (data_digest_size != 0) && (iscsi_validate_data_digest( packet_data, header_digest_size ) == 0) ) - return ISCSI_VALIDATE_PACKET_RESULT_ERROR_CRC32C_DATA_DIGEST; + 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 ISCSI_VALIDATE_PACKET_RESULT_OK; // All tests for iSCSI passed, return 0 + return 1; } /** - * @brief Extracts a single text key / value pairs out of an iSCSI packet into a hash map. + * @brief Loads iSCSI server configuration from INI file. * - * Parses and extracts a specific key and value pair out of an iSCSI packet - * data stream amd puts the extracted data into a hash map to be used by - * the iSCSI implementation. + * This function parses the INI configuration file + * and assigns it to the config section of the iSCSI + * global vector. * - * @param[in] 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. - * @param[in] len Length of the remaining packet data. - * @return Number of bytes used by the extracted 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_parse_text_key_value_pair(iscsi_hashmap *pairs, 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 ) { - logadd( LOG_ERROR, "iscsi_parse_text_key_value_pair: Key / value separator '=' not found" ); + if ( name == NULL ) + return -1; - return -1L; + if ( !file_isReadable( name ) ) { + free( name ); + + return 0; } - const uint key_len = (uint) (key_end - packet_data); + 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 ); - if ( key_len == 0 ) { - logadd( LOG_ERROR, "iscsi_parse_text_key_value_pair: Empty key found which is NOT allowed according to iSCSI specs" ); + name = (char *) iscsi_sprintf_alloc( "%s/%s", _configDir, ISCSI_GLOBALS_CONFIG_FILENAME ); - return -1L; + if ( name == NULL ) { + pthread_mutex_unlock( &globvec->scsi_device_config_mutex ); + + 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)" ); + if ( !file_isReadable( name ) ) { + pthread_mutex_unlock( &globvec->scsi_device_config_mutex ); + free( name ); - return -1L; + return 0; } - const uint hash_key_len = (key_len + 1UL); - uint8_t *hash_key = iscsi_hashmap_key_create( packet_data, hash_key_len ); + ini_parse( name, iscsi_config_load_from_ini, (void *) globvec ); + pthread_mutex_unlock( &globvec->scsi_device_config_mutex ); + free( name ); - if ( hash_key == NULL ) - return -1L; + return 1; +} - hash_key[key_len] = '\0'; +/** + * @brief Finds an iSCSI SCSI device configuration by name using pattern matching. + * + * Callback function for each element while iterating + * through the iSCSI SCSI device configuration 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 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. + */ +int iscsi_config_get_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) +{ + 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) ); + + if ( rc == FNM_NOMATCH ) + return 0; + + if ( rc != 0 ) + return -2; + + scsi_device_config_find->scsi_device_config = (iscsi_scsi_device_config *) value; + + return -1; +} + +/** + * @brief Retrieves a configuration value either from the iSCSI global vector or for a specified SCSI device name. + * + * This function uses wildcard matching + * only if the SCSI device name does NOT + * have a direct match. + * + * @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. + */ +int32_t iscsi_config_get(uint8_t *name, const int type) +{ + if ( name != NULL ) { + const uint key_len = (uint) (strlen( (char *) name ) + 1U); + uint8_t *hash_key = iscsi_hashmap_key_create( name, key_len ); + + if ( hash_key == NULL ) { + logadd( LOG_ERROR, "iscsi_config_get: Out of memory allocating memory for iSCSI SCSI device configuration key" ); + + return -1L; + } + + pthread_mutex_lock( &iscsi_globvec->scsi_device_config_mutex ); + + 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 ); + + if ( rc < 0 ) { + iscsi_scsi_device_config_find scsi_device_config_find = {NULL, name}; + + rc = iscsi_hashmap_iterate(iscsi_globvec->scsi_device_config, iscsi_config_get_callback, (uint8_t *) &scsi_device_config_find ); + + scsi_device_config = scsi_device_config_find.scsi_device_config; + + 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 ( new_scsi_device_config == NULL ) { + logadd( LOG_ERROR, "iscsi_config_get: Out of memory allocating memory for new iSCSI SCSI device configuration" ); + + pthread_mutex_unlock( &iscsi_globvec->scsi_device_config_mutex ); + iscsi_hashmap_key_destroy( hash_key ); + + return -1L; + } + + 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 ); + + 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; + } + } + + pthread_mutex_unlock( &iscsi_globvec->scsi_device_config_mutex ); + + if ( hash_key != NULL ) + iscsi_hashmap_key_destroy( hash_key ); + + if ( scsi_device_config != NULL ) { + switch ( type ) { + case ISCSI_GLOBALS_CONFIG_TYPE_HEADER_DIGEST : { + return scsi_device_config->header_digest; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_DATA_DIGEST : { + return scsi_device_config->data_digest; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_MAX_RECV_DS_LEN : { + return scsi_device_config->max_recv_ds_len; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_MAX_SESSION_CONNS : { + return scsi_device_config->max_session_conns; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_MAX_OUTSTANDING_R2T : { + return scsi_device_config->max_outstanding_r2t; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_DEFAULT_TIME_TO_WAIT : { + return scsi_device_config->default_time_to_wait; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_DEFAULT_TIME_TO_RETAIN : { + return scsi_device_config->default_time_to_retain; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_FIRST_BURST_LEN : { + return scsi_device_config->first_burst_len; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_MAX_BURST_LEN : { + return scsi_device_config->max_burst_len; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_ERR_RECOVERY_LEVEL : { + return scsi_device_config->err_recovery_level; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE : { + return scsi_device_config->scsi_physical_block_size; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_DEVICE_TYPE : { + return scsi_device_config->scsi_device_type; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE_SHIFT : { + return scsi_device_config->scsi_physical_block_size_shift; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE : { + return scsi_device_config->scsi_logical_block_size; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE_SHIFT : { + return scsi_device_config->scsi_logical_block_size_shift; + + 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); + + 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); + + 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_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); + + 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); + + 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_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); + + 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); + + 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; + } + default : { + return -1L; + + break; + } + } + } + } + + switch ( type ) { + case ISCSI_GLOBALS_CONFIG_TYPE_HEADER_DIGEST : { + return iscsi_globvec->header_digest; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_DATA_DIGEST : { + return iscsi_globvec->data_digest; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_MAX_RECV_DS_LEN : { + return iscsi_globvec->max_recv_ds_len; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_MAX_SESSION_CONNS : { + return iscsi_globvec->max_session_conns; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_MAX_OUTSTANDING_R2T : { + return iscsi_globvec->max_outstanding_r2t; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_DEFAULT_TIME_TO_WAIT : { + return iscsi_globvec->default_time_to_wait; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_DEFAULT_TIME_TO_RETAIN : { + return iscsi_globvec->default_time_to_retain; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_FIRST_BURST_LEN : { + return iscsi_globvec->first_burst_len; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_MAX_BURST_LEN : { + return iscsi_globvec->max_burst_len; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_ERR_RECOVERY_LEVEL : { + return iscsi_globvec->err_recovery_level; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_DEVICE_TYPE : { + return iscsi_globvec->scsi_device_type; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE : { + return iscsi_globvec->scsi_physical_block_size; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE_SHIFT : { + return iscsi_globvec->scsi_physical_block_size_shift; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE : { + return iscsi_globvec->scsi_logical_block_size; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE_SHIFT : { + return iscsi_globvec->scsi_logical_block_size_shift; + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_INIT_R2T : { + return (((iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_INIT_R2T) != 0) ? 1L : 0L); + + 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_GLOBALS_CONFIG_TYPE_FLAGS_DATA_SEQ_IN_ORDER : { + return (((iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_DATA_SEQ_IN_ORDER) != 0) ? 1L : 0L); + + break; + } + case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_REMOVABLE : { + return (((iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_SCSI_IO_REMOVABLE) != 0) ? 1L : 0L); + + 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_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_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_PHYSICAL_READ_ONLY : { + return (((iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_SCSI_IO_PHYSICAL_READ_ONLY) != 0) ? 1L : 0L); + + 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); + + 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 -1L; + + break; + } + } + + return -1L; +} + +/** + * @brief Extracts a single text key / value pairs out of an iSCSI packet into a hash map. + * + * Parses and extracts a specific key and value pair out of an iSCSI packet + * data stream amd puts the extracted data into a hash map to be used by + * the iSCSI implementation. + * + * @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. + * @param[in] len Length of the remaining packet data. + * @return Number of bytes used by the extracted 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. + */ +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 ); + + if ( key_end == NULL ) { + logadd( LOG_ERROR, "iscsi_parse_text_key_value_pair: Key / value separator '=' not found" ); + + return -1; + } + + const uint key_len = (uint) (key_end - packet_data); + + 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 -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 -1; + } + + 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 -1; + + hash_key[key_len] = '\0'; + + 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 -1; + } + + 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 -1; + } + + 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 -1; + } + + memcpy( hash_val, (key_end + 1), val_len ); + + const int rc = iscsi_hashmap_put( key_value_pairs, hash_key, hash_key_len, hash_val ); + + if ( rc < 0 ) + return -1; + + return (int) (hash_key_len + val_len); +} + +/** + * @brief Extracts all text key / value pairs out of an iSCSI packet into a hash map. + * + * Parses and extracts all key and value pairs out of iSCSI packet + * data amd puts the extracted data into a hash map to be used by + * the iSCSI implementation. + * + * @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. + * @param[in] len Length of the remaining packet data. + * @param[in] c_bit Non-zero value of C bit was set in previously. + * @param[in] partial_pairs Array of partial pair pointers in + * case C bit was set (multiple iSCSI packets for text data). + * @retval -1 An error occured during parsing key. + * @retval 0 Key and value pair was parsed successfully and was added to + * 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) +{ + 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; + + for (key_val_pair_len = 0; (key_val_pair_len < len) && packet_data[key_val_pair_len] != '\0'; key_val_pair_len++) { + } + + uint8_t *tmp_partial_buf = iscsi_sprintf_alloc( "%s%s", *partial_pairs, (const char *) packet_data ); + + if ( tmp_partial_buf == NULL ) + return -1; + + 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 -1; + + free( *partial_pairs ); + *partial_pairs = NULL; + + packet_data += (key_val_pair_len + 1); + len -= (key_val_pair_len + 1); + } + + if ( c_bit ) { // Strip partial parameters in case C bit was enabled previousley + if ( partial_pairs == NULL ) { + logadd( LOG_ERROR, "iscsi_parse_key_value_pairs: C bit set but missing partial parameter" ); + + return -1; + } + + uint 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 != 0U ) + key_val_pair_len++; // NUL char found, don't copy to target buffer' + + *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 -1; + } + + memcpy( *partial_pairs, &packet_data[key_val_pair_len], (len - key_val_pair_len) ); + + if ( key_val_pair_len != 0U ) + len = (key_val_pair_len - 1U); + else + return 0; + } + + int offset = 0; + + while ( ((uint) offset < len) && (packet_data[offset] != '\0') ) { + const int rc = iscsi_parse_text_key_value_pair( key_value_pairs, (packet_data + offset), (len - offset) ); + + if ( rc < 0 ) + return -1; + + offset += rc; + } + + return 0; +} + +/** + * @brief Extracts a string from a key and value pair. + * + * This function calculates the length of the key + * for the hash map function and returns the value + * as string. + * + * @param[in] key_value_pairs The hash map containing the key and value pairs to be extracted. + * @param[in] key The key to retrieve the string value from. + * @param[out] out_value The string value of the key is stored here. + * @retval -1 An error occured during value retrieval. + * 'out value' is unchanged. + * @retval 0 The value of the key has been successfully + * stored in the 'out_value'. + */ +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 ) + 1U); + + return iscsi_hashmap_get( key_value_pairs, key, key_len, out_value ); +} + +/** + * @brief Allocates and adds a string value to a key / value hash map pair. + * + * This function allocates memory for a string key + * and its string value. + * + * @param[in] key_value_pairs Pointer to the hash map which should + * contain the added string key and value pair. + * NULL is NOT allowed here, so be careful. + * @param[in] key String containing the key name as string. May + * NOT be NULL, so take caution. + * @param[in] value String containing the value to be stored. + * @return 0 on successful operation, or a negative value on + * error (memory exhaustion). + */ +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 ) + 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 -1; + } + + 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 -1; + } + + memcpy( hash_val, value, val_len ); + + return iscsi_hashmap_put( key_value_pairs, hash_key, key_len, hash_val ); +} + +/** + * @brief Allocates and updates a string value of a key / value hash map pair. + * + * This function allocates memory for a string key + * and its string value.\n + * If the key does not exist, it will be added as + * a new one. + * + * @param[in] key_value_pairs Pointer to the hash map which should + * contain the updated string key and value pair. + * NULL is NOT allowed here, so be careful. + * @param[in] key String containing the key name as string. May + * NOT be NULL, so take caution. + * @param[in] value String containing the value to be stored. + * @return 0 on successful operation, or a negative value on + * error (memory exhaustion). + */ +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 ) + 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 -1; + } + + 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 -1; + } + + memcpy( hash_val, value, val_len ); + + return iscsi_hashmap_put_free( key_value_pairs, hash_key, key_len, hash_val, iscsi_hashmap_key_destroy_value_callback, NULL ); +} + +/** + * @brief Extracts an integer value from a key and value pair. + * + * This function converts a string representation of a + * key and value pair to an integer value. + * + * @param[in] key_value_pairs The hash map containing the key and value pairs to be extracted. + * @param[in] key The key to retrieve the integer value from. + * @param[out] out_value The integer value of the key is stored here + * or 0 in case of an error during string to integer conversion. + * @retval -1 An error occured during value retrieval. + * 'out value' is unchanged. + * @retval 0 The value of the key has been successfully + * stored in the 'out_value'. + */ +static int iscsi_get_int_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, int32_t *out_value) +{ + uint8_t *str_val; + int rc = iscsi_get_key_value_pair( key_value_pairs, key, &str_val ); + + if ( rc == 0 ) + *out_value = (int32_t) atol( (char *) str_val ); + + return rc; +} + +/** + * @brief Allocates and adds an integer value to a key / value hash map pair. + * + * This function allocates memory for a string key + * and its integer representation as string value. + * + * @param[in] key_value_pairs Pointer to the hash map which should + * contain the added integer key and value pair. + * NULL is NOT allowed here, so be careful. + * @param[in] key String containing the key name as string. May + * NOT be NULL, so take caution. + * @param[in] value Integer containing the value to be stored. + * @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 int32_t 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 -1; + } + + return iscsi_add_key_value_pair( key_value_pairs, key, hash_val ); +} + +/** + * @brief Allocates and updates an integer value of a key / value hash map pair. + * + * This function allocates memory for a string key + * and its integer representation as string value.\n + * If the key does not exist, it will be added as + * a new one. + * + * @param[in] key_value_pairs Pointer to the hash map which should + * contain the updated integer key and value pair. + * NULL is NOT allowed here, so be careful. + * @param[in] key String containing the key name as string. May + * NOT be NULL, so take caution. + * @param[in] value Integer containing the value to be stored. + * @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 int32_t 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 -1; + } + + const int rc = iscsi_update_key_value_pair( key_value_pairs, key, hash_val ); + + free( hash_val ); + + return rc; +} + +/** + * @brief Extracts a boolean value from a key and value pair. + * + * This function converts a string representation of a + * key and value pair to a boolean value. + * + * @param[in] key_value_pairs The hash map containing the key and value pairs to be extracted. + * @param[in] key The key to retrieve the boolean value from. + * @param[out] out_value The boolean value of the key is stored here. + * 'Yes' represents true and any other string results in false. + * @retval -1 An error occured during value retrieval. + * 'out value' is unchanged. + * @retval 0 The value of the key has been successfully + * stored in the 'out_value'. + */ +static int iscsi_get_bool_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, int32_t *out_value) +{ + uint8_t *value; + int rc = iscsi_get_key_value_pair( key_value_pairs, key, &value ); + + if ( rc == 0 ) + *out_value = (strcasecmp( (char *) value, "Yes" ) == 0); + + return rc; +} + +/** + * @brief Allocates and adds an boolean value to a key / value hash map pair. + * + * This function allocates memory for a string key + * and its integer value.\n + * The string representation for true is: Yes\n + * The string representation for false is: No + * + * @param[in] key_value_pairs Pointer to the hash map which should + * contain the added boolean key and value pair. + * NULL is NOT allowed here, so be careful. + * @param[in] key String containing the key name as string. May + * NOT be NULL, so take caution. + * @param[in] value Boolean containing the value to be stored + * as string. + * @return 0 on successful operation, or a negative value on + * error (memory exhaustion). + */ +static int iscsi_add_bool_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int value) +{ + const uint8_t *hash_val = (uint8_t *) ((value != 0) ? "Yes" : "No"); + + return iscsi_add_key_value_pair( key_value_pairs, key, hash_val ); +} + +/** + * @brief Allocates and updates an boolean value of a key / value hash map pair. + * + * This function allocates memory for a string key + * and its integer value.\n + * The string representation for true is: Yes\n + * The string representation for false is: No\n + * If the key does not exist, it will be added as + * a new one. + * + * @param[in] key_value_pairs Pointer to the hash map which should + * contain the updated boolean key and value pair. + * NULL is NOT allowed here, so be careful. + * @param[in] key String containing the key name as string. May + * NOT be NULL, so take caution. + * @param[in] value Boolean containing the value to be stored + * as string. + * @return 0 on successful operation, or a negative value on + * error (memory exhaustion). + */ +static int iscsi_update_bool_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int value) +{ + const uint8_t *hash_val = (uint8_t *) ((value != 0) ? "Yes" : "No"); + + return iscsi_update_key_value_pair( key_value_pairs, key, hash_val ); +} + +/** + * @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 + * initialization. + * @param[in] tag Tag to associate with the portal group. + * @param[in] flags Flags to set for the portal group. + * @return Pointer to allocated and initialized portal group + * or NULL in case of memory + */ +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) ); + + if ( portal_group == NULL ) { + logadd( LOG_ERROR, "iscsi_portal_group_create: Out of memory allocating iSCSI portal group structure" ); + + return NULL; + } + + 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" ); + + free( portal_group ); + + return NULL; + } + + portal_group->ref_count = 0; + portal_group->tag = tag; + portal_group->flags = flags; + portal_group->chap_group = 0L; + + return portal_group; +} + +/** + * @brief iSCSI portal group destructor callback for hash map. + * + * Callback function for deallocation of an iSCSI + * 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. + * @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_group_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) +{ + iscsi_portal_group_destroy( (iscsi_portal_group *) value ); + + return 0; +} + +/** + * @brief Deallocates resources acquired by iscsi_portal_group_create. + * + * 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. + */ +void iscsi_portal_group_destroy(iscsi_portal_group *portal_group) +{ + if ( portal_group != NULL ) { + if ( portal_group->portals != NULL ) { + iscsi_hashmap_iterate( portal_group->portals, iscsi_portal_destroy_callback, NULL ); + iscsi_hashmap_destroy( portal_group->portals ); + + portal_group->portals = NULL; + } + + free( portal_group ); + } +} + +/** + * @brief Adds an iSCSI portal to the iSCSI portal group hash map. + * + * This function allocates host:port of iSCSI portal for use + * as key and sets the portal group in the portal. + * + * @param[in] portal_group iSCSI portal group to add portal to. May NOT be NULL, + * so take caution. + * @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 + * @retval 0 The portal has been added successfully to the + * portal group. + */ +int iscsi_portal_group_add_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_add_portal: Out of memory allocating temporarily key buffer for iSCSI portal" ); + + return -1; + } + + 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 -1; + } + + int rc = iscsi_hashmap_put( portal_group->portals, key, key_len, (uint8_t *) portal ); + + if ( rc < 0 ) { + logadd( LOG_ERROR, "iscsi_portal_group_add_portal: Adding portal to hash map containing iSCSI portal group failed" ); + + iscsi_hashmap_key_destroy( key ); + + return rc; + } + + portal->group = portal_group; + + 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 ); +} + +/** + * @brief Allocates and initializes an iSCSI portal structure. + * + * This function makes a copy of the passed host / IP address + * and port, but does NOT initialize the socket. + * + * @param[in] host Host / IP address of the portal. + * @param[in] port Port of the portal. + * @return Pointer to iSCSI portal structure or NULL + * in case of an error (memory exhausted). + */ +iscsi_portal *iscsi_portal_create(const uint8_t *host, const uint8_t *port) +{ + iscsi_portal *portal = (iscsi_portal *) malloc( sizeof(struct iscsi_portal) ); + + if ( portal == NULL ) { + logadd( LOG_ERROR, "iscsi_portal_create: Out of memory allocating iSCSI portal structure" ); + + return NULL; + } + + portal->group = NULL; + + const uint host_len = (uint) (strlen( (char *) host ) + 1U); + + portal->host = (uint8_t *) malloc( host_len ); + + if ( portal->host == NULL ) { + logadd( LOG_ERROR, "iscsi_portal_create: Out of memory allocating iSCSI portal host name" ); + + return NULL; + } + + memcpy( portal->host, host, host_len ); + + const uint port_len = (uint) (strlen( (char *) port ) + 1U); + + portal->port = (uint8_t *) malloc( port_len ); + + if ( portal->port == NULL ) { + logadd( LOG_ERROR, "iscsi_portal_create: Out of memory allocating iSCSI portal port" ); + + return NULL; + } + + memcpy( portal->port, port, port_len ); + + 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 + * and port but does NOT remove it from the portal group hash map. + * + * @param[in] portal Pointer to iSCSI portal to be deallocated, + * which may be NULL in which case the function does nothing. + */ +void iscsi_portal_destroy(iscsi_portal *portal) +{ + if ( portal != NULL ) { + if ( portal->port != NULL ) { + free( portal->port ); + + portal->port = NULL; + } + + if ( portal->host != NULL ) { + free( portal->host ); + + portal->host = NULL; + } + + free( 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 ( iscsi_hashmap_contains( pairs, hash_key, hash_key_len ) ) { - logadd( LOG_ERROR, "iscsi_parse_text_key_value_pair: Forbidden duplicate key discovered" ); + if ( rc == ISCSI_SCSI_TASK_RUN_UNKNOWN ) { + rc = iscsi_scsi_emu_primary_process( scsi_task ); - iscsi_hashmap_key_destroy( hash_key ); + 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 -1L; + return ISCSI_SCSI_TASK_RUN_COMPLETE; + } } - 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; + return rc; +} - if ( val_len > max_len ) { - logadd( LOG_ERROR, "iscsi_parse_text_key_value_pair: Value length larger than iSCSI specs allow" ); +/** + * @brief Allocates and initializes an iSCSI port. + * + * THis function marks the port in use, but does + * NOT set a transport ID. Everything else is + * initialized, however. + * + * @param[in] name Pointer to port name. This + * may NOT be NULL, so be careful. + * @param[in] id Identifier for this port. + * @param[in] index Index number for this port. + * @return Pointer to initialized iSCSI port or NULL + * in case of memory exhaustion. + */ +iscsi_port *iscsi_port_create(const uint8_t *name, const uint64_t id, const uint16_t index) +{ + iscsi_port *port = (iscsi_port *) malloc( sizeof(struct iscsi_port) ); - iscsi_hashmap_key_destroy( hash_key ); + if ( port == NULL ) { + logadd( LOG_ERROR, "iscsi_port_create: Out of memory allocating iSCSI port" ); - return -1L; + return NULL; } - uint8_t *hash_val = (uint8_t *) malloc( iscsi_align(val_len + 1UL, ISCSI_HASHMAP_VALUE_ALIGN) ); + const uint name_len = (uint) (strlen( (char *) name ) + 1UL); - if ( hash_val == NULL ) { - logadd( LOG_ERROR, "iscsi_parse_text_key_value_pair: Out of memory allocating memory for value string" ); + port->name = (uint8_t *) malloc( name_len ); - iscsi_hashmap_key_destroy( hash_key ); + if ( port->name == NULL ) { + logadd( LOG_ERROR, "iscsi_port_create: Out of memory allocating iSCSI port name" ); - return -1L; - } + free( port ); - memcpy( hash_val, key_end + 1, val_len ); + return NULL; + } - const int rc = iscsi_hashmap_put( pairs, hash_key, hash_key_len, hash_val ); + memcpy( port->name, name, name_len ); - if ( rc < 0 ) - return -1L; + port->transport_id = NULL; + port->id = id; + port->index = index; + port->flags = ISCSI_PORT_FLAGS_IN_USE; + port->transport_id_len = 0U; - return (int) (hash_key_len + val_len + 1UL); // Number of bytes for processed key / value pair (+1 for '=' and NUL terminator) + return port; } /** - * @brief Extracts all text key / value pairs out of an iSCSI packet into a hash map. + * @brief iSCSI port destructor callback for hash map. * - * Parses and extracts all key and value pairs out of iSCSI packet - * data amd puts the extracted data into a hash map to be used by - * the iSCSI implementation. + * Callback function for deallocation of an iSCSI + * port stored in the iSCSI device hash map. * - * @param[in] 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. - * @param[in] len Length of the remaining packet data. - * @param[in] c_bit Non-zero value of C bit was set in previously. - * @param[in] partial_pairs Array of partial pair pointers in - * case C bit was set (multiple iSCSI packets for text data). - * @retval -1 An error occured during parsing key. - * @retval 0 Key and value pair was parsed successfully and was added to - * 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_parse_key_value_pairs(iscsi_hashmap *pairs, const uint8_t *packet_data, uint len, int c_bit, uint8_t **partial_pairs) +int iscsi_port_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) { - if ( len == 0 ) - return 0L; // iSCSI specs don't allow zero length + iscsi_port_destroy( (iscsi_port *) value ); - if ( (partial_pairs != NULL) && (*partial_pairs != NULL) ) { // Strip partial text parameters in case C bit was enabled previously - uint key_val_pair_len; + return 0; +} - for (key_val_pair_len = 0; (key_val_pair_len < len) && packet_data[key_val_pair_len] != '\0'; key_val_pair_len++) { - } +/** + * @brief Deallocates all resources acquired iscsi_port_create. + * + * This function also frees the port name and transport ID, + * if they exist. + * + * @param[in] port iSCSI port to deallocate. This may + * be NULL in which case nothing happens. + */ +void iscsi_port_destroy(iscsi_port *port) +{ + if ( port != NULL ) { + if ( port->name != NULL ) { + free( port->name ); - uint8_t *tmp_partial_buf = iscsi_sprintf_alloc( "%s%s", *partial_pairs, (const char *) packet_data ); + port->name = NULL; + } - if ( tmp_partial_buf == NULL ) - return -1L; + if ( port->transport_id != NULL ) { + free( port->transport_id ); - const int rc = iscsi_parse_text_key_value_pair( pairs, tmp_partial_buf, (uint32_t) (key_val_pair_len + strlen( (char *) *partial_pairs )) ); - free( tmp_partial_buf ); + port->transport_id = NULL; + } - if ( rc < 0 ) - return -1L; + free( port ); + } +} - free( *partial_pairs ); - *partial_pairs = NULL; +/** + * @brief Retrieves the name of an iSCSI port. + * + * This function is just a getter. + * + * @param[in] port Pointer to iSCSI port to retrieve + * the name from and may NOT be NULL, so be + * careful. + * @return Pointer to string containing the name + * of the iSCSI port. + */ +uint8_t *iscsi_port_get_name(const iscsi_port *port) +{ + return port->name; +} - packet_data += (key_val_pair_len + 1); - len -= (key_val_pair_len + 1); - } +/** + * @brief Sets the SCSI transport ID of the iSCSI port. + * + * This function constructs the SCSI packet data + * for the SCSI transport id by assigning a name + * and the Initiator Session ID (ISID).\n + * Currently, always transport ID format 0x1 will + * be created. + * + * @param[in] Pointer to iSCSI port to assign the + * SCSI transport ID to. May NOT be NULL, so be + * careful. + * @param[in] Pointer to iSCSI name to assign + * along with the ISID as name. + * @param[in] Initiator Session ID (ISID). + * @return 0 if transport ID could be created + * successfully, a negative error code + * otherwise. + */ +int iscsi_port_transport_id_set(iscsi_port *port, const uint8_t *name, const uint64_t isid) +{ + uint8_t *tmp_buf = iscsi_sprintf_alloc( "%s,i,0x%12.12" PRIx64, name, isid ); - if ( c_bit ) { // Strip partial parameters in case C bit was enabled previousley - if ( partial_pairs == NULL ) { - logadd( LOG_ERROR, "iscsi_parse_key_value_pairs: C bit set but missing partial parameter" ); + if ( tmp_buf == NULL ) { + logadd( LOG_ERROR, "iscsi_port_transport_id_set: Out of memory allocating SCSI transport ID name for iSCSI port" ); - return -1L; - } + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + } - uint key_val_pair_len; + const uint name_len = (uint) (strlen( (char *) tmp_buf ) + 1U); + const uint len = ISCSI_ALIGN(name_len, ISCSI_ALIGN_SIZE); - for (key_val_pair_len = len - 1; (packet_data[key_val_pair_len] != '\0') && (key_val_pair_len > 0); key_val_pair_len--) { - } + 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" ); - if ( key_val_pair_len != 0 ) - key_val_pair_len++; // NUL char found, don't copy to target buffer' + free( tmp_buf ); - *partial_pairs = (uint8_t *) malloc ( (len - key_val_pair_len) + 1 ); + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER; + } - if ( *partial_pairs == NULL ) { - logadd( LOG_ERROR, "iscsi_parse_key_value_pairs: Out of memory allocating partial parameter" ); + port->transport_id = (iscsi_transport_id *) malloc( sizeof(struct iscsi_transport_id) + len ); - return -1L; - } + if ( port->transport_id == NULL ) { + logadd( LOG_ERROR, "iscsi_port_transport_id_set: Out of memory allocating SCSI transport ID for iSCSI port" ); - memcpy( *partial_pairs, &packet_data[key_val_pair_len], (len - key_val_pair_len) ); + free( tmp_buf ); - if ( key_val_pair_len != 0 ) - len = key_val_pair_len - 1; - else - return 0L; + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } - int offset = 0L; + 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 ); - while ( ((uint) offset < len) && (packet_data[offset] != '\0') ) { - const int rc = iscsi_parse_text_key_value_pair( pairs, (packet_data + offset), (len - offset) ); + 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, (len - name_len) ); - if ( rc < 0 ) - return -1L; + port->transport_id_len = (uint16_t) (offsetof(struct iscsi_transport_id, name) + len); - offset += rc; - } + free( tmp_buf ); - return 0L; + return ISCSI_CONNECT_PDU_READ_OK; } /** - * @brief Extracts a string from a key and value pair. + * @brief Creates and initializes an iSCSI device with a maximum number of LUNs. * - * This function calculates the length of the key - * for the hash map function and returns the value - * as string. + * This function creates a virtual SCSI device + * which links the DNBD3 images to their LUNs. * - * @param[in] key_value_pairs The hash map containing the key and value pairs to be extracted. - * @param[in] key The key to retrieve the string value from. - * @param[out] out_value The string value of the key is stored here. - * @retval -1 An error occured during value retrieval. - * 'out value' is unchanged. - * @retval 0 The value of the key has been successfully - * stored in the 'out_value'. + * @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. */ -static int iscsi_get_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, uint8_t **out_value) +iscsi_device *iscsi_device_create(const uint8_t *name, const int lun_id, const uint8_t protocol_id) { - const uint key_len = (uint) strlen( (char *) key ) + 1; + iscsi_device *device = (iscsi_device *) malloc( sizeof(struct iscsi_device) ); - return iscsi_hashmap_get( key_value_pairs, key, key_len, out_value ); -} + if ( device == NULL ) { + logadd( LOG_ERROR, "iscsi_device_create: Out of memory allocating iSCSI device" ); -/** - * @brief Allocates and adds a string value to a key / value hash map pair. - * - * This function allocates memory for a string key - * and its string value. - * - * @param[in] key_value_pairs Pointer to the hash map which should - * contain the added string key and value pair. - * NULL is NOT allowed here, so be careful. - * @param[in] key String containing the key name as string. May - * NOT be NULL, so take caution. - * @param[in] value String containing the value to be stored. - * @return 0 on successful operation, or a negative value on - * error (memory exhaustion). - */ -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; - uint8_t *hash_key = iscsi_hashmap_key_create( key, key_len ); + return NULL; + } - if ( hash_key == NULL ) { - logadd( LOG_ERROR, "iscsi_add_key_value_pair: Out of memory allocating key" ); + const uint len = (uint) (strlen( (char *) name ) + 1U); + + device->name = malloc( len ); - return -1L; + if ( device->name == NULL ) { + logadd( LOG_ERROR, "iscsi_device_create: Out of memory allocating iSCSI device name" ); + + free( device ); + + return NULL; } - const uint val_len = (uint) strlen( (char *) value ) + 1; - uint8_t *hash_val = (uint8_t *) malloc( iscsi_align(val_len, ISCSI_HASHMAP_VALUE_ALIGN) ); + memcpy( device->name, name, len ); - if ( hash_val == NULL ) { - logadd( LOG_ERROR, "iscsi_add_key_value_pair: Out of memory allocating string value" ); + device->luns = iscsi_hashmap_create( 8U ); - iscsi_hashmap_key_destroy( hash_key ); + if ( device->luns == NULL ) { + logadd( LOG_ERROR, "iscsi_device_create: Out of memory allocating iSCSI device LUN hash map" ); - return -1L; - } + free( device->name ); + free( device ); - memcpy( hash_val, value, val_len ); + return NULL; + } - return iscsi_hashmap_put( key_value_pairs, hash_key, key_len, hash_val ); -} + iscsi_scsi_lun *lun = iscsi_scsi_lun_create( lun_id ); -/** - * @brief Allocates and updates a string value of a key / value hash map pair. - * - * This function allocates memory for a string key - * and its string value.\n - * If the key does not exist, it will be added as - * a new one. - * - * @param[in] key_value_pairs Pointer to the hash map which should - * contain the updated string key and value pair. - * NULL is NOT allowed here, so be careful. - * @param[in] key String containing the key name as string. May - * NOT be NULL, so take caution. - * @param[in] value String containing the value to be stored. - * @return 0 on successful operation, or a negative value on - * error (memory exhaustion). - */ -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; - uint8_t *hash_key = iscsi_hashmap_key_create( key, key_len ); + 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_update_key_value_pair: Out of memory allocating key" ); + logadd( LOG_ERROR, "iscsi_device_create: Out of memory allocating iSCSI device LUN hash map" ); - return -1L; + pthread_rwlock_destroy( &device->luns_rwlock ); + iscsi_scsi_lun_destroy( lun ); + iscsi_hashmap_destroy( device->luns ); + free( device->name ); + free( device ); + + return NULL; } - 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 int rc = iscsi_hashmap_put( device->luns, hash_key, sizeof(lun_hash), (uint8_t *) lun ); - if ( hash_val == NULL ) { - logadd( LOG_ERROR, "iscsi_update_key_value_pair: Out of memory allocating string value" ); + 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 -1L; + return NULL; } - memcpy( hash_val, value, val_len ); + device->id = 0; + device->flags = 0; + device->active_conns = 0UL; + device->protocol_id = protocol_id; - return iscsi_hashmap_put_free( key_value_pairs, hash_key, key_len, hash_val, iscsi_hashmap_key_destroy_value_callback, NULL ); + return device; } /** - * @brief Extracts an integer value from a key and value pair. + * @brief iSCSI device destructor callback for hash map. * - * This function converts a string representation of a - * key and value pair to an integer value. + * Callback function for deallocation of an iSCSI + * device stored in the hash map managing all iSCSI + * devices. * - * @param[in] key_value_pairs The hash map containing the key and value pairs to be extracted. - * @param[in] key The key to retrieve the integer value from. - * @param[out] out_value The integer value of the key is stored here - * or 0 in case of an error during string to integer conversion. - * @retval -1 An error occured during value retrieval. - * 'out value' is unchanged. - * @retval 0 The value of the key has been successfully - * stored in the 'out_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. */ -static int iscsi_get_int_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, int32_t *out_value) +int iscsi_device_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) { - uint8_t *str_val; - int rc = iscsi_get_key_value_pair( key_value_pairs, key, &str_val ); - - if ( rc == 0 ) - *out_value = (int32_t) atol( (char *) str_val ); + iscsi_device_destroy( (iscsi_device *) value ); + iscsi_hashmap_key_destroy( key ); - return rc; + return 0; } /** - * @brief Allocates and adds an integer value to a key / value hash map pair. + * @brief Deallocates all resources acquired by iscsi_device_create. * - * This function allocates memory for a string key - * and its integer representation as string value. + * This function also frees the associated + * iSCSI ports, LUNs and the device name. * - * @param[in] key_value_pairs Pointer to the hash map which should - * contain the added integer key and value pair. - * NULL is NOT allowed here, so be careful. - * @param[in] key String containing the key name as string. May - * NOT be NULL, so take caution. - * @param[in] value Integer containing the value to be stored. - * @return 0 on successful operation, or a negative value on - * error (memory exhaustion). + * @param[in] device Pointer to iSCSI device to be freed. May + * be NULL in which case this function does + * nothing at all. */ -static int iscsi_add_int_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int value) +void iscsi_device_destroy(iscsi_device *device) { - const uint8_t *hash_val = iscsi_sprintf_alloc( "%d", value ); + if ( device != NULL ) { + if ( device->ports != NULL ) { + iscsi_hashmap_iterate( device->ports, iscsi_port_destroy_callback, NULL ); + iscsi_hashmap_destroy( device->ports ); - if ( hash_val == NULL ) { - logadd( LOG_ERROR, "iscsi_add_int_key_value_pair: Out of memory allocating integer value." ); + device->ports = NULL; + } - return -1L; - } + pthread_rwlock_destroy( &device->luns_rwlock ); - return iscsi_add_key_value_pair( key_value_pairs, key, hash_val ); + 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 Allocates and updates an integer value of a key / value hash map pair. + * @brief Gets an iSCSI device being in use by portal group identifier. * - * This function allocates memory for a string key - * and its integer representation as string value.\n - * If the key does not exist, it will be added as - * a new one. + * This function uses the unique portal group + * identifier in order to get the port. * - * @param[in] key_value_pairs Pointer to the hash map which should - * contain the updated integer key and value pair. - * NULL is NOT allowed here, so be careful. - * @param[in] key String containing the key name as string. May + * @param[in] device Pointer to iSCSI device to be searched. May * NOT be NULL, so take caution. - * @param[in] value Integer containing the value to be stored. - * @return 0 on successful operation, or a negative value on - * error (memory exhaustion). + * @param[in] id Portal group ID to be searched for. + * @return Pointer to iSCSI port belonging to the iSCSI + * portal group ID or NULL if either the portal + * group ID does not exist or the port is NOT in use. */ -static int iscsi_update_int_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int value) +iscsi_port *iscsi_device_find_port_by_portal_group_tag(const iscsi_device *device, const uint64_t id) { - const uint8_t *hash_val = iscsi_sprintf_alloc( "%d", value ); + iscsi_port *port; - if ( hash_val == NULL ) { - logadd( LOG_ERROR, "iscsi_update_int_key_value_pair: Out of memory allocating integer value." ); + if ( iscsi_hashmap_get( device->ports, (uint8_t *) &id, sizeof(id), (uint8_t **) &port ) < 0 ) + return NULL; - return -1L; - } + if ( (port == NULL) || ((port->flags & ISCSI_PORT_FLAGS_IN_USE) == 0) ) + return NULL; - return iscsi_update_key_value_pair( key_value_pairs, key, hash_val ); + return port; } /** - * @brief Extracts a boolean value from a key and value pair. + * @brief Searches an iSCSI LUN by LUN identifier. * - * This function converts a string representation of a - * key and value pair to a boolean value. + * This function searches for an iSCSI LUN by + * iterating through the iSCSI device LUN + * hash map. * - * @param[in] key_value_pairs The hash map containing the key and value pairs to be extracted. - * @param[in] key The key to retrieve the boolean value from. - * @param[out] out_value The boolean value of the key is stored here. - * 'Yes' represents true and any other string results in false. - * @retval -1 An error occured during value retrieval. - * 'out value' is unchanged. - * @retval 0 The value of the key has been successfully - * stored in the 'out_value'. + * @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. */ -static int iscsi_get_bool_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, int32_t *out_value) +iscsi_scsi_lun *iscsi_device_find_lun(iscsi_device *device, const int lun_id) { - uint8_t *value; - int rc = iscsi_get_key_value_pair( key_value_pairs, key, &value ); + const uint64_t hash_key = (uint64_t) lun_id; + iscsi_scsi_lun *lun; - if ( rc == 0 ) - *out_value = (strcasecmp( (char *) value, "Yes" ) == 0) ? true : false; + const int rc = iscsi_hashmap_get( device->luns, (uint8_t *) &hash_key, sizeof(hash_key), (uint8_t **) &lun ); - return rc; + if ( (rc < 0) || ((lun->flags & ISCSI_SCSI_LUN_FLAGS_REMOVED) != 0) ) + return NULL; + + return lun; } /** - * @brief Allocates and adds an boolean value to a key / value hash map pair. + * @brief Creates, initializes and adds an iSCSI target port to an iSCSI device. * - * This function allocates memory for a string key - * and its integer value.\n - * The string representation for true is: Yes\n - * The string representation for false is: No + * This function checks whether the iSCSI + * target port already exists for the + * device. * - * @param[in] key_value_pairs Pointer to the hash map which should - * contain the added boolean key and value pair. - * NULL is NOT allowed here, so be careful. - * @param[in] key String containing the key name as string. May - * NOT be NULL, so take caution. - * @param[in] value Boolean containing the value to be stored - * as string. - * @return 0 on successful operation, or a negative value on - * error (memory exhaustion). + * @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. */ -static int iscsi_add_bool_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int value) +int iscsi_device_port_add(iscsi_device *device, const uint8_t *name, const uint64_t id) { - const uint8_t *hash_val = (uint8_t *) ((value != 0) ? "Yes" : "No"); + if ( iscsi_hashmap_contains( device->ports, (uint8_t *) &id, sizeof(id) ) ) + return 1; - return iscsi_add_key_value_pair( key_value_pairs, key, hash_val ); + 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 Allocates and updates an boolean value of a key / value hash map pair. + * @brief Enqueues an iSCSI SCSI task to the first LUN of an iSCSI device. * - * This function allocates memory for a string key - * and its integer value.\n - * The string representation for true is: Yes\n - * The string representation for false is: No\n - * If the key does not exist, it will be added as - * a new one. + * This function adds an iSCSI SCSI task + * with an unique task identifier to the + * first LUN of an iSCSI device. * - * @param[in] key_value_pairs Pointer to the hash map which should - * contain the updated boolean key and value pair. - * NULL is NOT allowed here, so be careful. - * @param[in] key String containing the key name as string. May - * NOT be NULL, so take caution. - * @param[in] value Boolean containing the value to be stored - * as string. - * @return 0 on successful operation, or a negative value on - * error (memory exhaustion). + * @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. */ -static int iscsi_update_bool_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int value) +void iscsi_device_scsi_task_queue(iscsi_device *device, iscsi_scsi_task *scsi_task) { - const uint8_t *hash_val = (uint8_t *) ((value != 0) ? "Yes" : "No"); - - return iscsi_update_key_value_pair( key_value_pairs, key, hash_val ); + iscsi_scsi_lun_task_exec( scsi_task->lun, scsi_task ); } /** - * @brief Creates and initializes an iSCSI portal group. + * @brief Checks if an iSCSI target node NAA or EUI hex identifier is valid. * - * Specified tag and flags are used for portal group - * initialization. - * @param[in] tag Tag to associate with the portal group. - * @param[in] flags Flags to set for the portal group. - * @return Pointer to allocated and initialized portal group - * or NULL in case of memory + * 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. */ -iscsi_portal_group *iscsi_portal_group_create(const int tag, const int flags) +static bool iscsi_target_node_check_hex(const uint8_t *name, const size_t pos, const size_t len) { - iscsi_portal_group *portal_group = (iscsi_portal_group *) malloc( sizeof(struct iscsi_portal_group) ); + for ( size_t i = pos; i < len; i++ ) { + const uint8_t c = name[i]; - if ( portal_group == NULL ) { - logadd( LOG_ERROR, "iscsi_portal_group_create: Out of memory allocating iSCSI portal group structure" ); - - return NULL; + if ( (c < '0') || ((c > '9') && (c < 'A')) || ((c > 'F') && (c < 'a')) || (c > 'f') ) + return false; } - portal_group->portals = iscsi_hashmap_create( 0UL ); + return true; +} - if ( portal_group->portals == NULL ) { - logadd( LOG_ERROR, "iscsi_portal_group_create: Out of memory allocating iSCSI portal hash map" ); +/** + * @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; - free( portal_group ); + const size_t len = strlen( (char *) name ); - return NULL; + 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; + } } - portal_group->ref_count = 0L; - portal_group->tag = tag; - portal_group->flags = flags; - portal_group->chap_group = 0L; + 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 portal_group; + return 0; } /** - * @brief iSCSI portal destructor callback for hash map. + * @brief Checks if the iSCSI target node flags are valid. * - * Callback function for deallocation of an iSCSI - * portal stored in the iSCSI portal group hash map. + * This function checks if the set flags + * are contradicting themselves or are + * okay. * - * @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] 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. */ -int iscsi_portal_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) +static int iscsi_target_node_check_flags(const int flags, const int32_t chap_group) { - iscsi_portal_destroy( (iscsi_portal *) value ); - iscsi_hashmap_key_destroy( key ); + 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 0L; + return -1; } /** - * @brief Deallocates resources acquired by iscsi_portal_group_create. + * @brief Creates, initializes and adds a portal group to an iSCSI target node. * - * 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. + * 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. */ -void iscsi_portal_group_destroy(iscsi_portal_group *portal_group) +int iscsi_target_node_create_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) { - if ( portal_group != NULL ) { - if ( portal_group->portals != NULL ) { - iscsi_hashmap_iterate( portal_group->portals, iscsi_portal_destroy_callback, NULL ); - iscsi_hashmap_destroy( portal_group->portals ); + 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 ); - portal_group->portals = NULL; - } + if ( port_name == NULL ) + return -1; + + const int rc = iscsi_device_port_add( target->device, port_name, (uint64_t) portal_group->tag ); - free( portal_group ); - } + free( port_name ); + + return rc; } /** - * @brief Adds an iSCSI portal to the iSCSI portal group hash map. + * @brief Creates and initializes an iSCSI target node. * - * This function allocates host:port of iSCSI portal for use - * as key and sets the portal group in the portal. + * This function also allocates the underlying SCSI + * device and always initializes the first LUN. * - * @param[in] 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 - * allowed here, so be careful. - * @retval -1 An error occured during adding the portal, - * usually caused by memory exhaustion - * @retval 0 The portal has been added successfully to the - * portal group. - */ -int iscsi_portal_group_add_portal(iscsi_portal_group *portal_group, iscsi_portal *portal) -{ - uint8_t *tmp_buf = iscsi_sprintf_alloc( "%s:%s", portal->host, portal->port ); + * @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; - if ( tmp_buf == NULL ) - return -1L; + iscsi_target_node *target = (iscsi_target_node *) malloc( sizeof(struct iscsi_target_node) ); - const uint key_len = (uint) strlen( (char *) tmp_buf ) + 1; - uint8_t *key = iscsi_hashmap_key_create( tmp_buf, key_len ); + if ( target == NULL ) { + logadd( LOG_ERROR, "iscsi_target_node_create: Out of memory allocating iSCSI target node" ); - free( tmp_buf ); + return NULL; + } - if ( key == NULL ) { - logadd( LOG_ERROR, "iscsi_portal_group_add_portal: Out of memory allocating key for iSCSI portal" ); + 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 -1L; + return NULL; } - int rc = iscsi_hashmap_put( portal_group->portals, key, key_len, (uint8_t *) portal ); + memcpy( target->name, name, name_len ); - if ( rc < 0 ) { - logadd( LOG_ERROR, "iscsi_portal_group_add_portal: Adding portal to hash map containing iSCSI portal group failed" ); + if ( alias != NULL ) { + const uint alias_len = (uint) (strlen( (char *) alias ) + 1U); - iscsi_hashmap_key_destroy( key ); + target->alias = malloc( alias_len ); - return rc; - } + if ( target->alias == NULL ) { + logadd( LOG_ERROR, "iscsi_target_node_create: Out of memory allocating iSCSI target node alias" ); - portal->group = portal_group; + free( target->name ); + free( target ); - return 0L; -} + return NULL; + } -/** - * @brief Allocates and initializes an iSCSI portal structure. - * - * This function makes a copy of the passed host / IP address - * and port, but does NOT initialize the socket. - * - * @param[in] host Host / IP address of the portal. - * @param[in] port Port of the portal. - * @return Pointer to iSCSI portal structure or NULL - * in case of an error (memory exhausted). - */ -iscsi_portal *iscsi_portal_create(const uint8_t *host, const uint8_t *port) -{ - iscsi_portal *portal = (iscsi_portal *) malloc( sizeof(struct iscsi_portal) ); + memcpy( target->alias, alias, alias_len ); + } else { + target->alias = NULL; + } - if ( portal == NULL ) { - logadd( LOG_ERROR, "iscsi_portal_create: Out of memory allocating iSCSI portal structure" ); + 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; } - portal->group = 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; - const uint host_len = (uint) strlen( (char *) host ) + 1; + pthread_rwlock_wrlock( &iscsi_globvec->devices_rwlock ); + int rc = iscsi_hashmap_get( iscsi_globvec->devices, hash_key, key_len, (uint8_t **) &device ); - portal->host = (uint8_t *) malloc( host_len ); + if ( device != NULL ) { + pthread_rwlock_wrlock( &device->luns_rwlock ); - if ( portal->host == NULL ) { - logadd( LOG_ERROR, "iscsi_portal_create: Out of memory allocating iSCSI portal host name" ); + iscsi_scsi_lun *lun = iscsi_device_find_lun( device, lun_id ); - return NULL; - } + if ( lun == NULL ) { + lun = iscsi_scsi_lun_create( lun_id ); - memcpy( portal->host, host, host_len ); + if ( lun == NULL ) { + logadd( LOG_ERROR, "iscsi_target_node_create: Out of memory allocating iSCSI device LUN hash map" ); - const uint port_len = (uint) strlen( (char *) port ) + 1; + pthread_rwlock_unlock( &device->luns_rwlock ); + pthread_rwlock_unlock( &iscsi_globvec->devices_rwlock ); + iscsi_hashmap_key_destroy( hash_key ); - portal->port = (uint8_t *) malloc( port_len ); + if ( target->alias != NULL ) + free( target->alias ); - if ( portal->port == NULL ) { - logadd( LOG_ERROR, "iscsi_portal_create: Out of memory allocating iSCSI portal port" ); + free( target->name ); + free( target ); - return NULL; - } + return NULL; + } - memcpy( portal->port, port, port_len ); + const uint64_t lun_hash = lun_id; + uint8_t *lun_hash_key = iscsi_hashmap_key_create( (uint8_t *) &lun_hash, sizeof(lun_hash) ); - portal->sock = -1L; + if ( lun_hash_key == NULL ) { + logadd( LOG_ERROR, "iscsi_target_node_create: Out of memory allocating iSCSI device LUN hash map" ); - return portal; -} + pthread_rwlock_unlock( &device->luns_rwlock ); + pthread_rwlock_unlock( &iscsi_globvec->devices_rwlock ); + iscsi_scsi_lun_destroy( lun ); + iscsi_hashmap_key_destroy( hash_key ); -/** - * @brief Deallocates all resources acquired by iscsi_portal_create. - * - * This function frees the memory acquired for host / IP address - * and port but does NOT remove it from the portal group hash map. - * - * @param[in] portal Pointer to iSCSI portal to be deallocated, - * which may be NULL in which case the function does nothing. - */ -void iscsi_portal_destroy(iscsi_portal *portal) -{ - if ( portal != NULL ) { - if ( portal->port != NULL ) { - free( portal->port ); + if ( target->alias != NULL ) + free( target->alias ); - portal->port = NULL; + 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; + } } - if ( portal->host != NULL ) { - free( portal->host ); + pthread_rwlock_unlock( &device->luns_rwlock ); + iscsi_hashmap_key_destroy( hash_key ); - portal->host = NULL; + 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; } - free( portal ); - } -} + rc = iscsi_hashmap_put( iscsi_globvec->devices, hash_key, key_len, (uint8_t *) device ); -/** - * @brief Allocates and initializes an iSCSI port. - * - * THis function marks the port in use, but does - * NOT set a transport ID. Everything else is - * initialized, however. - * - * @param[in] name Pointer to port name. This - * may NOT be NULL, so be careful. - * @param[in] id Identifier for this port. - * @param[in] index Index number for this port. - * @return Pointer to initialized iSCSI port or NULL - * in case of memory exhaustion. - */ -iscsi_port *iscsi_port_create(const uint8_t *name, const uint64_t id, const uint16_t index) -{ - iscsi_port *port = (iscsi_port *) malloc( sizeof(struct iscsi_port) ); + if ( rc < 0 ) { + pthread_rwlock_unlock( &iscsi_globvec->devices_rwlock ); + iscsi_device_destroy( device ); + iscsi_hashmap_key_destroy( hash_key ); - if ( port == NULL ) { - logadd( LOG_ERROR, "iscsi_port_create: Out of memory allocating iSCSI port" ); + if ( target->alias != NULL ) + free( target->alias ); - return NULL; + free( target->name ); + free( target ); + + return NULL; + } } - const uint name_len = (uint) strlen( (char *) name ) + 1; + device->active_conns++; - port->name = (uint8_t *) malloc( name_len ); + pthread_rwlock_unlock( &iscsi_globvec->devices_rwlock ); - if ( port->name == NULL ) { - logadd( LOG_ERROR, "iscsi_port_create: Out of memory allocating iSCSI port name" ); + target->device = device; - free( port ); + 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; } - memcpy( port->name, name, name_len ); + 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; - port->transport_id = NULL; - port->id = id; - port->index = index; - port->flags = ISCSI_PORT_FLAGS_IN_USE; - port->transport_id_len = 0U; + return target; +} - return port; +/** + * @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 iscsi_port_create. + * @brief Deallocates all resources acquired by iscsi_target_node_create. * - * This function also frees the port name and transport ID, - * if they exist. + * This function also frees the IQN name, + * IQN alias and the associated SCSI device. * - * @param[in] port iSCSI port to deallocate. This may - * be NULL in which case nothing happens. + * @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_port_destroy(iscsi_port *port) +void iscsi_target_node_destroy(iscsi_target_node *target) { - if ( port != NULL ) { - if ( port->name != NULL ) { - free( port->name ); + if ( target != NULL ) { + if ( target->alias != NULL ) { + free( target->alias ); - port->name = NULL; + target->alias = NULL; } - if ( port->transport_id != NULL ) { - free( port->transport_id ); + if ( target->name != NULL ) { + free( target->name ); - port->transport_id = NULL; + target->name = NULL; } - free( port ); + free( target ); } } /** - * @brief Retrieves the name of an iSCSI port. + * @brief Sends a buffer from a source iSCSI IQN to target iSCSI IQNs. * - * This function is just a getter. + * 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] port Pointer to iSCSI port to retrieve - * the name from and may NOT be NULL, so be - * careful. - * @return Pointer to string containing the name - * of the iSCSI port. + * @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. */ -uint8_t *iscsi_port_get_name(const iscsi_port *port) +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) { - return port->name; + // TODO: Implement function. + + return -1; } /** - * @brief Sets the SCSI transport ID of the iSCSI port. - * - * This function constructs the SCSI packet data - * for the SCSI transport id by assigning a name - * and the Initiator Session ID (ISID).\n - * Currently, always transport ID format 0x1 will - * be created. + * @brief Calculates the WWN using 64-bit IEEE Extended NAA for a name. * - * @param[in] Pointer to iSCSI port to assign the - * SCSI transport ID to. May NOT be NULL, so be - * careful. - * @param[in] Pointer to iSCSI name to assign - * along with the ISID as name. - * @param[in] Initiator Session ID (ISID). - * @return 0 if transport ID could be created - * successfully, a negative error code - * otherwise. + * @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. */ -int iscsi_port_transport_id_set(iscsi_port *port, const uint8_t *name, const uint64_t isid) +uint64_t iscsi_target_node_wwn_get(const uint8_t *name) { - uint8_t *tmp_buf = iscsi_sprintf_alloc( "%s,i,0x%12.12" PRIx64, name, isid ); + uint64_t value = 0ULL; + int i = 0; - if ( tmp_buf == NULL ) { - logadd( LOG_ERROR, "iscsi_port_transport_id_set: Out of memory allocating SCSI transport ID name for iSCSI port" ); - - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + while ( name[i] != '\0' ) { + value = (value * 131ULL) + name[i++]; } - const uint name_len = (uint) strlen( (char *) tmp_buf ) + 1; - const uint len = iscsi_align(name_len, ISCSI_ALIGN_SIZE); + const uint64_t id_a = ((value & 0xFFF000000ULL) << 24ULL); - if ( (len < 20UL) || ((len + offsetof(struct iscsi_transport_id, name)) >= 65536UL) ) { - logadd( LOG_ERROR, "iscsi_port_transport_id_set: Out of memory allocating SCSI transport ID for iSCSI port" ); + return ((value & 0xFFFFFFULL) | 0x2000000347000000ULL | id_a); +} - free( tmp_buf ); +/** + * @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, ':' ); - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER; + while ( tmp != NULL ) { + tmp++; + + if ( image_rev != NULL ) + image_name = image_rev; + + image_rev = tmp; + tmp = (uint8_t *) strchr( (char *) tmp, ':' ); } - port->transport_id = (iscsi_transport_id *) malloc( sizeof(struct iscsi_transport_id) + len ); + if ( image_rev == NULL ) + image_rev = image_name; - if ( port->transport_id == NULL ) { - logadd( LOG_ERROR, "iscsi_port_transport_id_set: Out of memory allocating SCSI transport ID for iSCSI port" ); + const uint len = (uint) (image_rev - image_name); - free( tmp_buf ); + if ( len > 0U ) { + tmp = (uint8_t *) malloc( len ); - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + 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; } - port->transport_id->id = (ISCSI_TRANSPORT_ID_FORMAT << 6U) | ISCSI_TRANSPORT_ID_PROTOCOL_ID_ISCSI; - port->transport_id->reserved = 0U; - iscsi_put_be16( (uint8_t *) &port->transport_id->add_len, (uint16_t) len ); + const uint16_t rev = (uint16_t) ((len > 0U) ? atoi( (char *) image_rev ) : 0); + dnbd3_image_t *image = image_getOrLoad( (char *) image_name, rev ); - 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)) ); + if ( image == NULL ) { + image = image_getOrLoad( (char *) tmp, rev ); - port->transport_id_len = (uint16_t) (offsetof(struct iscsi_transport_id, name) + len); + 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 ); - return ISCSI_CONNECT_PDU_READ_OK; -} + 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); -/** - * @brief Gets an iSCSI device being in use by portal group identifier. - * - * This function uses the unique portal group - * identifier in order to get the port. - * - * @param[in] device Pointer to iSCSI device to be searched. May - * NOT be NULL, so take caution. - * @param[in] id Portal group ID to be searched for. - * @return Pointer to iSCSI port belonging to the iSCSI - * portal group ID or NULL if either the portal - * group ID does not exist or the port is NOT in use. - */ -iscsi_port *iscsi_device_find_port_by_portal_group_tag(const iscsi_device *device, const uint64_t id) -{ - iscsi_port *port; + image = image_getByWwn( wwn, rev, true ); - if ( iscsi_hashmap_get( device->ports, (uint8_t *) &id, sizeof(id), (uint8_t **) &port ) < 0 ) - return NULL; + 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 ( (port == NULL) || ((port->flags & ISCSI_PORT_FLAGS_IN_USE) == 0) ) - return NULL; + if ( len > 0U ) + free( tmp ); - return port; + 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 ); - - if ( session->connections == NULL ) { - logadd( LOG_ERROR, "iscsi_session_create: Out of memory allocating iSCSI session connection hash map" ); + session->tag = conn->pg_tag; + session->flags = 0; - free( session ); - - return NULL; - } + if ( (iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_INIT_R2T) != 0 ) + session->flags |= ISCSI_SESSION_FLAGS_INIT_R2T; - uint8_t *conn_key = iscsi_hashmap_key_create( (uint8_t *) &conn->cid, sizeof(conn->cid) ); + if ( (iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_IMMEDIATE_DATA) != 0 ) + session->flags |= ISCSI_SESSION_FLAGS_IMMEDIATE_DATA; - if ( conn_key == NULL ) { - logadd( LOG_ERROR, "iscsi_session_create: Out of memory allocating iSCSI session connection hash map" ); + if ( (iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_DATA_PDU_IN_ORDER) != 0 ) + session->flags |= ISCSI_SESSION_FLAGS_DATA_PDU_IN_ORDER; - iscsi_hashmap_destroy( session->connections ); - free( session ); - - return NULL; - } + if ( (iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_DATA_SEQ_IN_ORDER) != 0 ) + session->flags |= ISCSI_SESSION_FLAGS_DATA_SEQ_IN_ORDER; - iscsi_hashmap_put( session->connections, conn_key, sizeof(conn->cid), (uint8_t *) conn ); + 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; - 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; @@ -2922,19 +8975,44 @@ iscsi_session *iscsi_session_create(iscsi_connection *conn, iscsi_target_node *t return session; } +/** + * @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,33 +9151,118 @@ 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. * @@ -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; + + iscsi_scsi_task_lun_process_none( &sub_task->scsi_task ); + iscsi_scsi_task_xfer_complete( &sub_task->scsi_task ); + + 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 ); + } - const int rc = (int) send( conn->sock, buf, len, 0L ); + if ( task->pos == task->scsi_task.xfer_len ) + iscsi_list_remove( &task->node ); + } - return (rc > 0) ? rc : ISCSI_CONNECT_PDU_READ_ERR_FATAL; + 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 += (len + 1); - list += (strlen( (char *) list ) + 1); - } while ( list[0] != '\0' ); + 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); - rc = iscsi_get_bool_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IMMEDIATE_DATA, &int_val); + if ( int_val != 0L ) + conn->session->flags |= ISCSI_SESSION_FLAGS_INIT_R2T; + + 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); + + 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; + } + + 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 ); + + 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_LOGIN_RESPONSE; + 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; + 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_FATAL; + 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); @@ -4395,117 +10720,519 @@ static int iscsi_connection_login_check_session(iscsi_connection *conn, iscsi_pd iscsi_connection_drop( conn, init_port_name, 0 ); } - return rc; + return rc; +} + +/** + * @brief Initializes a rejecting login response packet. + * + * The login response structure has status detail + * invalid login request type set. + * + * @param[in] login_response_pdu Pointer to iSCSI login response PDU, + * NULL is an invalid value here, so take caution. + * @param[in] pdu Pointer to iSCSI login request PDU, may NOT + * be NULL, so be careful. + */ +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_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; +} + +/** + * @brief Creates an iSCSI PDU structure used by connections. + * + * The PDU structure is used for allowing partial + * reading from the TCP/IP socket and correctly + * 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. + * @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, 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 ) { + logadd( LOG_ERROR, "iscsi_pdu_create: Out of memory while allocating iSCSI 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 ); + + if ( bhs_pkt == NULL ) { + logadd( LOG_ERROR, "iscsi_pdu_create: Out of memory while allocating iSCSI PDU packet data" ); + + free( pdu ); + + return NULL; + } + + 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; +} + +/** + * @brief Destroys an iSCSI PDU structure used by connections. + * + * All associated data which has been read so + * far will be freed as well. + * + * @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) && (--pdu->ref == 0UL) ) { + if ( pdu->bhs_pkt != NULL ) { + free( pdu->bhs_pkt ); + + pdu->bhs_pkt = NULL; + pdu->ahs_pkt = NULL; + pdu->header_digest = NULL; + pdu->ds_cmd_data = NULL; + pdu->data_digest = NULL; + } + + free( 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 Initializes a rejecting login response packet. + * @brief Validates a stored iSCSI data digest (CRC32C) with actual DataSegment. * - * The login response structure has status detail - * invalid login request type set. + * Verifies data digest (CRC32C) with + * 0x82F63B78 polynomial reflect according + * to iSCSI specs. This function cannot + * fail. * - * @param[in] login_response_pdu Pointer to iSCSI login response PDU, - * NULL is an invalid value here, so take caution. - * @param[in] pdu Pointer to iSCSI login request PDU, may NOT + * @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. */ -void iscsi_login_response_reject_init(iscsi_pdu *login_response_pdu, const iscsi_pdu *pdu) +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) { - iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; + 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; - 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; + return (iscsi_get_le32(crc32c) == data_digest->crc32c); } /** - * @brief Creates an iSCSI PDU structure used by connections. + * @brief Checks whether iSCSI PDU cleanup procedure has to be deferred. * - * The PDU structure is used for allowing partial - * reading from the TCP/IP socket and correctly - * filling the data until everything has been read. + * This function checks whether the cleanup + * process of a written PDU has to be + * deferred to a later stage. * - * @param[in] conn Pointer to connection to link the PDU with. - * If this is NULL the connection has to be linked later. - * @return Pointer to allocated and zero filled PDU or NULL - * in case of an error (usually memory exhaustion). + * @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. */ -iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn) +static inline bool iscsi_connection_pdu_free_is_deferred(const iscsi_pdu *pdu) { - iscsi_pdu *pdu = (iscsi_pdu *) malloc( sizeof(struct iscsi_pdu) ); - - if ( pdu == NULL ) { - logadd( LOG_ERROR, "iscsi_pdu_create: Out of memory while allocating iSCSI PDU" ); - - return NULL; - } - - pdu->bhs_pkt = iscsi_create_packet(); - - if ( pdu->bhs_pkt == NULL ) { - free( pdu ); - - return NULL; - } - - 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 ( 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; - - return pdu; + return ((pdu != NULL) && ((pdu->bhs_pkt->opcode == ISCSI_OPCODE_SERVER_READY_XFER) || (pdu->bhs_pkt->opcode == ISCSI_OPCODE_SERVER_SCSI_DATA_IN))); } /** - * @brief Destroys an iSCSI PDU structure used by connections. + * @brief Handles iSCSI PDU cleanup after the PDU has been sent via TCP/IP to the client. * - * All associated data which has been read so - * far will be freed as well. + * 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] pdu PDU structure to be deallocated, may be NULL - * in which case this function does nothing. + * @param[in] user_data Pointer to iSCSI PDU which completed + * the TCP/IP write. May NOT be NULL, so be + * careful. */ -void iscsi_connection_pdu_destroy(iscsi_pdu *pdu) +static void iscsi_connection_pdu_write_complete(uint8_t *user_data, int err) { - 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 ); + iscsi_pdu *pdu = (iscsi_pdu *) user_data; + iscsi_connection *conn = pdu->conn; - pdu->key_value_pairs = NULL; - } + if ( conn->state >= ISCSI_CONNECT_STATE_EXITING ) + return; - if ( pdu->bhs_pkt != NULL ) { - free( pdu->bhs_pkt ); + iscsi_list_remove( &pdu->node ); - pdu->bhs_pkt = NULL; - } + if ( err != 0 ) + conn->state = ISCSI_CONNECT_STATE_EXITING; - free( pdu ); - } + 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 ); } /** @@ -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 ( (conn->data_digest != 0) && (pdu->ds_len != 0) ) - iscsi_calc_data_digest( pdu->bhs_pkt, conn->header_digest ); + if ( exec_queue == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_pdu_write: Out of memory while allocating execution queue for PDU write" ); + + 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_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; - } + iscsi_reject_packet *reject_pkt = (iscsi_reject_packet *) response_pdu->bhs_pkt; - 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; + + 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 ); - return 0L; + 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. @@ -4767,29 +11701,242 @@ static int iscsi_connection_pdu_header_handle_task_func_req(iscsi_connection *co { // TODO: Implement opcode. - return 0; + return 0; +} + +/** + * @brief Handles an incoming iSCSI header text request PDU. + * + * This function handles text request header + * 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] 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) +{ + 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 ); + + 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 Handles an incoming iSCSI header text request PDU. + * @brief Resends the Ready To Transfer (R2T) packet data. * - * This function handles text request header - * data sent by the client.\n - * If a response needs to be sent, this will - * be done as well. + * This function either sends a new R2T packet or + * resends an already sent one. * - * @param[in] conn iSCSI connection to handle. May - * NOT be NULL, so take caution. - * @param[in] pdu 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. + * @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_connection_pdu_header_handle_text_req(iscsi_connection *conn, iscsi_pdu *pdu) +static int iscsi_r2t_recovery_send(iscsi_connection *conn, iscsi_task *task, const uint32_t r2t_sn_ack, const int r2t_sn_send_new) { - // TODO: Implement opcode. + iscsi_pdu *pdu = iscsi_r2t_remove_pdu_from_snack_list( conn, task, r2t_sn_ack ); - return 0; + 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; + iscsi_nop_in_packet *nop_in_pkt = (iscsi_nop_in_packet *) response_pdu->bhs_pkt; - 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; - } - - 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 ); @@ -5033,6 +12299,139 @@ static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscs return ISCSI_CONNECT_PDU_READ_OK; } +/** + * @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. * @@ -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 ); + + 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; + } - return 0L; + 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; + + 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; - return 0L; + 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); + + bhs_pkt = iscsi_connection_pdu_append( pdu, ahs_len, conn->header_digest, ds_len, conn->data_digest ); + + 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) ); - const uint ahs_len = (uint) pdu->bhs_pkt->total_ahs_len << 2UL; + if ( stat_value != NULL ) { + uint8_t *stat_key = iscsi_hashmap_key_create( (uint8_t *) &stat_opcode, sizeof(stat_opcode) ); - 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 ( 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 ) - 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 ( 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 ( 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 ); - if ( rc == 0 ) + 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 ); + + 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 +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "globals.h" +#include "image.h" + +#if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) + // GCC-compatible compiler, targeting x86/x86-64 + #include +#elif defined(__GNUC__) && defined(__ARM_NEON__) + // GCC-compatible compiler, targeting ARM with NEON + #include +#elif defined(__GNUC__) && defined(__IWMMXT__) + // GCC-compatible compiler, targeting ARM with WMMX + #include +#elif (defined(__GNUC__) || defined(__xlC__)) && (defined(__VEC__) || defined(__ALTIVEC__)) + // XLC or GCC-compatible compiler, targeting PowerPC with VMX/VSX + #include +#elif defined(__GNUC__) && defined(__SPE__) + // GCC-compatible compiler, targeting PowerPC with SPE + #include +#elif defined(_MSC_VER) + // Microsoft C/C++-compatible compiler + #include +#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) = value; +} + +static inline void iscsi_put_be24(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 value) +{ + (*(uint32_t *) data) = value; +} + +static inline void iscsi_put_be64(uint8_t *data, const uint64_t value) +{ + (*(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) = val; + (*(uint16_t *) data) = iscsi_get_le16(value); } -static inline void iscsi_put_be24(uint8_t *data, const uint32_t val) +static inline void iscsi_put_le24(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 | (iscsi_get_le32(value) & 0xFFFFFF00UL)); } -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) { - (*(uint32_t *) *data) = val; + (*(uint32_t *) data) = iscsi_get_le32(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) { - (*(uint64_t *) data) = val; + (*(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 -// 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) +{ + (*(uint64_t *) data) = iscsi_get_be64(value); +} + +#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 >> 8U); - data[1] = (uint8_t) val; + (*(uint16_t *) data) = value; } -static inline void iscsi_put_be24(uint8_t *data, const uint32_t val) +static inline void iscsi_put_le24(uint8_t *data, const uint32_t value) { - data[0] = (uint8_t) (val >> 16UL); - data[1] = (uint8_t) (val >> 8UL); - 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_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,3018 +1168,6564 @@ typedef struct __attribute__((packed)) iscsi_data_digest { uint32_t crc32c; } iscsi_data_digest; + /** - * @brief iSCSI 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 - * the failed command. + * @brief iSCSI SCSI CDB packet data structure. * - * For some iSCSI responses, the response data segment MAY contain some - * response-related information (e.g., for a target failure, it may - * contain a vendor-specific detailed description of the failure). + * 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_ds_cmd_data { - /// SenseLength: This field indicates the length of Sense Data. - uint16_t len; +typedef struct __attribute__((packed)) iscsi_scsi_cdb { + /// SCSI opcode. + uint8_t opcode; - /// The Sense Data contains detailed information about a CHECK CONDITION. SPC3 specifies the format and content of the Sense Data. - uint8_t sense_data[0]; + /// Additional op-code specific data. + uint8_t data[0]; +} iscsi_scsi_cdb; - /// Response Data. - uint8_t res_data[0]; -} iscsi_ds_cmd_data; -/// SCSI command opcode (embedded in iSCSI protocol): TEST UNIT READY. -#define ISCSI_SCSI_OPCODE_TESTUNITREADY 0x00 +/// iSCSI SCSI Command Descriptor Block (CDB) for INQUIRY command flags: Enable Vital Product Data (EVPD). +#define ISCSI_SCSI_CDB_INQUIRY_FLAGS_EVPD (1 << 0) -/// SCSI command opcode (embedded in iSCSI protocol): READ(6). -#define ISCSI_SCSI_OPCODE_READ6 0x08 +/// iSCSI SCSI Command Descriptor Block (CDB) for INQUIRY command flags: Command Support Data (CMDDT). +#define ISCSI_SCSI_CDB_INQUIRY_FLAGS_CMDDT (1 << 1) -/// SCSI command opcode (embedded in iSCSI protocol): INQUIRY. -#define ISCSI_SCSI_OPCODE_INQUIRY 0x12 -/// SCSI command opcode (embedded in iSCSI protocol): MODE SELECT(6). -#define ISCSI_SCSI_OPCODE_MODESELECT6 0x15 +/** + * @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; -/// SCSI command opcode (embedded in iSCSI protocol): RESERVE(6). -#define ISCSI_SCSI_OPCODE_RESERVE6 0x16 + /// Logical Unit Number (LUN), CMMDT and EVPD. + uint8_t lun_flags; -/// SCSI command opcode (embedded in iSCSI protocol): RELEASE(6). -#define ISCSI_SCSI_OPCODE_RELEASE6 0x17 + /// Page code. + uint8_t page_code; -/// SCSI command opcode (embedded in iSCSI protocol): MODE SENSE(6). -#define ISCSI_SCSI_OPCODE_MODESENSE6 0x1A + /// Allocation length in bytes. + uint16_t alloc_len; -/// SCSI command opcode (embedded in iSCSI protocol): START STOP UNIT. -#define ISCSI_SCSI_OPCODE_STARTSTOPUNIT 0x1B + /// Control. + uint8_t control; +} iscsi_scsi_cdb_inquiry; -/// SCSI command opcode (embedded in iSCSI protocol): PREVENT ALLOW MEDIUM REMOVAL. -#define ISCSI_SCSI_OPCODE_PREVENTALLOW 0x1E -/// SCSI command opcode (embedded in iSCSI protocol): READ CAPACITY(10). -#define ISCSI_SCSI_OPCODE_READCAPACITY10 0x25 +/** + * @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; -/// SCSI command opcode (embedded in iSCSI protocol): READ(10). -#define ISCSI_SCSI_OPCODE_READ10 0x28 + /// Logical Block Address (LBA). + uint8_t lba[3]; -/// SCSI command opcode (embedded in iSCSI protocol): WRITE(10). -#define ISCSI_SCSI_OPCODE_WRITE10 0x2A + /// Transfer length in bytes. + uint8_t xfer_len; -/// SCSI command opcode (embedded in iSCSI protocol): WRITE AND VERIFY(10). -#define ISCSI_SCSI_OPCODE_WRITE_VERIFY10 0x2E + /// Control. + uint8_t control; +} iscsi_scsi_cdb_read_write_6; -/// SCSI command opcode (embedded in iSCSI protocol): VERIFY(10). -#define ISCSI_SCSI_OPCODE_VERIFY10 0x2F -/// SCSI command opcode (embedded in iSCSI protocol): PRE-FETCH(10). -#define ISCSI_SCSI_OPCODE_PREFETCH10 0x34 +/** + * @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; -/// SCSI command opcode (embedded in iSCSI protocol): SYNCHRONIZE CACHE(10). -#define ISCSI_SCSI_OPCODE_SYNCHRONIZECACHE10 0x35 + /// Flags. + int8_t flags; -/// SCSI command opcode (embedded in iSCSI protocol): READ DEFECT DATA(10). -#define ISCSI_SCSI_OPCODE_READ_DEFECT_DATA10 0x37 + /// Logical Block Address (LBA). + uint32_t lba; -/// SCSI command opcode (embedded in iSCSI protocol): WRITE SAME(10). -#define ISCSI_SCSI_OPCODE_WRITE_SAME10 0x41 + /// Group number. + int8_t group_num; -/// SCSI command opcode (embedded in iSCSI protocol): UNMAP. -#define ISCSI_SCSI_OPCODE_UNMAP 0x42 + /// Transfer length in bytes. + uint16_t xfer_len; -/// SCSI command opcode (embedded in iSCSI protocol): READ TOC/PMA/ATIP. -#define ISCSI_SCSI_OPCODE_READTOC 0x43 + /// Control. + uint8_t control; +} iscsi_scsi_cdb_read_write_10; -/// SCSI command opcode (embedded in iSCSI protocol): SANITIZE. -#define ISCSI_SCSI_OPCODE_SANITIZE 0x48 -/// SCSI command opcode (embedded in iSCSI protocol): MODE SELECT(10). -#define ISCSI_SCSI_OPCODE_MODESELECT10 0x55 +/** + * @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; -/// SCSI command opcode (embedded in iSCSI protocol): MODE SENSE(10). -#define ISCSI_SCSI_OPCODE_MODESENSE10 0x5A + /// Flags. + int8_t flags; -/// SCSI command opcode (embedded in iSCSI protocol): PERSISTENT RESERVE IN. -#define ISCSI_SCSI_OPCODE_PERSISTENT_RESERVE_IN 0x5E + /// Logical Block Address (LBA). + uint32_t lba; -/// SCSI command opcode (embedded in iSCSI protocol): PERSISTENT RESERVE OUT. -#define ISCSI_SCSI_OPCODE_PERSISTENT_RESERVE_OUT 0x5F + /// Transfer length in bytes. + uint32_t xfer_len; -/// SCSI command opcode (embedded in iSCSI protocol): Third-party Copy OUT. -#define ISCSI_SCSI_OPCODE_EXTENDED_COPY 0x83 + /// Restricted for MMC-6 and group number. + int8_t restrict_group_num; -/// SCSI command opcode (embedded in iSCSI protocol): Third-party Copy IN. -#define ISCSI_SCSI_OPCODE_RECEIVE_COPY_RESULTS 0x84 + /// Control. + uint8_t control; +} iscsi_scsi_cdb_read_write_12; -/// SCSI command opcode (embedded in iSCSI protocol): READ(16). -#define ISCSI_SCSI_OPCODE_READ16 0x88 -/// SCSI command opcode (embedded in iSCSI protocol): COMPARE AND WRITE. -#define ISCSI_SCSI_OPCODE_COMPARE_AND_WRITE 0x89 +/** + * @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; -/// SCSI command opcode (embedded in iSCSI protocol): WRITE(16). -#define ISCSI_SCSI_OPCODE_WRITE16 0x8A + /// Flags. + int8_t flags; -/// SCSI command opcode (embedded in iSCSI protocol): ORWRITE. -#define ISCSI_SCSI_OPCODE_ORWRITE 0x8B + /// Logical Block Address (LBA). + uint64_t lba; -/// SCSI command opcode (embedded in iSCSI protocol): WRITE AND VERIFY(16). -#define ISCSI_SCSI_OPCODE_WRITE_VERIFY16 0x8E + /// Transfer length in bytes. + uint32_t xfer_len; -/// SCSI command opcode (embedded in iSCSI protocol): VERIFY(16). -#define ISCSI_SCSI_OPCODE_VERIFY16 0x8F + /// Restricted for MMC-6 and group number. + int8_t restrict_group_num; -/// SCSI command opcode (embedded in iSCSI protocol): PRE-FETCH(16). -#define ISCSI_SCSI_OPCODE_PREFETCH16 0x90 + /// Control. + uint8_t control; +} iscsi_scsi_cdb_read_write_16; -/// SCSI command opcode (embedded in iSCSI protocol): SYNCHRONIZE CACHE(16). -#define ISCSI_SCSI_OPCODE_SYNCHRONIZECACHE16 0x91 -/// SCSI command opcode (embedded in iSCSI protocol): WRITE SAME(16). -#define ISCSI_SCSI_OPCODE_WRITE_SAME16 0x93 +/// 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 -/// SCSI command opcode (embedded in iSCSI protocol): WRITE ATOMIC(16). -#define ISCSI_SCSI_OPCODE_WRITE_ATOMIC16 0x9C +/// 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 -/// SCSI command opcode (embedded in iSCSI protocol): SERVICE ACTION IN(16). -#define ISCSI_SCSI_OPCODE_SERVICE_ACTION_IN 0x9E +/// 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 -/// SCSI command opcode (embedded in iSCSI protocol): REPORT LUNS. -#define ISCSI_SCSI_OPCODE_REPORTLUNS 0xA0 -/// SCSI command opcode (embedded in iSCSI protocol): MAINTENANCE IN. -#define ISCSI_SCSI_OPCODE_MAINTENANCE_IN 0xA3 +/** + * @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; -/// SCSI command opcode (embedded in iSCSI protocol): READ(12). -#define ISCSI_SCSI_OPCODE_READ12 0xA8 + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved; -/// SCSI command opcode (embedded in iSCSI protocol): WRITE(12). -#define ISCSI_SCSI_OPCODE_WRITE12 0xAA + /// Select report. + uint8_t select_report; -/// SCSI command opcode (embedded in iSCSI protocol): WRITE AND VERIFY(12). -#define ISCSI_SCSI_OPCODE_WRITE_VERIFY12 0xAE + /// Reserved for future usage (always MUST be 0 for now). + uint16_t reserved2; -/// SCSI command opcode (embedded in iSCSI protocol): VERIFY(12). -#define ISCSI_SCSI_OPCODE_VERIFY12 0xAF + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved3; -/// SCSI command opcode (embedded in iSCSI protocol): READ DEFECT DATA(12). -#define ISCSI_SCSI_OPCODE_READ_DEFECT_DATA12 0xB7 + /// Allocation length in bytes. + uint32_t alloc_len; + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved4; -/** - * @brief iSCSI SCSI command flags: No unsolicited data. - * - * (F) is set to 1 when no unsolicited SCSI Data-Out PDUs - * follow this PDU. When F = 1 for a write and if Expected - * Data Transfer Length is larger than the - * DataSegmentLength, the target may solicit additional data - * through R2T. - */ -#define ISCSI_SCSI_CMD_FLAGS_TASK_NO_UNSOLICITED_DATA (1 << 7) + /// 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 command flags: Expected input data. - * - * (R) is set to 1 when the command is expected to input data. - */ -#define ISCSI_SCSI_CMD_FLAGS_TASK_READ (1 << 6) /** - * @brief iSCSI SCSI command flags: Expected output data. + * @brief iSCSI SCSI CDB packet data structure for SCSI COMPARE AND WRITE command. * - * (W) is set to 1 when the command is expected to output data. + * There are 16 bytes in the CDB field for this command. */ -#define ISCSI_SCSI_CMD_FLAGS_TASK_WRITE (1 << 5) +typedef struct __attribute__((packed)) iscsi_scsi_cdb_cmp_write { + /// SCSI opcode. + iscsi_scsi_cdb cdb; + /// Flags. + int8_t flags; -/// SCSI command flags task attribute: Untagged. -#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_UNTAGGED 0x0 + /// Logical Block Address (LBA). + uint64_t lba; -/// SCSI command flags task attribute: Simple. -#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_SIMPLE 0x1 + /// Reserved for future usage (always MUST be 0 for now). + uint16_t reserved; -/// SCSI command flags task attribute: Ordered. -#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_ORDERED 0x2 + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved2; -/// SCSI command flags task attribute: Head of queue. -#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_HEAD_QUEUE 0x3 + /// Number of blocks in bytes. + uint8_t num_blocks; -/// SCSI command flags task attribute: ACA. -#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_ACA 0x4 + /// Restricted for MMC-6 and group number. + int8_t restrict_group_num; -/// SCSI command flags task attribute: Reserved. -#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_RESERVED_1 0x5 + /// Control. + uint8_t control; +} iscsi_scsi_cdb_cmp_write; -/// SCSI command flags task attribute: Reserved. -#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_RESERVED_2 0x6 -/// SCSI command flags task attribute: Reserved. -#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_RESERVED_3 0x7 +/// 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 -/// SCSI command flags Task Attributes (ATTR) are encoded in the first three LSBs. -#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_MASK 0x7 +/// 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 Flag and Task Attributes for SCSI command packet data. + * @brief iSCSI SCSI CDB packet data structure for SCSI SERVICE IN ACTION(16) command. * - * Flags and Task Attributes: - * At least one of the W and F bits MUST be set to 1.\n - * Either or both of R and W MAY be 1 when the Expected Data Transfer - * Length and/or the Bidirectional Read Expected Data Transfer Length - * are 0, but they MUST NOT both be 0 when the Expected Data Transfer - * Length and/or Bidirectional Read Expected Data Transfer Length are - * not 0 (i.e., when some data transfer is expected, the transfer - * direction is indicated by the R and/or W bit). + * There are 16 bytes in the CDB field for this command. */ -typedef struct __attribute__((packed)) iscsi_scsi_cmd_packet { - /// Always 1 according to the iSCSI specification. - uint8_t opcode; +typedef struct __attribute__((packed)) iscsi_scsi_cdb_service_action_in_16 { + /// SCSI opcode. + iscsi_scsi_cdb cdb; - /// Flags and Task Attributes. - int8_t flags_task; + /// Service action. + uint8_t action; - /// Reserved for future usage, MUST always be 0. - uint16_t reserved; + /// Logical Block Address (LBA), obselete by now. + uint64_t lba; - /// Total length of AHS. - uint8_t total_ahs_len; + /// Allocation length in bytes. + uint32_t alloc_len; - /// Length of DataSegment. - uint8_t ds_len[3]; + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved; - /// SCSI LUN bit mask. - uint64_t lun; + /// Control. + uint8_t control; +} iscsi_scsi_cdb_service_action_in_16; - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; - /** - * @brief Expected Data Transfer Length. - * - * For unidirectional operations, the Expected Data Transfer Length - * field contains the number of bytes of data involved in this SCSI - * operation. For a unidirectional write operation (W flag set to 1 and - * R flag set to 0), the initiator uses this field to specify the number - * of bytes of data it expects to transfer for this operation. For a - * unidirectional read operation (W flag set to 0 and R flag set to 1), - * the initiator uses this field to specify the number of bytes of data - * it expects the target to transfer to the initiator. It corresponds - * to the SAM-2 byte count.\n - * For bidirectional operations (both R and W flags are set to 1), this - * field contains the number of data bytes involved in the write - * transfer. For bidirectional operations, an additional header segment - * MUST be present in the header sequence that indicates the - * Bidirectional Read Expected Data Transfer Length. The Expected Data - * Transfer Length field and the Bidirectional Read Expected Data - * Transfer Length field correspond to the SAM-2 byte count. - * If the Expected Data Transfer Length for a write and the length of - * the immediate data part that follows the command (if any) are the - * same, then no more data PDUs are expected to follow. In this case, - * the F bit MUST be set to 1.\n - * If the Expected Data Transfer Length is higher than the - * FirstBurstLength (the negotiated maximum amount of unsolicited data - * the target will accept), the initiator MUST send the maximum amount - * of unsolicited data OR ONLY the immediate data, if any. - * Upon completion of a data transfer, the target informs the initiator - * (through residual counts) of how many bytes were actually processed - * (sent and/or received) by the target. - */ - uint32_t exp_xfer_len; +/** + * @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; - /// The CmdSN enables ordered delivery across multiple connections in a single session. - uint32_t cmd_sn; + /// Flags. + int8_t flags; - /// Command responses up to ExpStatSN - 1 (modulo 2**32) have been received (acknowledges status) on the connection. - uint32_t exp_stat_sn; + /// Logical Block Address (LBA). + uint32_t lba; - /** - * @brief SCSI Command Descriptor Block (CDB). - * - * 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. - */ - iscsi_cdb scsi_cdb; + /// Group number. + int8_t group_num; - /// Optional AHS packet data. - iscsi_ahs_packet ahs; + /// Transfer length in bytes. + uint16_t xfer_len; - /// Optional header digest. - iscsi_header_digest hdr_digest; + /// Control. + uint8_t control; +} iscsi_scsi_cdb_sync_cache_10; - /// Optional data segment, command data. - iscsi_ds_cmd_data ds_cmd_data; - /// Optional data digest. - iscsi_data_digest data_digest; -} iscsi_scsi_cmd_packet; +/** + * @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 SCSI response flags: Residual Underflow. + * @brief iSCSI SCSI CDB packet data structure for SCSI WRITE SAME(10) command. * - * (U) set for Residual Underflow. In this case, the Residual - * Count indicates the number of bytes that were not - * transferred out of the number of bytes that were expected - * to be transferred. For a bidirectional operation, the - * Residual Count contains the residual for the write - * operation. - * - * Bits O and U and bits o and u are mutually exclusive (i.e., having - * both o and u or O and U set to 1 is a protocol error). - * - * For a response other than "Command Completed at Target", bits 3-6 - * MUST be 0. + * There are 10 bytes in the CDB field for this command. */ -#define ISCSI_SCSI_RESPONSE_FLAGS_RES_UNDERFLOW (1 << 1) +typedef struct __attribute__((packed)) iscsi_scsi_cdb_write_same_10 { + /// SCSI opcode. + iscsi_scsi_cdb cdb; -/** - * @brief SCSI response flags: Residual Overflow. - * - * (O) set for Residual Overflow. In this case, the Residual - * Count indicates the number of bytes that were not - * transferred because the initiator's Expected Data - * Transfer Length was not sufficient. For a bidirectional - * operation, the Residual Count contains the residual for - * the write operation. - * - * Bits O and U and bits o and u are mutually exclusive (i.e., having - * both o and u or O and U set to 1 is a protocol error). - * - * For a response other than "Command Completed at Target", bits 3-6 - * MUST be 0. - */ -#define ISCSI_SCSI_RESPONSE_FLAGS_RES_OVERFLOW (1 << 2) + /// Flags. + int8_t flags; -/** - * @brief SCSI response flags: Bidirectional Read Residual Underflow. - * - * (u) set for Bidirectional Read Residual Underflow. In this - * case, the Bidirectional Read Residual Count indicates the - * number of bytes that were not transferred to the - * initiator out of the number of bytes expected to be - * transferred. - * - * Bits O and U and bits o and u are mutually exclusive (i.e., having - * both o and u or O and U set to 1 is a protocol error). - * - * For a response other than "Command Completed at Target", bits 3-6 - * MUST be 0. - */ -#define ISCSI_SCSI_RESPONSE_FLAGS_BIDI_READ_RES_UNDERFLOW (1 << 3) + /// Logical Block Address (LBA). + uint32_t lba; -/** - * @brief SCSI response flags: Bidirectional Read Residual Overflow. - * - + (o) set for Bidirectional Read Residual Overflow. In this - * case, the Bidirectional Read Residual Count indicates the - * number of bytes that were not transferred to the - * initiator because the initiator's Bidirectional Read - * Expected Data Transfer Length was not sufficient. - * - * Bits O and U and bits o and u are mutually exclusive (i.e., having - * both o and u or O and U set to 1 is a protocol error). - * - * For a response other than "Command Completed at Target", bits 3-6 - * MUST be 0. - */ -#define ISCSI_SCSI_RESPONSE_FLAGS_BIDI_READ_RES_OVERFLOW (1 << 4) + /// Group number. + int8_t group_num; -/** - * @brief SCSI status response code: Good. - * - * The Status field is used to report the SCSI status of the command (as - * specified in SAM2) and is only valid if the response code is - * Command Completed at Target. - * - * If a SCSI device error is detected while data from the initiator is - * still expected (the command PDU did not contain all the data and the - * target has not received a data PDU with the Final bit set), the - * target MUST wait until it receives a data PDU with the F bit set in - * the last expected sequence before sending the Response PDU. - */ -#define ISCSI_SCSI_RESPONSE_STATUS_GOOD 0x00 + /// Transfer length in bytes. + uint16_t xfer_len; + + /// Control. + uint8_t control; +} iscsi_scsi_cdb_write_same_10; -/** - * @brief SCSI status response code: Check condition. - * - * The Status field is used to report the SCSI status of the command (as - * specified in SAM2) and is only valid if the response code is - * Command Completed at Target. - * - * If a SCSI device error is detected while data from the initiator is - * still expected (the command PDU did not contain all the data and the - * target has not received a data PDU with the Final bit set), the - * target MUST wait until it receives a data PDU with the F bit set in - * the last expected sequence before sending the Response PDU. - */ -#define ISCSI_SCSI_RESPONSE_STATUS_CHECK_COND 0x02 /** - * @brief SCSI status response code: Busy. - * - * The Status field is used to report the SCSI status of the command (as - * specified in SAM2) and is only valid if the response code is - * Command Completed at Target. + * @brief iSCSI SCSI CDB packet data structure for SCSI WRITE SAME(16) command. * - * If a SCSI device error is detected while data from the initiator is - * still expected (the command PDU did not contain all the data and the - * target has not received a data PDU with the Final bit set), the - * target MUST wait until it receives a data PDU with the F bit set in - * the last expected sequence before sending the Response PDU. + * There are 16 bytes in the CDB field for this command. */ -#define ISCSI_SCSI_RESPONSE_STATUS_BUSY 0x08 +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 SCSI status response code: Residual conflict. - * - * The Status field is used to report the SCSI status of the command (as - * specified in SAM2) and is only valid if the response code is - * Command Completed at Target. + * @brief iSCSI SCSI CDB packet data structure for SCSI MODE SELECT(6) command. * - * If a SCSI device error is detected while data from the initiator is - * still expected (the command PDU did not contain all the data and the - * target has not received a data PDU with the Final bit set), the - * target MUST wait until it receives a data PDU with the F bit set in - * the last expected sequence before sending the Response PDU. + * There are 6 bytes in the CDB field for this command. */ -#define ISCSI_SCSI_RESPONSE_STATUS_RES_CONFLICT 0x18 +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 SCSI status response code: Task set full. + * @brief iSCSI SCSI CDB packet data structure for SCSI MODE SELECT(10) command. * - * The Status field is used to report the SCSI status of the command (as - * specified in SAM2) and is only valid if the response code is - * Command Completed at Target. - * - * If a SCSI device error is detected while data from the initiator is - * still expected (the command PDU did not contain all the data and the - * target has not received a data PDU with the Final bit set), the - * target MUST wait until it receives a data PDU with the F bit set in - * the last expected sequence before sending the Response PDU. + * There are 10 bytes in the CDB field for this command. */ -#define ISCSI_SCSI_RESPONSE_STATUS_TASK_SET_FULL 0x28 +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 SCSI status response code: ACA active. - * - * The Status field is used to report the SCSI status of the command (as - * specified in SAM2) and is only valid if the response code is - * Command Completed at Target. + * @brief iSCSI SCSI CDB packet data structure for SCSI MODE SENSE(6) command. * - * If a SCSI device error is detected while data from the initiator is - * still expected (the command PDU did not contain all the data and the - * target has not received a data PDU with the Final bit set), the - * target MUST wait until it receives a data PDU with the F bit set in - * the last expected sequence before sending the Response PDU. + * There are 6 bytes in the CDB field for this command. */ -#define ISCSI_SCSI_RESPONSE_STATUS_ACA_ACTIVE 0x30 +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 SCSI status response code: Task aborted. - * - * The Status field is used to report the SCSI status of the command (as - * specified in SAM2) and is only valid if the response code is - * Command Completed at Target. + * @brief iSCSI SCSI CDB packet data structure for SCSI MODE SENSE(10) command. * - * If a SCSI device error is detected while data from the initiator is - * still expected (the command PDU did not contain all the data and the - * target has not received a data PDU with the Final bit set), the - * target MUST wait until it receives a data PDU with the F bit set in - * the last expected sequence before sending the Response PDU. + * There are 10 bytes in the CDB field for this command. */ -#define ISCSI_SCSI_RESPONSE_STATUS_TASK_ABORTED 0x40 +typedef struct __attribute__((packed)) iscsi_scsi_cdb_mode_sense_10 { + /// SCSI opcode. + iscsi_scsi_cdb cdb; -/// SCSI response code: Command Completed at Target. -#define ISCSI_SCSI_RESPONSE_CODE_OK 0x00 + /// Flags. + int8_t flags; -/// SCSI response code: Target Failure. -#define ISCSI_SCSI_RESPONSE_CODE_FAIL 0x01 + /// Page code and page control. + uint8_t page_code_control; -/// SCSI response code: First vendor specific response code. -#define ISCSI_SCSI_RESPONSE_CODE_VENDOR_FIRST 0x80 + /// 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) -/// SCSI response code: Last vendor specific response code. -#define ISCSI_SCSI_RESPONSE_CODE_VENDOR_LAST 0xFF /** - * @brief iSCSI SCSI command response packet data. + * @brief iSCSI SCSI CDB packet data structure for SCSI REQUEST SENSE command. * - * The Response field is used to report a service response. The mapping - * of the response code into a SCSI service response code value, if - * needed, is outside the scope of this document. However, in symbolic - * terms, response value 0x00 maps to the SCSI service response (see + * There are 6 bytes in the CDB field for this command. */ -typedef struct __attribute__((packed)) iscsi_scsi_response_packet { - /// Always 0x21 according to specification. - uint8_t opcode; +typedef struct __attribute__((packed)) iscsi_scsi_cdb_req_sense { + /// SCSI opcode. + iscsi_scsi_cdb cdb; /// Flags. int8_t flags; - /// This field contains the iSCSI service response. - uint8_t response; + /// Reserved for future usage (always MUST be 0 for now). + uint16_t reserved; - /// The Status field is used to report the SCSI status of the command (as specified in SAM2) and is only valid if the response code is Command Completed at Target. - uint8_t status; + /// Allocation length in bytes. + uint8_t alloc_len; - /// Total AHS length. - uint8_t total_ahs_len; + /// Control. + uint8_t control; +} iscsi_scsi_cdb_req_sense; - /// Data segment length. - uint8_t ds_len[3]; - /// Reserved for future usage. Always MUST be 0. - uint64_t reserved; +/// 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) - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; - /** - * @brief Copy of the last accepted Selective Negative / Sequence Number Acknowledgment (SNACK) tag. - * - * This field contains a copy of the SNACK Tag of the last SNACK Tag - * accepted by the target on the same connection and for the command for - * which the response is issued. Otherwise, it is reserved and should - * be set to 0.\n - * After issuing a R-Data SNACK, the initiator must discard any SCSI - * status unless contained in a SCSI Response PDU carrying the same - * SNACK Tag as the last issued R-Data SNACK for the SCSI command on the - * current connection. - */ - uint32_t snack_tag; +/// 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 - /** - * @brief StatSN - Status Sequence Number. - * - * The StatSN is a sequence number that the target iSCSI layer generates - * per connection and that in turn enables the initiator to acknowledge - * status reception. The StatSN is incremented by 1 for every - * response/status sent on a connection, except for responses sent as a - * result of a retry or SNACK. In the case of responses sent due to a - * retransmission request, the StatSN MUST be the same as the first time - * the PDU was sent, unless the connection has since been restarted. - */ - uint32_t stat_sn; +/// 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 - /** - * @brief ExpCmdSN - Next Expected CmdSN from This Initiator. - * - * The ExpCmdSN is a sequence number that the target iSCSI returns to - * the initiator to acknowledge command reception. It is used to update - * a local variable with the same name. An ExpCmdSN equal to - * MaxCmdSN + 1 indicates that the target cannot accept new commands. - */ - uint32_t exp_cmd_sn; +/// 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 - /** - * @brief MaxCmdSN - Maximum CmdSN from This Initiator. - * - * The MaxCmdSN is a sequence number that the target iSCSI returns to - * the initiator to indicate the maximum CmdSN the initiator can send. - * It is used to update a local variable with the same name. If the - * MaxCmdSN is equal to ExpCmdSN - 1, this indicates to the initiator - * that the target cannot receive any additional commands. When the - * MaxCmdSN changes at the target while the target has no pending PDUs - * to convey this information to the initiator, it MUST generate a - * NOP-In to carry the new MaxCmdSN. - */ - uint32_t max_cmd_sn; +/// 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 - /** - * @brief ExpDataSN or Reserved. - * - * This field indicates the number of Data-In (read) PDUs the target has - * sent for the command.\n - * This field MUST be 0 if the response code is not Command Completed at - * Target or the target sent no Data-In PDUs for the command. - */ - uint32_t exp_data_sn; +/// 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 - /** - * @brief Bidirectional Read Residual Count or Reserved. - * - * The Bidirectional Read Residual Count field MUST be valid in the case - * where either the u bit or the o bit is set. If neither bit is set, - * the Bidirectional Read Residual Count field is reserved. Targets may - * set the Bidirectional Read Residual Count, and initiators may use it - * when the response code is Command Completed at Target. If the o bit - * is set, the Bidirectional Read Residual Count indicates the number of - * bytes that were not transferred to the initiator because the - * initiator's Bidirectional Read Expected Data Transfer Length was not - * sufficient. If the u bit is set, the Bidirectional Read Residual - * Count indicates the number of bytes that were not transferred to the - * initiator out of the number of bytes expected to be transferred. - */ - uint32_t bidi_read_res_cnt; +/// 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 - /** - * @brief Residual Count or Reserved. - * - * The Residual Count field MUST be valid in the case where either the U - * bit or the O bit is set. If neither bit is set, the Residual Count - * field MUST be ignored on reception and SHOULD be set to 0 when - * sending. Targets may set the residual count, and initiators may use - * it when the response code is Command Completed at Target (even if the - * status returned is not GOOD). If the O bit is set, the Residual - * Count indicates the number of bytes that were not transferred because - * the initiator's Expected Data Transfer Length was not sufficient. If - * the U bit is set, the Residual Count indicates the number of bytes - * that were not transferred out of the number of bytes expected to be - * transferred. - */ - uint32_t res_cnt; +/// 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 - /// Optional header digest. - iscsi_header_digest hdr_digest; +/// 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 - /// Optional data segment, command data. - iscsi_ds_cmd_data ds_cmd_data; +/// 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 - /// Optional data digest. - iscsi_data_digest data_digest; -} iscsi_scsi_response_packet; +/// 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 -/// Task management request function: ABORT TASK: aborts the task identified by the Referenced Task Tag field. -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_ABORT_TASK 0x01 +/// 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 -/// Task management request function: ABORT TASK SET: aborts all tasks issued via this session on the LU. -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_ABORT_TASK_SET 0x02 +/// 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 -/// Task management request function: CLEAR ACA - clears the Auto Contingent Allegiance condition. -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_CLEAR_ACA 0x03 +/// 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 -/// Task management request function: CLEAR TASK SET - aborts all tasks in the appropriate task set as defined by the TST field in the Control mode page (see SPC3). -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_CLEAR_TASK_SET 0x04 +/// 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) -/// Task management request function: LOGICAL UNIT RESET. -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_LOGICAL_UNIT_RESET 0x05 +/// 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)) -/// Task management request function: TARGET WARM RESET. -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_TARGET_WARM_RESET 0x06 +/// 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)) -/// Task management request function: TARGET COLD RESET. -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_TARGET_COLD_RESET 0x07 +/// 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)) -/// Task management request function: TASK REASSIGN - reassigns connection allegiance for the task identified by the Initiator Task Tag field to this connection, thus resuming the iSCSI exchanges for the task. -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_TASK_REASSIGN 0x08 +/// 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) -/** - * @brief iSCSI Task Management Function Request packet data. - * - * This structure is used to explicity control the execution of one - * or more tasks (iSCSI and SCSI). - */ -typedef struct __attribute__((packed)) iscsi_task_mgmt_func_req_packet { - /// Always 2 according to iSCSI specification. - uint8_t opcode; +/// 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) - /** - * @brief Function. - * - * The task management functions provide an initiator with a way to - * explicitly control the execution of one or more tasks (SCSI and iSCSI - * tasks). The task management function codes are listed below. For a - * more detailed description of SCSI task management, see SAM2. - */ - int8_t func; +/// 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) - /// Reserved fot future usage, always MUST be 0. - uint16_t reserved; +/// 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 - /// TotalAHSLength (MUST be 0 for this PDU). - uint8_t total_ahs_len; +/// 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 - /// DataSegmentLength (MUST be 0 for this PDU). - uint8_t ds_len[3]; +/// 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 - /** - * @brief Logical Unit Number (LUN) or Reserved. - * - * This field is required for functions that address a specific LU - * (ABORT TASK, CLEAR TASK SET, ABORT TASK SET, CLEAR ACA, LOGICAL UNIT - * RESET) and is reserved in all others - */ - uint64_t lun; +/// 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 - /** - * @brief Initiator Task Tag (ITT). - * - * This is the Initiator Task Tag of the task to be aborted for the - * ABORT TASK function or reassigned for the TASK REASSIGN function. - * For all the other functions, this field MUST be set to the reserved - * value 0xFFFFFFFF. - */ - uint32_t init_task_tag; +/// 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 - /// Referenced task tag or 0xFFFFFFFF. - uint32_t ref_task_tag; +/// 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 - /// CmdSN. - uint32_t cmd_sn; +/// 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 - /// ExpStatSN - uint32_t exp_stat_sn; +/// 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 - /** - * @brief RefCmdSN or Reserved. - * - * If an ABORT TASK is issued for a task created by an immediate - * command, then the RefCmdSN MUST be that of the task management - * request itself (i.e., the CmdSN and RefCmdSN are equal).\n - * For an ABORT TASK of a task created by a non-immediate command, the - * RefCmdSN MUST be set to the CmdSN of the task identified by the - * Referenced Task Tag field. Targets must use this field when the task - * identified by the Referenced Task Tag field is not with the target. - * Otherwise, this field is reserved. - */ - uint32_t ref_cmd_sn; +/// 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 - /** - * @brief ExpDataSN or Reserved. - * - * For recovery purposes, the iSCSI target and initiator maintain a data - * acknowledgment reference number - the first input DataSN number - * unacknowledged by the initiator. When issuing a new command, this - * number is set to 0. If the function is TASK REASSIGN, which - * establishes a new connection allegiance for a previously issued read - * or bidirectional command, the ExpDataSN will contain an updated data - * acknowledgment reference number or the value 0; the latter indicates - * that the data acknowledgment reference number is unchanged. The - * initiator MUST discard any data PDUs from the previous execution that - * it did not acknowledge, and the target MUST transmit all Data-In PDUs - * (if any) starting with the data acknowledgment reference number. The - * number of retransmitted PDUs may or may not be the same as the - * original transmission, depending on if there was a change in - * MaxRecvDataSegmentLength in the reassignment. The target MAY also - * send no more Data-In PDUs if all data has been acknowledged. - * The value of ExpDataSN MUST be 0 or higher than the DataSN of the - * last acknowledged Data-In PDU, but not larger than DataSN + 1 of the - * last Data-IN PDU sent by the target. Any other value MUST be ignored - * by the target. - * For other functions, this field is reserved - */ - uint32_t exp_data_sn; +/// 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) - /// Reserved for future usage, always MUST be 0. - uint64_t reserved2; +/// 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)) - /// Optional header digest. - iscsi_header_digest hdr_digest; -} iscsi_task_mgmt_func_req_packet; +/// 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)) -/// Task management function response: Function complete. -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_COMPLETE 0x00 -/// Task management function response: Task does not exist. -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_NO_EXIST 0x01 +/** + * @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; -/// Task management function response: LUN does not exist. -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_LUN_NO_EXIST 0x02 + /// Execution flags. + int8_t exec_flags; -/// Task management function response: Task still allegiant. -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_ALLEGIANT 0x03 + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved; -/// Task management function response: Task allegiance reassignment not supported. -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_UNSUPPORTED_ALLEGIANCE 0x04 + /// Power condition modifier. + uint8_t power_cond_mod; -/// Task management function response: Task management function not supported. -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_UNSUPPORTED_MGMT 0x05 + /// Flags. + int8_t flags; -/// Task management function response: Function authorization failed. -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_AUTH_FAILED 0x06 + /// Control. + uint8_t control; +} iscsi_scsi_cdb_start_stop_unit; -/// Task management function response: Function rejected. -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_REJECTED 0xFF + +/// 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 Task Management Function Response packet data. + * @brief iSCSI SCSI CDB packet data structure for SCSI PERSISTENT RESERVE OUT command. * - * For the functions ABORT TASK, ABORT TASK SET, CLEAR ACA, CLEAR TASK - * SET, LOGICAL UNIT RESET, TARGET COLD RESET, TARGET WARM RESET, and - * TASK REASSIGN, the target performs the requested task management - * function and sends a task management response back to the initiator. - * For TASK REASSIGN, the new connection allegiance MUST ONLY become - * effective at the target after the target issues the task management - * response. + * There are 10 bytes in the CDB field for this command. */ -typedef struct __attribute__((packed)) iscsi_task_mgmt_func_response_packet { - /// Always 0x22 according to specification. - uint8_t opcode; +typedef struct __attribute__((packed)) iscsi_scsi_cdb_pr_reserve_out { + /// SCSI opcode. + iscsi_scsi_cdb cdb; - /// Reserved for future usage (must be always 0x80 for now). - int8_t flags; + /// Service action. + uint8_t action; - /** - * @brief Function response. - * - * For the TARGET COLD RESET and TARGET WARM RESET functions, the target - * cancels all pending operations across all LUs known to the issuing - * initiator. For the TARGET COLD RESET function, the target MUST then - * close all of its TCP connections to all initiators (terminates all - * sessions).\n - * The mapping of the response code into a SCSI service response code - * value, if needed, is outside the scope of this document. However, in - * symbolic terms, Response values 0 and 1 map to the SCSI service - * response of FUNCTION COMPLETE. Response value 2 maps to the SCSI - * service response of INCORRECT LOGICAL UNIT NUMBER. All other - * Response values map to the SCSI service response of FUNCTION - * REJECTED. If a Task Management Function Response PDU does not arrive - * before the session is terminated, the SCSI service response is - * SERVICE DELIVERY OR TARGET FAILURE.\n - * The response to ABORT TASK SET and CLEAR TASK SET MUST only be issued - * by the target after all of the commands affected have been received - * by the target, the corresponding task management functions have been - * executed by the SCSI target, and the delivery of all responses - * delivered until the task management function completion has been - * confirmed (acknowledged through the ExpStatSN) by the initiator on - * all connections of this session.\n - * For the ABORT TASK function,\n - * -# if the Referenced Task Tag identifies a valid task leading to a - * successful termination, then targets must return the "Function - * complete" response. - * -# if the Referenced Task Tag does not identify an existing task - * but the CmdSN indicated by the RefCmdSN field in the Task - * Management Function Request is within the valid CmdSN window - * and less than the CmdSN of the Task Management Function Request - * itself, then targets must consider the CmdSN as received and - * return the "Function complete" response. - * -# if the Referenced Task Tag does not identify an existing task - * and the CmdSN indicated by the RefCmdSN field in the Task - * Management Function Request is outside the valid CmdSN window, - * then targets must return the "Task does not exist" response - */ - uint8_t response; + /// Scope and reservation type. + uint8_t scope_type; - /// Reserved for future usage, always MUST be 0. - uint8_t reserved; + /// Reserved for future usage (always MUST be 0 for now). + uint16_t reserved; - /// TotalAHSLength (MUST be 0 for this PDU). - uint8_t total_ahs_len; + /// Parameter list length in bytes. + uint32_t param_list_len; - /// DataSegmentLength (MUST be 0 for this PDU). - uint8_t ds_len[3]; + /// Control. + uint8_t control; +} iscsi_scsi_cdb_pr_reserve_out; - /// Reserved for future usage, always MUST be 0. - uint64_t reserved2; - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; +/// 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 - /// Reserved for future usage, always MUST be 0. - uint32_t reserved3; +/// 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 - /// StatSN. - uint32_t stat_sn; +/// 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 - /// ExpCmdSN. - uint32_t exp_cmd_sn; +/// 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 - /// MaxCmdSN. - uint32_t max_cmd_sn; +/// 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 - /// Reserved for future usage, always MUST be 0. - uint32_t reserved4; +/// 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) - /// Reserved for future usage, always MUST be 0. - uint64_t reserved5; +/// 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)) - /// Optional header digest. - iscsi_header_digest hdr_digest; -} iscsi_task_mgmt_func_response_packet; +/// 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)) -/// SCSI data out / in flags: Immediately process transfer. -#define ISCSI_SCSI_DATA_OUT_DATA_IN_FLAGS_IMMEDIATE (1 << 7) /** - * @brief iSCSI SCSI Data Out request packet data. + * @brief iSCSI SCSI CDB packet data structure for SCSI PERSISTENT RESERVE IN command. * - * THis structure is used by iSCSI for SCSI data output - * requests, i.e. write operations. + * There are 10 bytes in the CDB field for this command. */ -typedef struct __attribute__((packed)) iscsi_scsi_data_out_req_packet { - /// Always 2 according to iSCSI specification. - uint8_t opcode; +typedef struct __attribute__((packed)) iscsi_scsi_cdb_pr_reserve_in { + /// SCSI opcode. + iscsi_scsi_cdb cdb; - /// Flags. - int8_t flags; - - /// Reserved for future usage, always MUST be 0. - uint16_t reserved; + /// Service action. + uint8_t action; - /// TotalAHSLength. - uint8_t total_ahs_len; + /// Reserved for future usage (always MUST be 0 for now). + uint32_t reserved; - /** - * @brief DataSegmentLength. - * - * This is the data payload length of a SCSI Data-In or SCSI Data-Out - * PDU. The sending of 0-length data segments should be avoided, but - * initiators and targets MUST be able to properly receive 0-length data - * segments.\n - * The data segments of Data-In and Data-Out PDUs SHOULD be filled to - * the integer number of 4-byte words (real payload), unless the F bit - * is set to 1. - */ - uint8_t ds_len[3]; + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved2; - /** - * @brief Logical Unit Number (LUN) or Reserved. - * - * If the Target Transfer Tag is provided, then the LUN field MUST hold a - * valid value and be consistent with whatever was specified with the command; - * otherwise, the LUN field is reserved. - */ - uint64_t lun; + /// Parameter list length in bytes. + uint16_t param_list_len; - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; + /// Control. + uint8_t control; +} iscsi_scsi_cdb_pr_reserve_in; - /** - * @brief Target Transfer Tag or 0xFFFFFFFF. - * - * On outgoing data, the Target Transfer Tag is provided to the target - * if the transfer is honoring an R2T. In this case, the Target - * Transfer Tag field is a replica of the Target Transfer Tag provided - * with the R2T.\n - * The Target Transfer Tag values are not specified by this protocol, - * except that the value 0xFFFFFFFF is reserved and means that the - * Target Transfer Tag is not supplied. - */ - uint32_t target_xfer_tag; - /// Reserved for future usage, always MUST be 0. - uint32_t reserved2; +/** + * @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; - /// ExpStatSN. - uint32_t exp_stat_sn; + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved_obselete; - /// Reserved for future usage, always MUST be 0. - uint32_t reserved3; + /// Obselete byte. + uint8_t obselete; - /** - * @brief DataSN. - * - * For output (write) data PDUs, the DataSN is the Data-Out PDU number - * within the current output sequence. Either the current output - * sequence is identified by the Initiator Task Tag (for unsolicited - * data) or it is a data sequence generated for one R2T (for data - * solicited through R2T). - */ - uint32_t data_sn; + /// Obselete word. + uint16_t obselete2; - /** - * @brief Buffer Offset. - * - * The Buffer Offset field contains the offset of this PDU payload data - * within the complete data transfer. The sum of the buffer offset and - * length should not exceed the expected transfer length for the - * command.\n - * The order of data PDUs within a sequence is determined by - * DataPDUInOrder. When set to Yes, it means that PDUs have to be in - * increasing buffer offset order and overlays are forbidden.\n - * The ordering between sequences is determined by DataSequenceInOrder. - * When set to Yes, it means that sequences have to be in increasing - * buffer offset order and overlays are forbidden. - */ - uint32_t buf_offset; + /// Control. + uint8_t control; +} iscsi_scsi_cdb_pr_reserve_6; - /// Reserved for future usage, always MUST be 0. - uint32_t reserved4; - /// Optional header digest. - iscsi_header_digest hdr_digest; +/// 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) - /// Data segment. - iscsi_ds_cmd_data ds_cmd_data; +/// 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) - /// Optional data digest. - iscsi_data_digest data_digest; -} iscsi_scsi_data_out_req_packet; /** - * @brief SCSI Data In reponse flags: Status. + * @brief iSCSI SCSI CDB packet data structure for SCSI RESERVE(10) command. * - * (S) set to indicate that the Command Status field - * contains status. If this bit is set to 1, the - * F bit MUST also be set to 1. + * There are 10 bytes in the CDB field for this command. */ -#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS (1 << 0) +typedef struct __attribute__((packed)) iscsi_scsi_cdb_pr_reserve_10 { + /// SCSI opcode. + iscsi_scsi_cdb cdb; -/** - * @brief SCSI Data In reponse flags: Residual Underflow. - * - * (U) set for Residual Underflow. In this case, the Residual - * Count indicates the number of bytes that were not - * transferred out of the number of bytes that were expected - * to be transferred. For a bidirectional operation, the - * Residual Count contains the residual for the write - * operation. - */ -#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_UNDERFLOW (1 << 1) + /// Flags. + int8_t flags; -/** - * @brief SCSI Data In reponse flags: Residual Overflow. - * - * (O) set for Residual Overflow. In this case, the Residual - * Count indicates the number of bytes that were not - * transferred because the initiator's Expected Data - * Transfer Length was not sufficient. For a bidirectional - * operation, the Residual Count contains the residual for - * the write operation. - */ -#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_OVERFLOW (1 << 2) + /// 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 SCSI Data In reponse flags: ACK. - * - * (A) for sessions with ErrorRecoveryLevel=1 or higher, the target sets - * this bit to 1 to indicate that it requests a positive acknowledgment - * from the initiator for the data received. The target should use the - * A bit moderately; it MAY only set the A bit to 1 once every - * MaxBurstLength bytes, or on the last Data-In PDU that concludes the - * entire requested read data transfer for the task from the target's - * perspective, and it MUST NOT do so more frequently. The target MUST - * NOT set to 1 the A bit for sessions with ErrorRecoveryLevel=0. The - * initiator MUST ignore the A bit set to 1 for sessions with - * ErrorRecoveryLevel=0.\n - * On receiving a Data-In PDU with the A bit set to 1 on a session with - * ErrorRecoveryLevel greater than 0, if there are no holes in the read - * data until that Data-In PDU, the initiator MUST issue a SNACK of type - * DataACK, except when it is able to acknowledge the status for the - * task immediately via the ExpStatSN on other outbound PDUs if the - * status for the task is also received. In the latter case - * (acknowledgment through the ExpStatSN), sending a SNACK of type - * DataACK in response to the A bit is OPTIONAL, but if it is done, it - * must not be sent after the status acknowledgment through the - * ExpStatSN. If the initiator has detected holes in the read data - * prior to that Data-In PDU, it MUST postpone issuing the SNACK of type - * DataACK until the holes are filled. An initiator also MUST NOT - * acknowledge the status for the task before those holes are filled. A - * status acknowledgment for a task that generated the Data-In PDUs is - * considered by the target as an implicit acknowledgment of the Data-In - * PDUs if such an acknowledgment was requested by the target. - */ -#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_ACK (1 << 6) /** - * @brief SCSI Data In reponse flags: Final. + * @brief iSCSI SCSI CDB packet data structure for SCSI RELEASE(6) command. * - * (F) for outgoing data, this bit is 1 for the last PDU of unsolicited - * data or the last PDU of a sequence that answers an R2T. - * For incoming data, this bit is 1 for the last input (read) data PDU - * of a sequence. Input can be split into several sequences, each - * having its own F bit. Splitting the data stream into sequences does - * not affect DataSN counting on Data-In PDUs. It MAY be used as a - * "change direction" indication for bidirectional operations that need - * such a change.\n - * DataSegmentLength MUST NOT exceed MaxRecvDataSegmentLength for the - * direction it is sent, and the total of all the DataSegmentLength of - * all PDUs in a sequence MUST NOT exceed MaxBurstLength (or - * FirstBurstLength for unsolicited data). However, the number of - * individual PDUs in a sequence (or in total) may be higher than the - * ratio of MaxBurstLength (or FirstBurstLength) to - * MaxRecvDataSegmentLength (as PDUs may be limited in length by the - * capabilities of the sender). Using a DataSegmentLength of 0 may - * increase beyond what is reasonable for the number of PDUs and should - * therefore be avoided.\n - * For bidirectional operations, the F bit is 1 for both the end of the - * input sequences and the end of the output sequences + * There are 6 bytes in the CDB field for this command. */ -#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_FINAL (1 << 7) +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 Data In response packet data. + * @brief iSCSI SCSI CDB packet data structure for SCSI RELEASE(10) command. * - * THis structure is used by iSCSI for SCSI data input - * responses, i.e. read operations. + * There are 10 bytes in the CDB field for this command. */ -typedef struct __attribute__((packed)) iscsi_scsi_data_in_response_packet { - /// Always 0x25 according to iSCSI specification. - uint8_t opcode; +typedef struct __attribute__((packed)) iscsi_scsi_cdb_pr_release_10 { + /// SCSI opcode. + iscsi_scsi_cdb cdb; - /// Incoming data flags. The fields StatSN, Status, and Residual Count only have meaningful content if the S bit is set to 1. + /// Flags. int8_t flags; - /// Rserved for future usage, always MUST be 0. - uint8_t reserved; + /// Obselete. + uint8_t obselete; - /** - * @brief Status or Reserved. - * - * Status can accompany the last Data-In PDU if the command did not end - * with an exception (i.e., the status is "good status" - GOOD, - * CONDITION MET, or INTERMEDIATE-CONDITION MET). The presence of - * status (and of a residual count) is signaled via the S flag bit. - * Although targets MAY choose to send even non-exception status in - * separate responses, initiators MUST support non-exception status in - * Data-In PDUs. - */ - uint8_t status; + /// Third-party device identifier. + uint8_t third_party_dev_id; - /// TotalAHSLength. - uint8_t total_ahs_len; + /// Reserved for future usage (always MUST be 0 for now). + uint16_t reserved; - /** - * @brief DataSegmentLength. - * - * This is the data payload length of a SCSI Data-In or SCSI Data-Out - * PDU. The sending of 0-length data segments should be avoided, but - * initiators and targets MUST be able to properly receive 0-length data - * segments.\n - * The data segments of Data-In and Data-Out PDUs SHOULD be filled to - * the integer number of 4-byte words (real payload), unless the F bit - * is set to 1. - */ - uint8_t ds_len[3]; + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved2; - /** - * @brief Logical Unit Number (LUN) or Reserved. - * - * If the Target Transfer Tag is provided, then the LUN field MUST hold a - * valid value and be consistent with whatever was specified with the command; - * otherwise, the LUN field is reserved. - */ - uint64_t lun; + /// Parameter list length in bytes. + uint16_t param_list_len; - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; + /// Control. + uint8_t control; +} iscsi_scsi_cdb_pr_release_10; - /** - * @brief Target Transfer Tag or 0xFFFFFFFF. - * - * On incoming data, the Target Transfer Tag and LUN MUST be provided by - * the target if the A bit is set to 1; otherwise, they are reserved. - * The Target Transfer Tag and LUN are copied by the initiator into the - * SNACK of type DataACK that it issues as a result of receiving a SCSI - * Data-In PDU with the A bit set to 1.\n - * The Target Transfer Tag values are not specified by this protocol, - * except that the value 0xFFFFFFFF is reserved and means that the - * Target Transfer Tag is not supplied. - */ - uint32_t target_xfer_tag; - /// StatSN. - uint32_t stat_sn; +/** + * @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 + * the failed command. + * + * For some iSCSI responses, the response data segment MAY contain some + * 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_scsi_ds_cmd_data { + /// SenseLength: This field indicates the length of Sense Data. + uint16_t len; - /// ExpCmdSN. + /// The Sense Data contains detailed information about a CHECK CONDITION. SPC3 specifies the format and content of the Sense Data. + uint8_t sense_data[0]; - uint32_t exp_cmd_sn; + /// Response Data. + uint8_t res_data[0]; +} iscsi_scsi_ds_cmd_data; - /// MaxCmdSN. - uint32_t max_cmd_sn; - /** - * @brief DataSN. - * - * For input (read) or bidirectional Data-In PDUs, the DataSN is the - * input PDU number within the data transfer for the command identified - * by the Initiator Task Tag.\n - * R2T and Data-In PDUs, in the context of bidirectional commands, share - * the numbering sequence. - */ - uint32_t data_sn; +/// iSCSI SCSI Basic Inquiry Data peripheral type: Direct access device. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_DIRECT 0x00 - /** - * @brief Buffer Offset. - * - * The Buffer Offset field contains the offset of this PDU payload data - * within the complete data transfer. The sum of the buffer offset and - * length should not exceed the expected transfer length for the - * command.\n - * The order of data PDUs within a sequence is determined by - * DataPDUInOrder. When set to Yes, it means that PDUs have to be in - * increasing buffer offset order and overlays are forbidden.\n - * The ordering between sequences is determined by DataSequenceInOrder. - * When set to Yes, it means that sequences have to be in increasing - * buffer offset order and overlays are forbidden. - */ - uint32_t buf_offset; +/// iSCSI SCSI Basic Inquiry Data peripheral type: Sequential access device. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_SEQ 0x01 - /// Residual Count or Reserved. - uint32_t res_cnt; +/// iSCSI SCSI Basic Inquiry Data peripheral type: Printer device. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_PRINTER 0x02 - /// Optional header digest. - iscsi_header_digest hdr_digest; +/// iSCSI SCSI Basic Inquiry Data peripheral type: Processor device. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_PROCESSOR 0x03 - /// Data segment. - iscsi_ds_cmd_data ds_cmd_data; +/// iSCSI SCSI Basic Inquiry Data peripheral type: Write once device. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_WORM 0x04 - /// Optional data digest. - iscsi_data_digest data_digest; -} iscsi_scsi_data_in_response_packet; +/// 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 -/** - * @brief iSCSI Ready To Transfer packet data. - * - * When an initiator has submitted a SCSI command with data that passes - * from the initiator to the target (write), the target may specify - * which blocks of data it is ready to receive. The target may request - * that the data blocks be delivered in whichever order is convenient - * for the target at that particular instant. This information is - * passed from the target to the initiator in the Ready To Transfer - * (R2T) PDU. - * - * In order to allow write operations without an explicit initial R2T, - * the initiator and target MUST have negotiated the key InitialR2T to - * No during login. - * - * An R2T MAY be answered with one or more SCSI Data-Out PDUs with a - * matching Target Transfer Tag. If an R2T is answered with a single - * Data-Out PDU, the buffer offset in the data PDU MUST be the same as - * the one specified by the R2T, and the data length of the data PDU - * MUST be the same as the Desired Data Transfer Length specified in the - * R2T. If the R2T is answered with a sequence of data PDUs, the buffer - * offset and length MUST be within the range of those specified by the - * R2T, and the last PDU MUST have the F bit set to 1. If the last PDU - * (marked with the F bit) is received before the Desired Data Transfer - * Length is transferred, a target MAY choose to reject that PDU with - * the "Protocol Error" reason code. DataPDUInOrder governs the - * Data-Out PDU ordering. If DataPDUInOrder is set to Yes, the buffer - * offsets and lengths for consecutive PDUs MUST form a continuous - * non-overlapping range, and the PDUs MUST be sent in increasing offset - * order. - * - * The target may send several R2T PDUs. It therefore can have a number - * of pending data transfers. The number of outstanding R2T PDUs is - * limited by the value of the negotiated key MaxOutstandingR2T. Within - * a task, outstanding R2Ts MUST be fulfilled by the initiator in the - * order in which they were received. - * - * R2T PDUs MAY also be used to recover Data-Out PDUs. Such an R2T - * (Recovery-R2T) is generated by a target upon detecting the loss of - * one or more Data-Out PDUs due to: - * - * - Digest error - * - * - Sequence error - * - * - Sequence reception timeout - * - * A Recovery-R2T carries the next unused R2TSN but requests part of or - * the entire data burst that an earlier R2T (with a lower R2TSN) had - * already requested. - * - * DataSequenceInOrder governs the buffer offset ordering in consecutive - * R2Ts. If DataSequenceInOrder is Yes, then consecutive R2Ts MUST - * refer to continuous non-overlapping ranges, except for Recovery-R2Ts. - */ -typedef struct __attribute__((packed)) iscsi_r2t_packet { - /// Always 0x31 according to iSCSI specification. - uint8_t opcode; +/// iSCSI SCSI Basic Inquiry Data peripheral type: Scanner device. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_SCANNER 0x06 - /// Reserved for future usage (must be always 0x80 for now). - int8_t flags; +/// iSCSI SCSI Basic Inquiry Data peripheral type: Optical memory device. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_OPTICAL 0x07 - /// Reserved for future usage, always MUST be 0 for now. - uint16_t reserved; +/// iSCSI SCSI Basic Inquiry Data peripheral type: Medium changer device. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_CHANGER 0x08 - /// TotalAHSLength, MUST be 0 for this PDU. - uint8_t total_ahs_len; +/// iSCSI SCSI Basic Inquiry Data peripheral type: Communications device. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_COMM 0x09 - /// DataSegmentLength, MUST be 0 0 for this PDU. - uint8_t ds_len[3]; +/// iSCSI SCSI Basic Inquiry Data peripheral type: Unknown or no device. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_UNKNOWN 0x1F - /// Logical Unit Number (LUN) or Reserved. - uint64_t lun; +/// iSCSI SCSI Basic Inquiry Data peripheral type: First bit of the five bits. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_FIRST_BIT 0 - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; +/// 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) - /// Target Transfer Tag (TTT). - uint32_t target_xfer_tag; +/// 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)) - /// The StatSN field will contain the next StatSN. The StatSN for this connection is not advanced after this PDU is sent. - uint32_t stat_sn; +/// 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)) - /// ExpCmdSN. - uint32_t exp_cmd_sn; +/// 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)) - /// MaxCmdSN. - uint32_t max_cmd_sn; +/// 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 - /// DataSN. - uint32_t data_sn; +/// 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 - /// Ready To Transfer Sequence Number (R2TSN) is the R2T PDU input PDU number within the command identified by the Initiator Task Tag. For bidirectional commands, R2T and Data-In PDUs share the input PDU numbering sequence. - uint32_t r2t_sn; +/// 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 - /** - * @brief Buffer Offset. - * - * The target therefore also specifies a buffer offset that indicates - * the point at which the data transfer should begin, relative to the - * beginning of the total data transfer. - */ - uint32_t buf_offset; +/// iSCSI SCSI Basic Inquiry Data peripheral identifier: Vendor specific. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_ID_VENDOR_UNIQ 0x4 - /** - * @brief Desired Data Transfer Length. - * - * The target specifies how many bytes it wants the initiator to send - * because of this R2T PDU. The target may request the data from the - * initiator in several chunks, not necessarily in the original order of - * the data. The Desired Data Transfer Length MUST NOT be 0 and MUST NOT - * exceed MaxBurstLength. - */ - uint32_t des_data_xfer_len; +/// iSCSI SCSI Basic Inquiry Data peripheral identifier: First bit of the three bits. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_ID_FIRST_BIT 5 - /// Optional header digest. - iscsi_header_digest hdr_digest; -} iscsi_r2t_packet; +/// 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)) -/** - * @brief SCSI Asynchronous Message Event: SCSI Async Event. - * - * A SCSI asynchronous event is reported in the sense data. - * Sense Data that accompanies the report, in the data - * segment, identifies the condition. The sending of a - * SCSI event ("asynchronous event reporting" in SCSI - * terminology) is dependent on the target support for SCSI - * asynchronous event reporting as indicated in the - * standard INQUIRY data. Its use may be enabled by - * parameters in the SCSI Control mode page. - */ -#define ISCSI_ASYNC_MSG_EVENT_SCSI_ASYNC_EVENT 0x00 +/// 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)) -/** - * @brief SCSI Asynchronous Message Event: Logout Request. - * - * The target requests Logout. This Async Message MUST - * be sent on the same connection as the one requesting - * to be logged out. The initiator MUST honor this request - * by issuing a Logout as early as possible but no later - * than Parameter3 seconds. The initiator MUST send a Logout - * with a reason code of "close the connection" OR "close the - * session" to close all the connections. Once this message is - * received, the initiator SHOULD NOT issue new iSCSI commands on - * the connection to be logged out. The target MAY reject any - * new I/O requests that it receives after this message with the - * reason code "Waiting for Logout". If the initiator does not - * log out in Parameter3 seconds, the target should send an Async - * PDU with iSCSI event code "Dropped the connection" if possible - * or simply terminate the transport connection. Parameter1 and - * Parameter2 are reserved. - */ -#define ISCSI_ASYNC_MSG_EVENT_LOGOUT_REQUEST 0x01 +/// 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)) -/** - * @brief SCSI Asynchronous Message Event: Connection Drop Notification. - * - * The target indicates that it will drop the connection. - * The Parameter1 field indicates the CID of the connection that - * is going to be dropped.\n - * The Parameter2 field (Time2Wait) indicates, in seconds, the - * minimum time to wait before attempting to reconnect or - * reassign.\n - * The Parameter3 field (Time2Retain) indicates the maximum time - * allowed to reassign commands after the initial wait (in - * Parameter2).\n - * If the initiator does not attempt to reconnect and/or reassign - * the outstanding commands within the time specified by - * Parameter3, or if Parameter3 is 0, the target will terminate - * all outstanding commands on this connection. In this case, no - * other responses should be expected from the target for the - * outstanding commands on this connection.\n - * A value of 0 for Parameter2 indicates that reconnect can be - * attempted immediately. - */ -#define ISCSI_ASYNC_MSG_EVENT_CONNECT_DROP_NOTIFY 0x02 -/** - * @brief SCSI Asynchronous Message Event: Session Drop Notification. - * - * The target indicates that it will drop all the connections - * of this session.\n - * The Parameter1 field is reserved.\n - * The Parameter2 field (Time2Wait) indicates, in seconds, the - * minimum time to wait before attempting to reconnect.\n - * The Parameter3 field (Time2Retain) indicates the maximum time - * allowed to reassign commands after the initial wait (in - * Parameter2).\n - * If the initiator does not attempt to reconnect and/or reassign - * the outstanding commands within the time specified by - * Parameter3, or if Parameter3 is 0, the session is terminated.\n - * In this case, the target will terminate all outstanding - * commands in this session; no other responses should be - * expected from the target for the outstanding commands in this - * session. A value of 0 for Parameter2 indicates that reconnect - * can be attempted immediately. - */ -#define ISCSI_ASYNC_MSG_EVENT_SESSION_DROP_NOTIFY 0x03 +/// 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 -/** - * @brief SCSI Asynchronous Message Event: Negotiation Request. - * - * The target requests parameter negotiation on this connection. - * The initiator MUST honor this request by issuing a Text - * Request (that can be empty) on the same connection as early - * as possible, but no later than Parameter3 seconds, unless a - * Text Request is already pending on the connection, or by - * issuing a Logout Request. If the initiator does not issue a - * Text Request, the target may reissue the Asynchronous Message - * requesting parameter negotiation. - */ -#define ISCSI_ASYNC_MSG_EVENT_NEGOTIATION_REQUEST 0x04 +/// 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) -/** - * @brief SCSI Asynchronous Message Event: Task Termination. - * - * All active tasks for a LU with a matching LUN field in the - * Async Message PDU are being terminated. The receiving - * initiator iSCSI layer MUST respond to this message by - * taking the following steps, in order: - * - Stop Data-Out transfers on that connection for all active - * TTTs for the affected LUN quoted in the Async Message PDU. - * - Acknowledge the StatSN of the Async Message PDU via a - * NOP-Out PDU with ITT=0xFFFFFFFF (i.e., non-ping flavor), - * while copying the LUN field from the Async Message to - * NOP-Out. - * This value of AsyncEvent, however, MUST NOT be used on an - * iSCSI session unless the new TaskReporting text key was - * negotiated to FastAbort on the session. - */ -#define ISCSI_ASYNC_MSG_EVENT_TASK_TERMINATION 0x05 +/// 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)) -/// SCSI Asynchronous Message Event: First vendor-specific iSCSI event. The AsyncVCode details the vendor code, and data MAY accompany the report. -#define ISCSI_ASYNC_MSG_EVENT_VENDOR_FIRST 0xF8 +/// 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)) -/// SCSI Asynchronous Message Event: Last vendor-specific iSCSI event. The AsyncVCode details the vendor code, and data MAY accompany the report. -#define ISCSI_ASYNC_MSG_EVENT_VENDOR_LAST 0xFF +/// 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)) -/** - * @brief iSCSI Asynchronous Message packet data. - * - * An Asynchronous Message may be sent from the target to the initiator - * without corresponding to a particular command. The target specifies - * the reason for the event and sense data.\n - * Some Asynchronous Messages are strictly related to iSCSI, while - * others are related to SCSI - */ -typedef struct __attribute__((packed)) iscsi_async_msg_packet { - /// Always 0x32 according to iSCSI specification. - uint8_t opcode; +/// iSCSI SCSI Basic Inquiry Data peripheral type modifier: Removable media. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_MOD_FLAGS_REMOVABLE_MEDIA (1 << 7) - /// Reserved for future usage (must be always 0x80 for now). - int8_t flags; - /// Reserved for future usage, always MUST be 0. - uint16_t reserved; +/// iSCSI SCSI Basic Inquiry Data ANSI version: None. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_NONE 0x0 - /// TotalAHSLength, MUST be 0 for this PDU. - uint8_t total_ahs_len; +/// iSCSI SCSI Basic Inquiry Data ANSI version: SPC. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_SPC 0x3 - /// DataSegmentLength, MUST be 0 0 for this PDU. - uint8_t ds_len[3]; +/// iSCSI SCSI Basic Inquiry Data ANSI version: SPC2. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_SPC2 0x4 - /// The LUN field MUST be valid if AsyncEvent is 0. Otherwise, this field is reserved. - uint64_t lun; +/// iSCSI SCSI Basic Inquiry Data ANSI version: SPC3. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_SPC3 0x5 - /// Tag (always 0xFFFFFFFF for now). - uint32_t tag; +/// iSCSI SCSI Basic Inquiry Data ANSI version: SPC4. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_SPC4 0x6 - /// Reserved for future usage, always MUST be 0. - uint32_t reserved2; +/// iSCSI SCSI Basic Inquiry Data ANSI version: SPC5. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_SPC5 0x7 - /** - * @brief StatSN. - * - * The StatSN counts this PDU as an acknowledgeable event (the StatSN is - * advanced), which allows for initiator and target state synchronization. - */ - uint32_t stat_sn; +/// iSCSI SCSI Basic Inquiry Data ANSI version: First bit of the three bits. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_FIRST_BIT 0 - /// ExpCmdSN. - uint32_t exp_cmd_sn; +/// 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) - /// MaxCmdSN. - uint32_t max_cmd_sn; +/// 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)) - /// AsyncEvent. - uint8_t async_event; +/// 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)) - /// AsyncVCode is a vendor-specific detail code that is only valid if the AsyncEvent field indicates a vendor-specific event. Otherwise, it is reserved. - uint8_t async_vcode; +/// 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)) - /// Parameter1 or Reserved. - uint16_t param_1; +/// iSCSI SCSI Basic Inquiry Data ECMA version: First bit of the three bits. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ECMA_FIRST_BIT 3 - /// Parameter2 or Reserved. - uint16_t param_2; +/// 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) - /// Parameter3 or Reserved. - uint16_t param_3; +/// 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)) - /// Reserved for future usage, always MUST be 0. - uint32_t reserved3; +/// 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)) - /// Optional header digest. - iscsi_header_digest hdr_digest; +/// 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)) - /// Data segment. - iscsi_ds_cmd_data ds_cmd_data; +/// iSCSI SCSI Basic Inquiry Data ISO version: First bit of the two bits. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ISO_FIRST_BIT 6 - /// Optional data digest. - iscsi_data_digest data_digest; -} iscsi_async_msg_packet; +/// 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)) -/** - * @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; +/// 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)) - /// Sense Data. - uint16_t sense_data[0]; +/// 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 Event Data. - uint16_t event_data[0]; -} iscsi_sense_event_data_packet; + +/// 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 Text Request flags: Continue. + * @brief iSCSI SCSI basic inquiry data packet. * - * (C) When set to 1, this bit indicates that the text (set of key=value - * pairs) in this Text Request is not complete (it will be continued on - * subsequent Text Requests); otherwise, it indicates that this Text - * Request ends a set of key=value pairs. A Text Request with the C bit - * set to 1 MUST have the F bit set to 0. + * This structure is used by the SCSI INQUIRY command + * in order to fill in the result if the EVPD bit is + * cleared. */ -#define ISCSI_TEXT_REQ_FLAGS_CONTINUE (1 << 6) +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 Text Request flags: Final. + * @brief iSCSI SCSI standard inquiry data packet. * - * (F) When set to 1, this bit indicates that this is the last or only Text - * Request in a sequence of Text Requests; otherwise, it indicates that - * more Text Requests will follow. + * This structure is used by the SCSI INQUIRY command + * in order to fill in the result if the EVPD bit is + * cleared. */ -#define ISCSI_TEXT_REQ_FLAGS_FINAL (1 << 7) +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 Text Request packet data. - * - * The Text Request is provided to allow for the exchange of information - * and for future extensions. It permits the initiator to inform a - * target of its capabilities or request some special operations. - * - * An initiator MUST NOT have more than one outstanding Text Request on - * a connection at any given time. + * @brief iSCSI SCSI extended inquiry data packet. * - * On a connection failure, an initiator must either explicitly abort - * any active allegiant text negotiation task or cause such a task to be - * implicitly terminated by the target. + * 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_text_req_packet { - /// Always 0x04 according to iSCSI specification. - uint8_t opcode; +typedef struct __attribute__((packed)) iscsi_scsi_ext_inquiry_data_packet { + /// iSCSI SCSI standard inquiry data packet. + iscsi_scsi_std_inquiry_data_packet std_inquiry; - /// Text request flags. + /// Vendor specific. + uint8_t vendor_spec[20]; + + /// Flags. int8_t flags; - /// Reserved for future usage, always MUST be 0. - uint16_t reserved; + /// Reserved for future usage (always MUST be 0). + uint8_t reserved; - /// TotalAHSLength. - uint8_t total_ahs_len; + /// Version descriptors. + uint16_t version_desc[8]; - /// DataSegmentLength. - uint8_t ds_len[3]; + /// Reserved for future usage (always MUST be 0). + uint64_t reserved2[2]; - /// Logical Unit Number (LUN) or Reserved. - uint64_t lun; + /// Reserved for future usage (always MUST be 0). + uint32_t reserved3; - /** - * @brief Initiator Task Tag (ITT). - * - * This is the initiator-assigned identifier for this Text Request. If - * the command is sent as part of a sequence of Text Requests and - * responses, the Initiator Task Tag MUST be the same for all the - * requests within the sequence (similar to linked SCSI commands). The - * I bit for all requests in a sequence also MUST be the same. - */ - uint32_t init_task_tag; + /// Reserved for future usage (always MUST be 0). + uint16_t reserved4; +} iscsi_scsi_ext_inquiry_data_packet; - /** - * @brief Target Transfer Tag (TTT). - * - * When the Target Transfer Tag is set to the reserved value 0xFFFFFFFF, - * it tells the target that this is a new request, and the target resets - * any internal state associated with the Initiator Task Tag (resets the - * current negotiation state).\n - * The target sets the Target Transfer Tag in a Text Response to a value - * other than the reserved value 0xFFFFFFFF whenever it indicates that - * it has more data to send or more operations to perform that are - * associated with the specified Initiator Task Tag. It MUST do so - * whenever it sets the F bit to 0 in the response. By copying the - * Target Transfer Tag from the response to the next Text Request, the - * initiator tells the target to continue the operation for the specific - * Initiator Task Tag. The initiator MUST ignore the Target Transfer - * Tag in the Text Response when the F bit is set to 1.\n - * This mechanism allows the initiator and target to transfer a large - * amount of textual data over a sequence of text-command/text-response - * exchanges or to perform extended negotiation sequences.\n - * If the Target Transfer Tag is not 0xFFFFFFFF, the LUN field MUST be - * sent by the target in the Text Response.\n - * A target MAY reset its internal negotiation state if an exchange is - * stalled by the initiator for a long time or if it is running out of - * resources.\n - * Long Text Responses are handled as shown in the following example:\n - * @verbatim - * I->T Text SendTargets=All (F = 1, TTT = 0xFFFFFFFF) - * T->I Text (F = 0, TTT = 0x12345678) - * I->T Text (F = 1, TTT = 0x12345678) - * T->I Text (F = 0, TTT = 0x12345678) - * I->T Text (F = 1, TTT = 0x12345678) - * ... - * T->I Text (F = 1, TTT = 0xFFFFFFFF) - * @endverbatim - */ - uint32_t target_xfer_tag; - /// CmdSN. - uint32_t cmd_sn; +/// 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 - /// ExpStatSN. - uint32_t exp_stat_sn; +/// 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 - /// Reserved for future usage, always MUST be 0. - uint64_t reserved2[2]; +/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral type: Printer device. +#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_PRINTER 0x02 - /// Optional header digest. - iscsi_header_digest hdr_digest; +/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral type: Processor device. +#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_PROCESSOR 0x03 - /** - * @brief Data segment. - * - * The data lengths of a Text Request MUST NOT exceed the iSCSI target - * MaxRecvDataSegmentLength (a parameter that is negotiated per - * connection and per direction).\n - * A key=value pair can span Text Request or Text Response boundaries. - * A key=value pair can start in one PDU and continue on the next. In - * other words, the end of a PDU does not necessarily signal the end of - * a key=value pair.\n - * The target responds by sending its response back to the initiator. - * The response text format is similar to the request text format. The - * Text Response MAY refer to key=value pairs presented in an earlier - * Text Request, and the text in the request may refer to earlier - * responses.\n - * 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 Vital Product Data (VPD) Page Inquiry Data peripheral type: Write once device. +#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_WORM 0x04 - /// Optional data digest. - iscsi_data_digest data_digest; -} iscsi_text_req_packet; +/// 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 -/** - * @brief Text Response flags: Continue. - * - * (C) When set to 1, this bit indicates that the text (set of key=value - * pairs) in this Text Response is not complete (it will be continued on - * subsequent Text Responses); otherwise, it indicates that this Text - * Response ends a set of key=value pairs. A Text Response with the - * C bit set to 1 MUST have the F bit set to 0. - */ -#define ISCSI_TEXT_RESPONSE_FLAGS_CONTINUE (1 << 6) +/// 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 Text Response flags: Final. - * - * (F) When set to 1, in response to a Text Request with the Final bit set - * to 1, the F bit indicates that the target has finished the whole - * operation. Otherwise, if set to 0 in response to a Text Request with - * the Final Bit set to 1, it indicates that the target has more work to - * do (invites a follow-on Text Request). A Text Response with the - * F bit set to 1 in response to a Text Request with the F bit set to 0 - * is a protocol error.\n - * A Text Response with the F bit set to 1 MUST NOT contain key=value - * pairs that may require additional answers from the initiator. - * A Text Response with the F bit set to 1 MUST have a Target Transfer - * Tag field set to the reserved value 0xFFFFFFFF.\n - * A Text Response with the F bit set to 0 MUST have a Target Transfer - * Tag field set to a value other than the reserved value 0xFFFFFFFF. - */ -#define ISCSI_TEXT_RESPONSE_FLAGS_FINAL (1 << 7) /** - * @brief iSCSI Text Response packet data. + * @brief iSCSI SCSI Vital Product Data (VPD) Page Inquiry data packet. * - * The Text Response PDU contains the target's responses to the - * initiator's Text Request. The format of the Text field matches that - * of the Text Request. + * 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_text_response_packet { - /// Always 0x24 according to iSCSI specification. - uint8_t opcode; +typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_inquiry_data_packet { + /// Peripheral device type and qualifier. + uint8_t peripheral_type_id; - /// Text response flags. - int8_t flags; + /// Page code. + uint8_t page_code; - /// Reserved for future usage, always MUST be 0. - uint16_t reserved; + /// Allocation length in bytes. + uint16_t alloc_len; - /// TotalAHSLength. - uint8_t total_ahs_len; + /// Parameters. + uint8_t params[0]; +} iscsi_scsi_vpd_page_inquiry_data_packet; - /// DataSegmentLength. - uint8_t ds_len[3]; - /// Logical Unit Number (LUN) or Reserved. - uint64_t lun; +/// 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 - /// The Initiator Task Tag matches the tag used in the initial Text Request. - uint32_t init_task_tag; +/// 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 - /** - * @brief Target Transfer Tag (TTT). - * - * When a target has more work to do (e.g., cannot transfer all the - * remaining text data in a single Text Response or has to continue the - * negotiation) and has enough resources to proceed, it MUST set the - * Target Transfer Tag to a value other than the reserved value - * 0xFFFFFFFF. Otherwise, the Target Transfer Tag MUST be set to - * 0xFFFFFFFF.\n - * When the Target Transfer Tag is not 0xFFFFFFFF, the LUN field may be - * significant.\n - * The initiator MUST copy the Target Transfer Tag and LUN in its next - * request to indicate that it wants the rest of the data.\n - * When the target receives a Text Request with the Target Transfer Tag - * set to the reserved value 0xFFFFFFFF, it resets its internal - * information (resets state) associated with the given Initiator Task - * Tag (restarts the negotiation).\n - * When a target cannot finish the operation in a single Text Response - * and does not have enough resources to continue, it rejects the Text - * Request with the appropriate Reject code.\n - * A target may reset its internal state associated with an Initiator - * Task Tag (the current negotiation state) as expressed through the - * Target Transfer Tag if the initiator fails to continue the exchange - * for some time. The target may reject subsequent Text Requests with - * the Target Transfer Tag set to the "stale" value. - */ - uint32_t target_xfer_tag; +/// 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) - /// StatSN. The target StatSN variable is advanced by each Text Response sent. - uint32_t stat_sn; +/// 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)) - /// ExpCmdSN. - uint32_t exp_cmd_sn; +/// 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)) - /// MaxCmdSN. - uint32_t max_cmd_sn; +/// 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)) - /// Reserved for future usage, always MUST be 0. - uint64_t reserved2[2]; +/// 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 - /// Optional header digest. - iscsi_header_digest hdr_digest; +/// 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 - /** - * @brief Data segment. - * - * The data lengths of a Text Response MUST NOT exceed the iSCSI - * initiator MaxRecvDataSegmentLength (a parameter that is negotiated - * per connection and per direction).\n - * The text in the Text Response Data is governed by the same rules as - * the text in the Text Request Data.\n - * Although the initiator is the requesting party and controls the - * request-response initiation and termination, the target can offer - * 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 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 - /// Optional data digest. - iscsi_data_digest data_digest; -} iscsi_text_response_packet; +/// 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) -/// 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 SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data packet. * - * A and B: 22-bit OUI - * (the I/G and U/L bits are omitted) - * C and D: 24-bit Qualifier. + * This structure is used by the SCSI INQUIRY command + * in order to fill in the result if the EVPD bit is + * set. */ -#define ISCSI_ISID_TYPE_FORMAT_OUI 0x0 +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 Initiator Session ID (ISID) type: EN: Format (IANA Enterprise Number). + * @brief iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor IEEE NAA Extended Inquiry data packet. * - * A: Reserved - * B and C: EN (IANA Enterprise Number) - * D: Qualifier + * This structure is used by the SCSI INQUIRY command + * in order to fill in the result if the EVPD bit is + * set. */ -#define ISCSI_ISID_TYPE_FORMAT_EN 0x1 +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 Initiator Session ID (ISID) type: Random. + * @brief iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor T10 Vendor ID Inquiry data packet. * - * A: Reserved - * B and C: Random - * D: Qualifier + * This structure is used by the SCSI INQUIRY command + * in order to fill in the result if the EVPD bit is + * set. */ -#define ISCSI_ISID_TYPE_FORMAT_RANDOM 0x2 +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 Initiator Session ID (ISID) packet data. + * @brief iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Relative Target Port Inquiry data packet. * - * This is an initiator-defined component of the session identifier and - * is structured as follows: + * 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. * - * For the T field values 00b and 01b, a combination of A and B (for - * 00b) or B and C (for 01b) identifies the vendor or organization whose - * component (software or hardware) generates this ISID. A vendor or - * organization with one or more OUIs, or one or more Enterprise - * Numbers, MUST use at least one of these numbers and select the - * appropriate value for the T field when its components generate ISIDs. - * An OUI or EN MUST be set in the corresponding fields in network byte - * order (byte big-endian). - * - * If the T field is 10b, B and C are set to a random 24-bit unsigned - * integer value in network byte order (byte big-endian). - * - * The Qualifier field is a 16-bit or 24-bit unsigned integer value that - * provides a range of possible values for the ISID within the selected - * namespace. It may be set to any value within the constraints - * specified in the iSCSI protocol. + * 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. * - * If the ISID is derived from something assigned to a hardware adapter - * or interface by a vendor as a preset default value, it MUST be - * configurable to a value assigned according to the SCSI port behavior - * desired by the system in which it is installed. The resultant ISID - * MUST also be persistent over power cycles, reboot, card swap, etc. + * 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_isid { - /// Meaning depends on T bit, either 22-bit OUI or reserved. - uint8_t a; +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; - /// Meaning depends on T bit, either 22-bit OUI, EN (IANA Enterprise Number) or random. - uint16_t b; + /// Logical unit identifier. + uint16_t id; +} iscsi_scsi_vpd_page_design_desc_logical_unit_group_inquiry_data_packet; - /// Meaning depends on T bit, either 24-bit Qualifier, EN (IANA Enterprise Number) or random. - uint8_t c; - /// Meaning depends on T bit, either 24-bit Qualifier or Qualifier. - uint16_t d; -} iscsi_isid; +/// 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 Key used during SecurityNegotiation stage of Login Phase: Session type. + * @brief iSCSI SCSI Vital Product Data (VPD) Extended Inquiry data packet. * - * @verbatim - * Use: LO, Declarative, Any-Stage - * Senders: Initiator - * Scope: SW - * SessionType= - * Default is Normal. - * @endverbatim - * The initiator indicates the type of session it wants to create. The - * target can either accept it or reject it.\n - * A Discovery session indicates to the target that the only purpose of - * this session is discovery. The only requests a target accepts in - * this type of session are a Text Request with a SendTargets key and a - * Logout Request with reason "close the session".\n - * The Discovery session implies MaxConnections = 1 and overrides both - * the default and an explicit setting. ErrorRecoveryLevel MUST be 0 - * (zero) for Discovery sessions.\n - * Depending on the type of session, a target may decide on resources to - * allocate, the security to enforce, etc., for the session. If the - * SessionType key is thus going to be offered as "Discovery", it SHOULD - * be offered in the initial Login Request by the initiator. + * This structure is used by the SCSI INQUIRY command + * in order to fill in the result if the EVPD bit is + * set. */ -#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_SESSION_TYPE "SessionType" +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 Key used during SecurityNegotiation stage of Login Phase: Initiator name. + * @brief iSCSI SCSI Vital Product Data (VPD) Mode Page Policy Descriptor Inquiry data packet. * - * @verbatim - * Use: IO, Declarative, Any-Stage - * Senders: Initiator - * Scope: SW - * InitiatorName= - * Examples: - * InitiatorName=iqn.1992-04.de.uni-freiburg.bwlehrpool:qcow2.5003 - * InitiatorName=iqn.2001-02.de.uni-freiburg.matrix:basty.eduroam - * InitiatorName=naa.52004567BA64678D - * @endverbatim - * The initiator of the TCP connection MUST provide this key to the - * remote endpoint at the first login of the Login Phase for every - * connection. The InitiatorName key enables the initiator to identify - * itself to the remote endpoint.\n - * The InitiatorName MUST NOT be redeclared within the Login Phase. + * This structure is used by the SCSI INQUIRY command + * in order to fill in the result if the EVPD bit is + * set. */ -#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_INITIATOR_NAME "InitiatorName" +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 Key used during SecurityNegotiation stage of Login Phase: Target name. + * @brief iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data packet. * - * @verbatim - * Use: IO by initiator, FFPO by target - only as response to a - * SendTargets, Declarative, Any-Stage - * Senders: Initiator and target - * Scope: SW - * TargetName= - * Examples: - * TargetName=iqn.1993-11.de.uni-freiburg:diskarrays.sn.5003 - * TargetName=eui.020000023B040506 - * TargetName=naa.62004567BA64678D0123456789ABCDEF - * @endverbatim - * The initiator of the TCP connection MUST provide this key to the - * remote endpoint in the first Login Request if the initiator is not - * establishing a Discovery session. The iSCSI Target Name specifies - * the worldwide unique name of the target.\n - * The TargetName key may also be returned by the SendTargets Text - * Request (which is its only use when issued by a target).\n - * The TargetName MUST NOT be redeclared within the Login Phase. + * This structure is used by the SCSI INQUIRY command + * in order to fill in the result if the EVPD bit is + * set. */ -#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_NAME "TargetName" +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 Key used during SecurityNegotiation stage of Login Phase: Target address. + * @brief iSCSI SCSI Vital Product Data (VPD) SCSI Port Designation Descriptor Inquiry data packet. * - * @verbatim - * Use: ALL, Declarative, Any-Stage - * Senders: Target - * Scope: SW - * TargetAddress=domainname[:port][,portal-group-tag] - * @endverbatim - * The domainname can be specified as either a DNS host name, a dotted- - * decimal IPv4 address, or a bracketed IPv6 address as specified in - * RFC3986.\n - * If the TCP port is not specified, it is assumed to be the IANA- - * assigned default port for iSCSI.\n - * If the TargetAddress is returned as the result of a redirect status - * in a Login Response, the comma and portal-group-tag MUST be omitted. - * If the TargetAddress is returned within a SendTargets response, the - * portal-group-tag MUST be included.\n - * @verbatim - * Examples: - * TargetAddress=10.0.0.1:5003,1 - * TargetAddress=[1080:0:0:0:8:800:200C:417A],65 - * TargetAddress=[1080::8:800:200C:417A]:5003,1 - * TargetAddress=gitlab.uni-freiburg.de,443 - * @endverbatim - * The formats for the port and portal-group-tag are the same as the one - * specified in TargetPortalGroupTag. + * This structure is used by the SCSI INQUIRY command + * in order to fill in the result if the EVPD bit is + * set. */ -#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS "TargetAddress" +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 Key used during SecurityNegotiation stage of Login Phase: Initiator alias. - * - * @verbatim - * Use: ALL, Declarative, Any-Stage - * Senders: Initiator - * Scope: SW - * InitiatorAlias= - * Examples: - * InitiatorAlias=Web Server 5 - * InitiatorAlias=matrix.uni-freiburg.de - * InitiatorAlias=Matrix Server - * @endverbatim - * If an initiator has been configured with a human-readable name or - * description, it SHOULD be communicated to the target during a Login - * Request PDU. If not, the host name can be used instead. This string - * is not used as an identifier, nor is it meant to be used for - * authentication or authorization decisions. It can be displayed by - * 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" /** - * @brief Key used during SecurityNegotiation stage of Login Phase: Target alias. + * @brief iSCSI SCSI command INQUIRY Vital Product Data (VPD) SCSI Port Designation Descriptor entry fill. * - * @verbatim - * Use: ALL, Declarative, Any-Stage - * Senders: Target - * Scope: SW - * TargetAlias= - * Examples: - * TargetAlias=Bob-s Disk - * TargetAlias=Database Server 1 Log Disk - * TargetAlias=Web Server 3 Disk 20 - * @endverbatim - * If a target has been configured with a human-readable name or - * description, this name SHOULD be communicated to the initiator during - * a Login Response PDU if SessionType=Normal. This string is not used - * as an identifier, nor is it meant to be used for authentication or - * authorization decisions. It can be displayed by the initiator's user - * interface in a list of targets to which it is connected. + * 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. */ -#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS "TargetAlias" +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 Key used during SecurityNegotiation stage of Login Phase: Target portal group tag. + * @brief iSCSI SCSI Vital Product Data (VPD) Page Block Limits Inquiry data packet. * - * @verbatim - * Use: IO by target, Declarative, Any-Stage - * Senders: Target - * Scope: SW - * TargetPortalGroupTag=<16-bit-binary-value> - * Example: - * TargetPortalGroupTag=1 - * @endverbatim - * The TargetPortalGroupTag key is a 16-bit binary-value that uniquely - * identifies a portal group within an iSCSI target node. This key - * carries the value of the tag of the portal group that is servicing - * the Login Request. The iSCSI target returns this key to the - * initiator in the Login Response PDU to the first Login Request PDU - * that has the C bit set to 0 when TargetName is given by the - * initiator.\n - * SAM2 notes in its informative text that the TPGT value should be - * non-zero; note that this is incorrect. A zero value is allowed as a - * legal value for the TPGT. This discrepancy currently stands - * corrected in SAM4. + * This structure is used by the SCSI INQUIRY command + * in order to fill in the result if the EVPD bit is + * set. */ -#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG "TargetPortalGroupTag" +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 Key used during SecurityNegotiation stage of Login Phase: Authentication method. + * @brief iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data packet. * - * @verbatim - * Use: During Login - Security Negotiation - * Senders: Initiator and target - * Scope: connection - * AuthMethod = - * @endverbatim - * The main item of security negotiation is the authentication method - * (AuthMethod).\n - * The authentication methods that can be used (appear in the list-of- - * values) are either vendor-unique methods or those listed in the - * following table: - * Name | Description - * :--- | :--------------------------------------------------------------- - * KRB5 | Kerberos V5 - defined in RFC4120 - * SRP | Secure Remote Password - defined in RFC2945 - * CHAP | Challenge Handshake Authentication Protocol - defined in RFC1994 - * None | No authentication - * - * The AuthMethod selection is followed by an "authentication exchange" - * specific to the authentication method selected.\n - * The authentication method proposal may be made by either the - * initiator or the target. However, the initiator MUST make the first - * step specific to the selected authentication method as soon as it is - * selected. It follows that if the target makes the authentication - * method proposal, the initiator sends the first key(s) of the exchange - * together with its authentication method selection.\n - * The authentication exchange authenticates the initiator to the target - * and, optionally, the target to the initiator. Authentication is - * OPTIONAL to use but MUST be supported by the target and initiator. - * The initiator and target MUST implement CHAP. All other - * authentication methods are OPTIONAL.\n - * Private or public extension algorithms MAY also be negotiated for - * authentication methods. Whenever a private or public extension - * algorithm is part of the default offer (the offer made in the absence - * of explicit administrative action), the implementer MUST ensure that - * CHAP is listed as an alternative in the default offer and "None" is - * not part of the default offer.\n - * Extension authentication methods MUST be named using one of the - * following two formats: - * -# Z-reversed.vendor.dns_name.do_something= - * -# New public key with no name prefix constraints - * - * Authentication methods named using the Z- format are used as private - * extensions. New public keys must be registered with IANA using the - * IETF Review process RFC5226. New public extensions for - * authentication methods MUST NOT use the Z# name prefix.\n - * For all of the public or private extension authentication methods, - * the method-specific keys MUST conform to the format specified for - * standard-label.\n - * To identify the vendor for private extension authentication methods, - * we suggest using the reversed DNS-name as a prefix to the proper - * digest names.\n - * The part of digest-name following Z- MUST conform to the format for - * standard-label.\n - * Support for public or private extension authentication methods is - * OPTIONAL. + * This structure is used by the SCSI INQUIRY command + * in order to fill in the result if the EVPD bit is + * set. */ -#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD "AuthMethod" +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 Key used during SecurityNegotiation stage of Login Phase: Kerberos V5 (KRB5): KRB_AP_REQ. + * @brief iSCSI SCSI Vital Product Data (VPD) Page Thin Provision Inquiry data packet. * - * For KRB5 (Kerberos V5) (see RFC4120 and RFC1964), the initiator MUST use: - * @verbatim - * KRB_AP_REQ= - * @endverbatim - * where KRB_AP_REQ is the client message as defined in RFC4120. - * The default principal name assumed by an iSCSI initiator or target - * (prior to any administrative configuration action) MUST be the iSCSI - * Initiator Name or iSCSI Target Name, respectively, prefixed by the - * string "iscsi/".\n - * If the initiator authentication fails, the target MUST respond with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator has selected the mutual authentication option (by setting - * MUTUAL-REQUIRED in the ap-options field of the KRB_AP_REQ), the - * target MUST reply with: - * @verbatim - * KRB_AP_REP= - * @endverbatim - * where KRB_AP_REP is the server's response message as defined in - * RFC4120.\n - * If mutual authentication was selected and target authentication - * fails, the initiator MUST close the connection.\n - * KRB_AP_REQ and KRB_AP_REP are binary-values, and their binary length - * (not the length of the character string that represents them in - * encoded form) MUST NOT exceed 65536 bytes. Hex or Base64 encoding - * may be used for KRB_AP_REQ and KRB_AP_REP. + * This structure is used by the SCSI INQUIRY command + * in order to fill in the result if the EVPD bit is + * set. */ -#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_KRB_AP_REQ "KRB_AP_REQ" +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 Key used during SecurityNegotiation stage of Login Phase: Kerberos V5 (KRB5): KRB_AP_REP. + * @brief iSCSI SCSI Sense Event data packet. * - * For KRB5 (Kerberos V5) (see RFC4120 and RFC1964), the initiator MUST use: - * @verbatim - * KRB_AP_REQ= - * @endverbatim - * where KRB_AP_REQ is the client message as defined in RFC4120. - * The default principal name assumed by an iSCSI initiator or target - * (prior to any administrative configuration action) MUST be the iSCSI - * Initiator Name or iSCSI Target Name, respectively, prefixed by the - * string "iscsi/".\n - * If the initiator authentication fails, the target MUST respond with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator has selected the mutual authentication option (by setting - * MUTUAL-REQUIRED in the ap-options field of the KRB_AP_REQ), the - * target MUST reply with: - * @verbatim - * KRB_AP_REP= - * @endverbatim - * where KRB_AP_REP is the server's response message as defined in - * RFC4120.\n - * If mutual authentication was selected and target authentication - * fails, the initiator MUST close the connection.\n - * KRB_AP_REQ and KRB_AP_REP are binary-values, and their binary length - * (not the length of the character string that represents them in - * encoded form) MUST NOT exceed 65536 bytes. Hex or Base64 encoding - * may be used for KRB_AP_REQ and KRB_AP_REP. + * 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: */ -#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_KRB_AP_REP "KRB_AP_REP" +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 Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_U. + * @brief iSCSI SCSI basic sense data packet data. * - * For SRP RFC2945, the initiator MUST use: - * @verbatim - * SRP_U= TargetAuth=Yes or TargetAuth=No - * @endverbatim - * The target MUST answer with a Login reject with the "Authorization - * Failure" status or reply with: - * @verbatim - * SRP_GROUP= SRP_s= - * @endverbatim - * where G1,G2... are proposed groups, in order of preference. - * The initiator MUST either close the connection or continue with: - * @verbatim - * SRP_A= - * SRP_GROUP= - * @endverbatim - * where G is one of G1,G2... that were proposed by the target. - * The target MUST answer with a Login reject with the "Authentication - * Failure" status or reply with: - * @verbatim - * SRP_B= - * @endverbatim - * The initiator MUST close the connection or continue with: - * @verbatim - * SRP_M= - * @endverbatim - * If the initiator authentication fails, the target MUST answer with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator sent TargetAuth=Yes in the first message (requiring target - * authentication), the target MUST reply with: - * @verbatim - * SRP_HM= - * @endverbatim - * If the target authentication fails, the initiator MUST close the - * connection:\n - * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using - * the SHA1 hash function, such as SRP-SHA1) and - * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups - * specified in RFC3723.\n - * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are - * binary-values. The length of s,A,B,M and H(A | M | K) in binary form - * (not the length of the character string that represents them in - * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may - * be used for s,A,B,M and H(A | M | K).\n - * For the SRP_GROUP, all the groups specified in RFC3723 up to - * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be - * supported by initiators and targets. To guarantee interoperability, - * targets MUST always offer "SRP-1536" as one of the proposed groups. + * This is the basic SCSI sense data shared by + * all SCSI sense data. */ -#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_SRP_SRP_U "SRP_U" +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 Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_GROUP. + * @brief iSCSI SCSI sense data check condition packet data. * - * For SRP RFC2945, the initiator MUST use: - * @verbatim - * SRP_U= TargetAuth=Yes or TargetAuth=No - * @endverbatim - * The target MUST answer with a Login reject with the "Authorization - * Failure" status or reply with: - * @verbatim - * SRP_GROUP= SRP_s= - * @endverbatim - * where G1,G2... are proposed groups, in order of preference. - * The initiator MUST either close the connection or continue with: - * @verbatim - * SRP_A= - * SRP_GROUP= - * @endverbatim - * where G is one of G1,G2... that were proposed by the target. - * The target MUST answer with a Login reject with the "Authentication - * Failure" status or reply with: - * @verbatim - * SRP_B= - * @endverbatim - * The initiator MUST close the connection or continue with: - * @verbatim - * SRP_M= - * @endverbatim - * If the initiator authentication fails, the target MUST answer with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator sent TargetAuth=Yes in the first message (requiring target - * authentication), the target MUST reply with: - * @verbatim - * SRP_HM= - * @endverbatim - * If the target authentication fails, the initiator MUST close the - * connection:\n - * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using - * the SHA1 hash function, such as SRP-SHA1) and - * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups - * specified in RFC3723.\n - * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are - * binary-values. The length of s,A,B,M and H(A | M | K) in binary form - * (not the length of the character string that represents them in - * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may - * be used for s,A,B,M and H(A | M | K).\n - * For the SRP_GROUP, all the groups specified in RFC3723 up to - * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be - * supported by initiators and targets. To guarantee interoperability, - * targets MUST always offer "SRP-1536" as one of the proposed groups. + * This is the additional SCSI sense data used by + * the check condition status code. */ -#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_SRP_SRP_GROUP "SRP_GROUP" +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 Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_A. + * @brief iSCSI SCSI command READ CAPACITY(10) parameter data packet data. * - * For SRP RFC2945, the initiator MUST use: - * @verbatim - * SRP_U= TargetAuth=Yes or TargetAuth=No - * @endverbatim - * The target MUST answer with a Login reject with the "Authorization - * Failure" status or reply with: - * @verbatim - * SRP_GROUP= SRP_s= - * @endverbatim - * where G1,G2... are proposed groups, in order of preference. - * The initiator MUST either close the connection or continue with: - * @verbatim - * SRP_A= - * SRP_GROUP= - * @endverbatim - * where G is one of G1,G2... that were proposed by the target. - * The target MUST answer with a Login reject with the "Authentication - * Failure" status or reply with: - * @verbatim - * SRP_B= - * @endverbatim - * The initiator MUST close the connection or continue with: - * @verbatim - * SRP_M= - * @endverbatim - * If the initiator authentication fails, the target MUST answer with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator sent TargetAuth=Yes in the first message (requiring target - * authentication), the target MUST reply with: - * @verbatim - * SRP_HM= - * @endverbatim - * If the target authentication fails, the initiator MUST close the - * connection:\n - * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using - * the SHA1 hash function, such as SRP-SHA1) and - * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups - * specified in RFC3723.\n - * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are - * binary-values. The length of s,A,B,M and H(A | M | K) in binary form - * (not the length of the character string that represents them in - * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may - * be used for s,A,B,M and H(A | M | K).\n - * For the SRP_GROUP, all the groups specified in RFC3723 up to - * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be - * supported by initiators and targets. To guarantee interoperability, - * targets MUST always offer "SRP-1536" as one of the proposed groups. + * This returns the Logical Block Address (LBA) + * and block length in bytes. */ -#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_SRP_SRP_A "SRP_A" +typedef struct __attribute__((packed)) iscsi_scsi_read_capacity_10_parameter_data_packet { + /// Last valid Logical Block Address (LBA). + uint32_t lba; -/** - * @brief Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_B. - * - * For SRP RFC2945, the initiator MUST use: - * @verbatim - * SRP_U= TargetAuth=Yes or TargetAuth=No - * @endverbatim - * The target MUST answer with a Login reject with the "Authorization - * Failure" status or reply with: - * @verbatim - * SRP_GROUP= SRP_s= - * @endverbatim - * where G1,G2... are proposed groups, in order of preference. - * The initiator MUST either close the connection or continue with: - * @verbatim - * SRP_A= - * SRP_GROUP= - * @endverbatim - * where G is one of G1,G2... that were proposed by the target. - * The target MUST answer with a Login reject with the "Authentication - * Failure" status or reply with: - * @verbatim - * SRP_B= - * @endverbatim - * The initiator MUST close the connection or continue with: - * @verbatim - * SRP_M= - * @endverbatim - * If the initiator authentication fails, the target MUST answer with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator sent TargetAuth=Yes in the first message (requiring target - * authentication), the target MUST reply with: - * @verbatim - * SRP_HM= - * @endverbatim - * If the target authentication fails, the initiator MUST close the - * connection:\n - * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using - * the SHA1 hash function, such as SRP-SHA1) and - * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups - * specified in RFC3723.\n - * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are - * binary-values. The length of s,A,B,M and H(A | M | K) in binary form - * (not the length of the character string that represents them in - * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may - * be used for s,A,B,M and H(A | M | K).\n - * For the SRP_GROUP, all the groups specified in RFC3723 up to - * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be - * supported by initiators and targets. To guarantee interoperability, - * targets MUST always offer "SRP-1536" as one of the proposed groups. + /// 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. */ -#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_SRP_SRP_B "SRP_B" +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 Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_M. + * @brief iSCSI SCSI command REPORT LUNS parameter data LUN list packet data. * - * For SRP RFC2945, the initiator MUST use: - * @verbatim - * SRP_U= TargetAuth=Yes or TargetAuth=No - * @endverbatim - * The target MUST answer with a Login reject with the "Authorization - * Failure" status or reply with: - * @verbatim - * SRP_GROUP= SRP_s= - * @endverbatim - * where G1,G2... are proposed groups, in order of preference. - * The initiator MUST either close the connection or continue with: - * @verbatim - * SRP_A= - * SRP_GROUP= - * @endverbatim - * where G is one of G1,G2... that were proposed by the target. - * The target MUST answer with a Login reject with the "Authentication - * Failure" status or reply with: - * @verbatim - * SRP_B= - * @endverbatim - * The initiator MUST close the connection or continue with: - * @verbatim - * SRP_M= - * @endverbatim - * If the initiator authentication fails, the target MUST answer with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator sent TargetAuth=Yes in the first message (requiring target - * authentication), the target MUST reply with: - * @verbatim - * SRP_HM= - * @endverbatim - * If the target authentication fails, the initiator MUST close the - * connection:\n - * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using - * the SHA1 hash function, such as SRP-SHA1) and - * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups - * specified in RFC3723.\n - * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are - * binary-values. The length of s,A,B,M and H(A | M | K) in binary form - * (not the length of the character string that represents them in - * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may - * be used for s,A,B,M and H(A | M | K).\n - * For the SRP_GROUP, all the groups specified in RFC3723 up to - * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be - * supported by initiators and targets. To guarantee interoperability, - * targets MUST always offer "SRP-1536" as one of the proposed groups. + * This returns the number of entries in the + * LUN list in bytes. */ -#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_SRP_SRP_M "SRP_M" +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 Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_HM. + * @brief iSCSI SCSI command REPORT LUNS parameter data LUN entry packet data. * - * For SRP RFC2945, the initiator MUST use: - * @verbatim - * SRP_U= TargetAuth=Yes or TargetAuth=No - * @endverbatim - * The target MUST answer with a Login reject with the "Authorization - * Failure" status or reply with: - * @verbatim - * SRP_GROUP= SRP_s= - * @endverbatim - * where G1,G2... are proposed groups, in order of preference. - * The initiator MUST either close the connection or continue with: - * @verbatim - * SRP_A= - * SRP_GROUP= - * @endverbatim - * where G is one of G1,G2... that were proposed by the target. - * The target MUST answer with a Login reject with the "Authentication - * Failure" status or reply with: - * @verbatim - * SRP_B= - * @endverbatim - * The initiator MUST close the connection or continue with: - * @verbatim - * SRP_M= - * @endverbatim - * If the initiator authentication fails, the target MUST answer with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator sent TargetAuth=Yes in the first message (requiring target - * authentication), the target MUST reply with: - * @verbatim - * SRP_HM= - * @endverbatim - * If the target authentication fails, the initiator MUST close the - * connection:\n - * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using - * the SHA1 hash function, such as SRP-SHA1) and - * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups - * specified in RFC3723.\n - * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are - * binary-values. The length of s,A,B,M and H(A | M | K) in binary form - * (not the length of the character string that represents them in - * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may - * be used for s,A,B,M and H(A | M | K).\n - * For the SRP_GROUP, all the groups specified in RFC3723 up to - * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be - * supported by initiators and targets. To guarantee interoperability, - * targets MUST always offer "SRP-1536" as one of the proposed groups. + * This returns a single LUN entry of the + * LUN list. */ -#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_SRP_SRP_HM "SRP_HM" +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 Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_A. + * @brief iSCSI SCSI command REPORT LUNS parameter data LUN entry fill. * - * For CHAP RFC1994, the initiator MUST use: - * @verbatim - * CHAP_A= - * @endverbatim - * where A1,A2... are proposed algorithms, in order of preference. - * The target MUST answer with a Login reject with the "Authentication - * Failure" status or reply with: - * @verbatim - * CHAP_A= - * CHAP_I= - * CHAP_C= - * @endverbatim - * where A is one of A1,A2... that were proposed by the initiator. - * The initiator MUST continue with: - * @verbatim - * CHAP_N= - * CHAP_R= - * @endverbatim - * or, if it requires target authentication, with: - * @verbatim - * CHAP_N= - * CHAP_R= - * CHAP_I= - * CHAP_C= - * @endverbatim - * If the initiator authentication fails, the target MUST answer with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator required target authentication, the target MUST either - * answer with a Login reject with "Authentication Failure" or reply - * with: - * @verbatim - * CHAP_N= - * CHAP_R= - * @endverbatim - * If the target authentication fails, the initiator MUST close the - * connection:\n - * where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, - * Algorithm, Identifier, Challenge, and Response as defined in - * RFC1994.\n - * N is a text string; A,A1,A2, and I are numbers; C and R are - * binary-values. Their binary length (not the length of the character - * string that represents them in encoded form) MUST NOT exceed - * 1024 bytes. Hex or Base64 encoding may be used for C and R.\n - * For the Algorithm, as stated in [RFC1994], one value is required to - * be implemented: - * @verbatim - * 5 (CHAP with MD5) - * @endverbatim - * To guarantee interoperability, initiators MUST always offer it as one - * of the proposed algorithms. + * This structure is used by iterating through + * all iSCSI LUNs in order to fill in the + * REPORT LUNS parameter data structure. */ -#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_A "CHAP_A" +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 Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_I. + * @brief iSCSI SCSI command MODE SELECT(6) parameter list packet data. * - * For CHAP RFC1994, the initiator MUST use: - * @verbatim - * CHAP_A= - * @endverbatim - * where A1,A2... are proposed algorithms, in order of preference. - * The target MUST answer with a Login reject with the "Authentication - * Failure" status or reply with: - * @verbatim - * CHAP_A= - * CHAP_I= - * CHAP_C= - * @endverbatim - * where A is one of A1,A2... that were proposed by the initiator. - * The initiator MUST continue with: - * @verbatim - * CHAP_N= - * CHAP_R= - * @endverbatim - * or, if it requires target authentication, with: - * @verbatim - * CHAP_N= - * CHAP_R= - * CHAP_I= - * CHAP_C= - * @endverbatim - * If the initiator authentication fails, the target MUST answer with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator required target authentication, the target MUST either - * answer with a Login reject with "Authentication Failure" or reply - * with: - * @verbatim - * CHAP_N= - * CHAP_R= - * @endverbatim - * If the target authentication fails, the initiator MUST close the - * connection:\n - * where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, - * Algorithm, Identifier, Challenge, and Response as defined in - * RFC1994.\n - * N is a text string; A,A1,A2, and I are numbers; C and R are - * binary-values. Their binary length (not the length of the character - * string that represents them in encoded form) MUST NOT exceed - * 1024 bytes. Hex or Base64 encoding may be used for C and R.\n - * For the Algorithm, as stated in [RFC1994], one value is required to - * be implemented: - * @verbatim - * 5 (CHAP with MD5) - * @endverbatim - * To guarantee interoperability, initiators MUST always offer it as one - * of the proposed algorithms. + * This returns 32-bit vendor specific data. */ -#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_I "CHAP_I" +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 Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_C. + * @brief iSCSI SCSI command MODE SELECT(10) parameter list packet data. * - * For CHAP RFC1994, the initiator MUST use: - * @verbatim - * CHAP_A= - * @endverbatim - * where A1,A2... are proposed algorithms, in order of preference. - * The target MUST answer with a Login reject with the "Authentication - * Failure" status or reply with: - * @verbatim - * CHAP_A= - * CHAP_I= - * CHAP_C= - * @endverbatim - * where A is one of A1,A2... that were proposed by the initiator. - * The initiator MUST continue with: - * @verbatim - * CHAP_N= - * CHAP_R= - * @endverbatim - * or, if it requires target authentication, with: - * @verbatim - * CHAP_N= - * CHAP_R= - * CHAP_I= - * CHAP_C= - * @endverbatim - * If the initiator authentication fails, the target MUST answer with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator required target authentication, the target MUST either - * answer with a Login reject with "Authentication Failure" or reply - * with: - * @verbatim - * CHAP_N= - * CHAP_R= - * @endverbatim - * If the target authentication fails, the initiator MUST close the - * connection:\n - * where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, - * Algorithm, Identifier, Challenge, and Response as defined in - * RFC1994.\n - * N is a text string; A,A1,A2, and I are numbers; C and R are - * binary-values. Their binary length (not the length of the character - * string that represents them in encoded form) MUST NOT exceed - * 1024 bytes. Hex or Base64 encoding may be used for C and R.\n - * For the Algorithm, as stated in [RFC1994], one value is required to - * be implemented: - * @verbatim - * 5 (CHAP with MD5) - * @endverbatim - * To guarantee interoperability, initiators MUST always offer it as one - * of the proposed algorithms. + * This returns 64-bit vendor specific data. */ -#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_C "CHAP_C" +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 Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_N. + * @brief iSCSI SCSI command MODE SENSE(6) parameter header packet data. * - * For CHAP RFC1994, the initiator MUST use: - * @verbatim - * CHAP_A= - * @endverbatim - * where A1,A2... are proposed algorithms, in order of preference. - * The target MUST answer with a Login reject with the "Authentication - * Failure" status or reply with: - * @verbatim - * CHAP_A= - * CHAP_I= - * CHAP_C= - * @endverbatim - * where A is one of A1,A2... that were proposed by the initiator. - * The initiator MUST continue with: - * @verbatim - * CHAP_N= - * CHAP_R= - * @endverbatim - * or, if it requires target authentication, with: - * @verbatim - * CHAP_N= - * CHAP_R= - * CHAP_I= - * CHAP_C= - * @endverbatim - * If the initiator authentication fails, the target MUST answer with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator required target authentication, the target MUST either - * answer with a Login reject with "Authentication Failure" or reply - * with: - * @verbatim - * CHAP_N= - * CHAP_R= - * @endverbatim - * If the target authentication fails, the initiator MUST close the - * connection:\n - * where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, - * Algorithm, Identifier, Challenge, and Response as defined in - * RFC1994.\n - * N is a text string; A,A1,A2, and I are numbers; C and R are - * binary-values. Their binary length (not the length of the character - * string that represents them in encoded form) MUST NOT exceed - * 1024 bytes. Hex or Base64 encoding may be used for C and R.\n - * For the Algorithm, as stated in [RFC1994], one value is required to - * be implemented: - * @verbatim - * 5 (CHAP with MD5) - * @endverbatim - * To guarantee interoperability, initiators MUST always offer it as one - * of the proposed algorithms. + * This returns the mode parameter header + * data. */ -#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_N "CHAP_N" +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 Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_R. + * @brief iSCSI SCSI command MODE SENSE(10) parameter header packet data. * - * For CHAP RFC1994, the initiator MUST use: - * @verbatim - * CHAP_A= - * @endverbatim - * where A1,A2... are proposed algorithms, in order of preference. - * The target MUST answer with a Login reject with the "Authentication - * Failure" status or reply with: - * @verbatim - * CHAP_A= - * CHAP_I= - * CHAP_C= - * @endverbatim - * where A is one of A1,A2... that were proposed by the initiator. - * The initiator MUST continue with: - * @verbatim - * CHAP_N= - * CHAP_R= - * @endverbatim - * or, if it requires target authentication, with: - * @verbatim - * CHAP_N= - * CHAP_R= - * CHAP_I= - * CHAP_C= - * @endverbatim - * If the initiator authentication fails, the target MUST answer with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator required target authentication, the target MUST either - * answer with a Login reject with "Authentication Failure" or reply - * with: - * @verbatim - * CHAP_N= - * CHAP_R= - * @endverbatim - * If the target authentication fails, the initiator MUST close the - * connection:\n - * where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, - * Algorithm, Identifier, Challenge, and Response as defined in - * RFC1994.\n - * N is a text string; A,A1,A2, and I are numbers; C and R are - * binary-values. Their binary length (not the length of the character - * string that represents them in encoded form) MUST NOT exceed - * 1024 bytes. Hex or Base64 encoding may be used for C and R.\n - * For the Algorithm, as stated in [RFC1994], one value is required to - * be implemented: - * @verbatim - * 5 (CHAP with MD5) - * @endverbatim - * To guarantee interoperability, initiators MUST always offer it as one - * of the proposed algorithms. + * This returns the mode parameter header + * data. */ -#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_R "CHAP_R" +typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_10_parameter_header_data_packet { + /// Mode data length in bytes. + uint16_t mode_data_len; -/* Login/Text Operational Text Keys + /// Medium type. + uint8_t medium_type; - Some session-specific parameters MUST only be carried on the leading - connection and cannot be changed after the leading connection login - (e.g., MaxConnections - the maximum number of connections). This - holds for a single connection session with regard to connection - restart. The keys that fall into this category have the "use: LO" - (Leading Only). + /// Flags. + int8_t flags; - Keys that can only be used during login have the "use: IO" - (Initialize Only), while those that can be used in both the Login - Phase and Full Feature Phase have the "use: ALL". + /// Long Logical Block Address (LONGLBA). + uint8_t long_lba; - Keys that can only be used during the Full Feature Phase use FFPO - (Full Feature Phase Only). + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved; - Keys marked as Any-Stage may also appear in the SecurityNegotiation - stage, while all other keys described in this section are - operational keys. + /// Block descriptor length in bytes. + uint16_t block_desc_len; +} iscsi_scsi_mode_sense_10_parameter_header_data_packet; - Keys that do not require an answer are marked as Declarative. - Key scope is indicated as session-wide (SW) or connection-only (CO). +/** + * @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; - "Result function", wherever mentioned, states the function that can - be applied to check the validity of the responder selection. - "Minimum" means that the selected value cannot exceed the offered - value. "Maximum" means that the selected value cannot be lower than - the offered value. "AND" means that the selected value must be a - possible result of a Boolean "and" function with an arbitrary Boolean - value (e.g., if the offered value is No the selected value must be - No). "OR" means that the selected value must be a possible result of - a Boolean "or" function with an arbitrary Boolean value (e.g., if the - offered value is Yes the selected value must be Yes). -*/ /** - * @brief Login/Text Operational Session Text Key: Header digest. - * - * @verbatim - * Use: IO - * Senders: Initiator and target - * Scope: CO - * HeaderDigest = - * Default is None for HeaderDigest. - * @endverbatim - * Digests enable the checking of end-to-end, non-cryptographic data - * integrity beyond the integrity checks provided by the link layers and - * the covering of the whole communication path, including all elements - * that may change the network-level PDUs, such as routers, switches, - * and proxies.\n - * The following table lists cyclic integrity checksums that can be - * negotiated for the digests and MUST be implemented by every iSCSI - * initiator and target. These digest options only have error detection - * significance. - * Name | Description | Generator - * :----- | :---------- | :---------- - * CRC32C | 32-bit CRC | 0x11EDC6F41 - * None | no digest || + * @brief iSCSI SCSI command MODE SENSE(10) long LBA mode parameter block descriptor packet data. * - * The generator polynomial G(x) for this digest is given in hexadecimal - * notation (e.g. "0x3b" stands for 0011 1011, and the polynomial is - * x**5 + x**4 + x**3 + x + 1).\n - * When the initiator and target agree on a digest, this digest MUST be - * used for every PDU in the Full Feature Phase.\n - * Padding bytes, when present in a segment covered by a CRC, SHOULD be - * set to 0 and are included in the CRC.\n - * The CRC MUST be calculated by a method that produces the same results - * as the following process: - * - The PDU bits are considered as the coefficients of a polynomial - * M(x) of degree n - 1; bit 7 of the lowest numbered byte is - * considered the most significant bit (x**n - 1), followed by bit 6 - * of the lowest numbered byte through bit 0 of the highest numbered - * byte (x**0). - * - The most significant 32 bits are complemented. - * - The polynomial is multiplied by x**32, then divided by G(x). The - * generator polynomial produces a remainder R(x) of degree <= 31. - * - The coefficients of R(x) are formed into a 32-bit sequence. - * - The bit sequence is complemented, and the result is the CRC. - * - The CRC bits are mapped into the digest word. The x**31 - * coefficient is mapped to bit 7 of the lowest numbered byte of the - * digest, and the mapping continues with successive coefficients and - * bits so that the x**24 coefficient is mapped to bit 0 of the lowest - * numbered byte. The mapping continues further with the x**23 - * coefficient mapped to bit 7 of the next byte in the digest until - * the x**0 coefficient is mapped to bit 0 of the highest numbered - * byte of the digest. - * - Computing the CRC over any segment (data or header) extended to - * include the CRC built using the generator 0x11edc6f41 will always - * get the value 0x1c2d19ed as its final remainder (R(x)). This value - * is given here in its polynomial form (i.e., not mapped as the - * digest word). - * - * For a discussion about selection criteria for the CRC, see RFC3385.\n - * For a detailed analysis of the iSCSI polynomial, see Castagnoli93.\n - * Private or public extension algorithms MAY also be negotiated for - * digests. Whenever a private or public digest extension algorithm is - * part of the default offer (the offer made in the absence of explicit - * administrative action), the implementer MUST ensure that CRC32C is - * listed as an alternative in the default offer and "None" is not part - * of the default offer.\n - * Extension digest algorithms MUST be named using one of the following - * two formats: - * 1. Y-reversed.vendor.dns_name.do_something= - * 2. New public key with no name prefix constraints - * - * Digests named using the Y- format are used for private purposes - * (unregistered). New public keys must be registered with IANA using - * the IETF Review process (RFC5226). New public extensions for - * digests MUST NOT use the Y# name prefix.\n - * For private extension digests, to identify the vendor we suggest - * using the reversed DNS-name as a prefix to the proper digest names.\n - * The part of digest-name following Y- MUST conform to the format for - * standard-label specified.\n - * Support for public or private extension digests is OPTIONAL. + * This returns the long Logical Block + * Address (LBA) mode parameter block + * descriptor data. */ -#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_HEADER_DIGEST "HeaderDigest" +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 Login/Text Operational Session Text Key: Data digest. - * - * @verbatim - * Use: IO - * Senders: Initiator and target - * Scope: CO - * DataDigest = - * Default is None for DataDigest. - * @endverbatim - * Digests enable the checking of end-to-end, non-cryptographic data - * integrity beyond the integrity checks provided by the link layers and - * the covering of the whole communication path, including all elements - * that may change the network-level PDUs, such as routers, switches, - * and proxies.\n - * The following table lists cyclic integrity checksums that can be - * negotiated for the digests and MUST be implemented by every iSCSI - * initiator and target. These digest options only have error detection - * significance. - * Name | Description | Generator - * :----- | :---------- | :---------- - * CRC32C | 32-bit CRC | 0x11EDC6F41 - * None | no digest || + * @brief iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page packet data. * - * The generator polynomial G(x) for this digest is given in hexadecimal - * notation (e.g. "0x3b" stands for 0011 1011, and the polynomial is - * x**5 + x**4 + x**3 + x + 1).\n - * When the initiator and target agree on a digest, this digest MUST be - * used for every PDU in the Full Feature Phase.\n - * Padding bytes, when present in a segment covered by a CRC, SHOULD be - * set to 0 and are included in the CRC.\n - * The CRC MUST be calculated by a method that produces the same results - * as the following process: - * - The PDU bits are considered as the coefficients of a polynomial - * M(x) of degree n - 1; bit 7 of the lowest numbered byte is - * considered the most significant bit (x**n - 1), followed by bit 6 - * of the lowest numbered byte through bit 0 of the highest numbered - * byte (x**0). - * - The most significant 32 bits are complemented. - * - The polynomial is multiplied by x**32, then divided by G(x). The - * generator polynomial produces a remainder R(x) of degree <= 31. - * - The coefficients of R(x) are formed into a 32-bit sequence. - * - The bit sequence is complemented, and the result is the CRC. - * - The CRC bits are mapped into the digest word. The x**31 - * coefficient is mapped to bit 7 of the lowest numbered byte of the - * digest, and the mapping continues with successive coefficients and - * bits so that the x**24 coefficient is mapped to bit 0 of the lowest - * numbered byte. The mapping continues further with the x**23 - * coefficient mapped to bit 7 of the next byte in the digest until - * the x**0 coefficient is mapped to bit 0 of the highest numbered - * byte of the digest. - * - Computing the CRC over any segment (data or header) extended to - * include the CRC built using the generator 0x11edc6f41 will always - * get the value 0x1c2d19ed as its final remainder (R(x)). This value - * is given here in its polynomial form (i.e., not mapped as the - * digest word). + * 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. * - * For a discussion about selection criteria for the CRC, see RFC3385.\n - * For a detailed analysis of the iSCSI polynomial, see Castagnoli93.\n - * Private or public extension algorithms MAY also be negotiated for - * digests. Whenever a private or public digest extension algorithm is - * part of the default offer (the offer made in the absence of explicit - * administrative action), the implementer MUST ensure that CRC32C is - * listed as an alternative in the default offer and "None" is not part - * of the default offer.\n - * Extension digest algorithms MUST be named using one of the following - * two formats: - * 1. Y-reversed.vendor.dns_name.do_something= - * 2. New public key with no name prefix constraints + * 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. * - * Digests named using the Y- format are used for private purposes - * (unregistered). New public keys must be registered with IANA using - * the IETF Review process (RFC5226). New public extensions for - * digests MUST NOT use the Y# name prefix.\n - * For private extension digests, to identify the vendor we suggest - * using the reversed DNS-name as a prefix to the proper digest names.\n - * The part of digest-name following Y- MUST conform to the format for - * standard-label specified.\n - * Support for public or private extension digests is OPTIONAL. + * This returns mode page specific data. */ -#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST "DataDigest" +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 Login/Text Operational Session Text Key: New connections. + * @brief iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) disconnect / reconnect mode page packet data. * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * Irrelevant when: SessionType=Discovery - * MaxConnections= - * Default is 1. - * @endverbatim - * Result function is Minimum.\n - * The initiator and target negotiate the maximum number of connections - * requested/acceptable. + * This returns mode page specific data. */ -#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS "MaxConnections" +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 Login/Text Operational Session Text Key: Send targets. + * @brief iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) verify error recovery mode page packet data. * - * @verbatim - * Use: FFPO - * Senders: Initiator - * Scope: SW - * @endverbatim - * The text in this appendix is a normative part of this document.\n - * To reduce the amount of configuration required on an initiator, iSCSI - * provides the SendTargets Text Request. The initiator uses the - * SendTargets request to get a list of targets to which it may have - * access, as well as the list of addresses (IP address and TCP port) on - * which these targets may be accessed.\n + * 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 + +/// SCSI command opcode (embedded in iSCSI protocol): MODE SELECT(6). +#define ISCSI_SCSI_OPCODE_MODESELECT6 0x15 + +/// SCSI command opcode (embedded in iSCSI protocol): RESERVE(6). +#define ISCSI_SCSI_OPCODE_RESERVE6 0x16 + +/// SCSI command opcode (embedded in iSCSI protocol): RELEASE(6). +#define ISCSI_SCSI_OPCODE_RELEASE6 0x17 + +/// SCSI command opcode (embedded in iSCSI protocol): MODE SENSE(6). +#define ISCSI_SCSI_OPCODE_MODESENSE6 0x1A + +/// SCSI command opcode (embedded in iSCSI protocol): START STOP UNIT. +#define ISCSI_SCSI_OPCODE_STARTSTOPUNIT 0x1B + +/// SCSI command opcode (embedded in iSCSI protocol): PREVENT ALLOW MEDIUM REMOVAL. +#define ISCSI_SCSI_OPCODE_PREVENTALLOW 0x1E + +/// SCSI command opcode (embedded in iSCSI protocol): READ CAPACITY(10). +#define ISCSI_SCSI_OPCODE_READCAPACITY10 0x25 + +/// SCSI command opcode (embedded in iSCSI protocol): READ(10). +#define ISCSI_SCSI_OPCODE_READ10 0x28 + +/// SCSI command opcode (embedded in iSCSI protocol): WRITE(10). +#define ISCSI_SCSI_OPCODE_WRITE10 0x2A + +/// SCSI command opcode (embedded in iSCSI protocol): WRITE AND VERIFY(10). +#define ISCSI_SCSI_OPCODE_WRITE_VERIFY10 0x2E + +/// SCSI command opcode (embedded in iSCSI protocol): VERIFY(10). +#define ISCSI_SCSI_OPCODE_VERIFY10 0x2F + +/// SCSI command opcode (embedded in iSCSI protocol): PRE-FETCH(10). +#define ISCSI_SCSI_OPCODE_PREFETCH10 0x34 + +/// SCSI command opcode (embedded in iSCSI protocol): SYNCHRONIZE CACHE(10). +#define ISCSI_SCSI_OPCODE_SYNCHRONIZECACHE10 0x35 + +/// SCSI command opcode (embedded in iSCSI protocol): READ DEFECT DATA(10). +#define ISCSI_SCSI_OPCODE_READ_DEFECT_DATA10 0x37 + +/// SCSI command opcode (embedded in iSCSI protocol): WRITE SAME(10). +#define ISCSI_SCSI_OPCODE_WRITE_SAME10 0x41 + +/// SCSI command opcode (embedded in iSCSI protocol): UNMAP. +#define ISCSI_SCSI_OPCODE_UNMAP 0x42 + +/// SCSI command opcode (embedded in iSCSI protocol): READ TOC/PMA/ATIP. +#define ISCSI_SCSI_OPCODE_READTOC 0x43 + +/// 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 + +/// SCSI command opcode (embedded in iSCSI protocol): PERSISTENT RESERVE IN. +#define ISCSI_SCSI_OPCODE_PERSISTENT_RESERVE_IN 0x5E + +/// SCSI command opcode (embedded in iSCSI protocol): PERSISTENT RESERVE OUT. +#define ISCSI_SCSI_OPCODE_PERSISTENT_RESERVE_OUT 0x5F + +/// SCSI command opcode (embedded in iSCSI protocol): Third-party Copy OUT. +#define ISCSI_SCSI_OPCODE_EXTENDED_COPY 0x83 + +/// SCSI command opcode (embedded in iSCSI protocol): Third-party Copy IN. +#define ISCSI_SCSI_OPCODE_RECEIVE_COPY_RESULTS 0x84 + +/// SCSI command opcode (embedded in iSCSI protocol): READ(16). +#define ISCSI_SCSI_OPCODE_READ16 0x88 + +/// SCSI command opcode (embedded in iSCSI protocol): COMPARE AND WRITE. +#define ISCSI_SCSI_OPCODE_COMPARE_AND_WRITE 0x89 + +/// SCSI command opcode (embedded in iSCSI protocol): WRITE(16). +#define ISCSI_SCSI_OPCODE_WRITE16 0x8A + +/// SCSI command opcode (embedded in iSCSI protocol): ORWRITE. +#define ISCSI_SCSI_OPCODE_ORWRITE 0x8B + +/// SCSI command opcode (embedded in iSCSI protocol): WRITE AND VERIFY(16). +#define ISCSI_SCSI_OPCODE_WRITE_VERIFY16 0x8E + +/// SCSI command opcode (embedded in iSCSI protocol): VERIFY(16). +#define ISCSI_SCSI_OPCODE_VERIFY16 0x8F + +/// SCSI command opcode (embedded in iSCSI protocol): PRE-FETCH(16). +#define ISCSI_SCSI_OPCODE_PREFETCH16 0x90 + +/// SCSI command opcode (embedded in iSCSI protocol): SYNCHRONIZE CACHE(16). +#define ISCSI_SCSI_OPCODE_SYNCHRONIZECACHE16 0x91 + +/// SCSI command opcode (embedded in iSCSI protocol): WRITE SAME(16). +#define ISCSI_SCSI_OPCODE_WRITE_SAME16 0x93 + +/// SCSI command opcode (embedded in iSCSI protocol): WRITE ATOMIC(16). +#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_16 0x9E + +/// SCSI command opcode (embedded in iSCSI protocol): REPORT LUNS. +#define ISCSI_SCSI_OPCODE_REPORTLUNS 0xA0 + +/// SCSI command opcode (embedded in iSCSI protocol): MAINTENANCE IN. +#define ISCSI_SCSI_OPCODE_MAINTENANCE_IN 0xA3 + +/// SCSI command opcode (embedded in iSCSI protocol): READ(12). +#define ISCSI_SCSI_OPCODE_READ12 0xA8 + +/// SCSI command opcode (embedded in iSCSI protocol): WRITE(12). +#define ISCSI_SCSI_OPCODE_WRITE12 0xAA + +/// SCSI command opcode (embedded in iSCSI protocol): WRITE AND VERIFY(12). +#define ISCSI_SCSI_OPCODE_WRITE_VERIFY12 0xAE + +/// SCSI command opcode (embedded in iSCSI protocol): VERIFY(12). +#define ISCSI_SCSI_OPCODE_VERIFY12 0xAF + +/// SCSI command opcode (embedded in iSCSI protocol): READ DEFECT DATA(12). +#define ISCSI_SCSI_OPCODE_READ_DEFECT_DATA12 0xB7 + + +/** + * @brief iSCSI SCSI command flags: No unsolicited data. + * + * (F) is set to 1 when no unsolicited SCSI Data-Out PDUs + * follow this PDU. When F = 1 for a write and if Expected + * Data Transfer Length is larger than the + * DataSegmentLength, the target may solicit additional data + * through R2T. + */ +#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. + * + * (R) is set to 1 when the command is expected to input data. + */ +#define ISCSI_SCSI_CMD_FLAGS_TASK_READ (1 << 6) + +/** + * @brief iSCSI SCSI command flags: Expected output data. + * + * (W) is set to 1 when the command is expected to output data. + */ +#define ISCSI_SCSI_CMD_FLAGS_TASK_WRITE (1 << 5) + + +/// SCSI command flags task attribute: Untagged. +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_UNTAGGED 0x0 + +/// SCSI command flags task attribute: Simple. +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_SIMPLE 0x1 + +/// SCSI command flags task attribute: Ordered. +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_ORDERED 0x2 + +/// SCSI command flags task attribute: Head of queue. +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_HEAD_QUEUE 0x3 + +/// SCSI command flags task attribute: ACA. +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_ACA 0x4 + +/// SCSI command flags task attribute: Reserved. +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_RESERVED_1 0x5 + +/// SCSI command flags task attribute: Reserved. +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_RESERVED_2 0x6 + +/// SCSI command flags task attribute: Reserved. +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_RESERVED_3 0x7 + +/// SCSI command flags Task Attributes (ATTR) are encoded in the first three LSBs. +#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_MASK 0x7 + + +/** + * @brief iSCSI Flag and Task Attributes for SCSI command packet data. + * + * Flags and Task Attributes: + * At least one of the W and F bits MUST be set to 1.\n + * Either or both of R and W MAY be 1 when the Expected Data Transfer + * Length and/or the Bidirectional Read Expected Data Transfer Length + * are 0, but they MUST NOT both be 0 when the Expected Data Transfer + * Length and/or Bidirectional Read Expected Data Transfer Length are + * not 0 (i.e., when some data transfer is expected, the transfer + * direction is indicated by the R and/or W bit). + */ +typedef struct __attribute__((packed)) iscsi_scsi_cmd_packet { + /// Always 1 according to the iSCSI specification. + uint8_t opcode; + + /// Flags and Task Attributes. + int8_t flags_task; + + /// Reserved for future usage, MUST always be 0. + uint16_t reserved; + + /// Total length of AHS. + uint8_t total_ahs_len; + + /// Length of DataSegment. + uint8_t ds_len[3]; + + /// SCSI LUN bit mask. + uint64_t lun; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /** + * @brief Expected Data Transfer Length. + * + * For unidirectional operations, the Expected Data Transfer Length + * field contains the number of bytes of data involved in this SCSI + * operation. For a unidirectional write operation (W flag set to 1 and + * R flag set to 0), the initiator uses this field to specify the number + * of bytes of data it expects to transfer for this operation. For a + * unidirectional read operation (W flag set to 0 and R flag set to 1), + * the initiator uses this field to specify the number of bytes of data + * it expects the target to transfer to the initiator. It corresponds + * to the SAM-2 byte count.\n + * For bidirectional operations (both R and W flags are set to 1), this + * field contains the number of data bytes involved in the write + * transfer. For bidirectional operations, an additional header segment + * MUST be present in the header sequence that indicates the + * Bidirectional Read Expected Data Transfer Length. The Expected Data + * Transfer Length field and the Bidirectional Read Expected Data + * Transfer Length field correspond to the SAM-2 byte count. + * If the Expected Data Transfer Length for a write and the length of + * the immediate data part that follows the command (if any) are the + * same, then no more data PDUs are expected to follow. In this case, + * the F bit MUST be set to 1.\n + * If the Expected Data Transfer Length is higher than the + * FirstBurstLength (the negotiated maximum amount of unsolicited data + * the target will accept), the initiator MUST send the maximum amount + * of unsolicited data OR ONLY the immediate data, if any. + * Upon completion of a data transfer, the target informs the initiator + * (through residual counts) of how many bytes were actually processed + * (sent and/or received) by the target. + */ + uint32_t exp_xfer_len; + + /// The CmdSN enables ordered delivery across multiple connections in a single session. + uint32_t cmd_sn; + + /// Command responses up to ExpStatSN - 1 (modulo 2**32) have been received (acknowledges status) on the connection. + uint32_t exp_stat_sn; + + /** + * @brief SCSI Command Descriptor Block (CDB). + * + * 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. + */ + iscsi_scsi_cdb scsi_cdb; + + /// Optional AHS packet data. + iscsi_ahs_packet ahs; + + /// Optional header digest. + iscsi_header_digest hdr_digest; + + /// Optional data segment, command data. + iscsi_scsi_ds_cmd_data ds_cmd_data; + + /// Optional data digest. + iscsi_data_digest data_digest; +} iscsi_scsi_cmd_packet; + + +/** + * @brief SCSI response flags: Residual Underflow. + * + * (U) set for Residual Underflow. In this case, the Residual + * Count indicates the number of bytes that were not + * transferred out of the number of bytes that were expected + * to be transferred. For a bidirectional operation, the + * Residual Count contains the residual for the write + * operation. + * + * Bits O and U and bits o and u are mutually exclusive (i.e., having + * both o and u or O and U set to 1 is a protocol error). + * + * For a response other than "Command Completed at Target", bits 3-6 + * MUST be 0. + */ +#define ISCSI_SCSI_RESPONSE_FLAGS_RES_UNDERFLOW (1 << 1) + +/** + * @brief SCSI response flags: Residual Overflow. + * + * (O) set for Residual Overflow. In this case, the Residual + * Count indicates the number of bytes that were not + * transferred because the initiator's Expected Data + * Transfer Length was not sufficient. For a bidirectional + * operation, the Residual Count contains the residual for + * the write operation. + * + * Bits O and U and bits o and u are mutually exclusive (i.e., having + * both o and u or O and U set to 1 is a protocol error). + * + * For a response other than "Command Completed at Target", bits 3-6 + * MUST be 0. + */ +#define ISCSI_SCSI_RESPONSE_FLAGS_RES_OVERFLOW (1 << 2) + +/** + * @brief SCSI response flags: Bidirectional Read Residual Underflow. + * + * (u) set for Bidirectional Read Residual Underflow. In this + * case, the Bidirectional Read Residual Count indicates the + * number of bytes that were not transferred to the + * initiator out of the number of bytes expected to be + * transferred. + * + * Bits O and U and bits o and u are mutually exclusive (i.e., having + * both o and u or O and U set to 1 is a protocol error). + * + * For a response other than "Command Completed at Target", bits 3-6 + * MUST be 0. + */ +#define ISCSI_SCSI_RESPONSE_FLAGS_BIDI_READ_RES_UNDERFLOW (1 << 3) + +/** + * @brief SCSI response flags: Bidirectional Read Residual Overflow. + * + + (o) set for Bidirectional Read Residual Overflow. In this + * case, the Bidirectional Read Residual Count indicates the + * number of bytes that were not transferred to the + * initiator because the initiator's Bidirectional Read + * Expected Data Transfer Length was not sufficient. + * + * Bits O and U and bits o and u are mutually exclusive (i.e., having + * both o and u or O and U set to 1 is a protocol error). + * + * For a response other than "Command Completed at Target", bits 3-6 + * MUST be 0. + */ +#define ISCSI_SCSI_RESPONSE_FLAGS_BIDI_READ_RES_OVERFLOW (1 << 4) + +/** + * @brief SCSI status response code: Good. + * + * The Status field is used to report the SCSI status of the command (as + * specified in SAM2) and is only valid if the response code is + * Command Completed at Target. + * + * If a SCSI device error is detected while data from the initiator is + * still expected (the command PDU did not contain all the data and the + * target has not received a data PDU with the Final bit set), the + * target MUST wait until it receives a data PDU with the F bit set in + * the last expected sequence before sending the Response PDU. + */ +#define ISCSI_SCSI_RESPONSE_STATUS_GOOD 0x00 + +/** + * @brief SCSI status response code: Check condition. + * + * The Status field is used to report the SCSI status of the command (as + * specified in SAM2) and is only valid if the response code is + * Command Completed at Target. + * + * If a SCSI device error is detected while data from the initiator is + * still expected (the command PDU did not contain all the data and the + * target has not received a data PDU with the Final bit set), the + * target MUST wait until it receives a data PDU with the F bit set in + * the last expected sequence before sending the Response PDU. + */ +#define ISCSI_SCSI_RESPONSE_STATUS_CHECK_COND 0x02 + +/** + * @brief SCSI status response code: Busy. + * + * The Status field is used to report the SCSI status of the command (as + * specified in SAM2) and is only valid if the response code is + * Command Completed at Target. + * + * If a SCSI device error is detected while data from the initiator is + * still expected (the command PDU did not contain all the data and the + * target has not received a data PDU with the Final bit set), the + * target MUST wait until it receives a data PDU with the F bit set in + * the last expected sequence before sending the Response PDU. + */ +#define ISCSI_SCSI_RESPONSE_STATUS_BUSY 0x08 + +/** + * @brief SCSI status response code: Residual conflict. + * + * The Status field is used to report the SCSI status of the command (as + * specified in SAM2) and is only valid if the response code is + * Command Completed at Target. + * + * If a SCSI device error is detected while data from the initiator is + * still expected (the command PDU did not contain all the data and the + * target has not received a data PDU with the Final bit set), the + * target MUST wait until it receives a data PDU with the F bit set in + * the last expected sequence before sending the Response PDU. + */ +#define ISCSI_SCSI_RESPONSE_STATUS_RES_CONFLICT 0x18 + +/** + * @brief SCSI status response code: Task set full. + * + * The Status field is used to report the SCSI status of the command (as + * specified in SAM2) and is only valid if the response code is + * Command Completed at Target. + * + * If a SCSI device error is detected while data from the initiator is + * still expected (the command PDU did not contain all the data and the + * target has not received a data PDU with the Final bit set), the + * target MUST wait until it receives a data PDU with the F bit set in + * the last expected sequence before sending the Response PDU. + */ +#define ISCSI_SCSI_RESPONSE_STATUS_TASK_SET_FULL 0x28 + +/** + * @brief SCSI status response code: ACA active. + * + * The Status field is used to report the SCSI status of the command (as + * specified in SAM2) and is only valid if the response code is + * Command Completed at Target. + * + * If a SCSI device error is detected while data from the initiator is + * still expected (the command PDU did not contain all the data and the + * target has not received a data PDU with the Final bit set), the + * target MUST wait until it receives a data PDU with the F bit set in + * the last expected sequence before sending the Response PDU. + */ +#define ISCSI_SCSI_RESPONSE_STATUS_ACA_ACTIVE 0x30 + +/** + * @brief SCSI status response code: Task aborted. + * + * The Status field is used to report the SCSI status of the command (as + * specified in SAM2) and is only valid if the response code is + * Command Completed at Target. + * + * If a SCSI device error is detected while data from the initiator is + * still expected (the command PDU did not contain all the data and the + * target has not received a data PDU with the Final bit set), the + * target MUST wait until it receives a data PDU with the F bit set in + * the last expected sequence before sending the Response PDU. + */ +#define ISCSI_SCSI_RESPONSE_STATUS_TASK_ABORTED 0x40 + + +/// SCSI response code: Command Completed at Target. +#define ISCSI_SCSI_RESPONSE_CODE_OK 0x00 + +/// SCSI response code: Target Failure. +#define ISCSI_SCSI_RESPONSE_CODE_FAIL 0x01 + +/// SCSI response code: First vendor specific response code. +#define ISCSI_SCSI_RESPONSE_CODE_VENDOR_FIRST 0x80 + +/// SCSI response code: Last vendor specific response code. +#define ISCSI_SCSI_RESPONSE_CODE_VENDOR_LAST 0xFF + +/** + * @brief iSCSI SCSI command response packet data. + * + * The Response field is used to report a service response. The mapping + * of the response code into a SCSI service response code value, if + * needed, is outside the scope of this document. However, in symbolic + * terms, response value 0x00 maps to the SCSI service response (see + */ +typedef struct __attribute__((packed)) iscsi_scsi_response_packet { + /// Always 0x21 according to specification. + uint8_t opcode; + + /// Flags. + int8_t flags; + + /// This field contains the iSCSI service response. + uint8_t response; + + /// The Status field is used to report the SCSI status of the command (as specified in SAM2) and is only valid if the response code is Command Completed at Target. + uint8_t status; + + /// Total AHS length. + uint8_t total_ahs_len; + + /// Data segment length. + uint8_t ds_len[3]; + + /// Reserved for future usage. Always MUST be 0. + uint64_t reserved; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /** + * @brief Copy of the last accepted Selective Negative / Sequence Number Acknowledgment (SNACK) tag. + * + * This field contains a copy of the SNACK Tag of the last SNACK Tag + * accepted by the target on the same connection and for the command for + * which the response is issued. Otherwise, it is reserved and should + * be set to 0.\n + * After issuing a R-Data SNACK, the initiator must discard any SCSI + * status unless contained in a SCSI Response PDU carrying the same + * SNACK Tag as the last issued R-Data SNACK for the SCSI command on the + * current connection. + */ + uint32_t snack_tag; + + /** + * @brief StatSN - Status Sequence Number. + * + * The StatSN is a sequence number that the target iSCSI layer generates + * per connection and that in turn enables the initiator to acknowledge + * status reception. The StatSN is incremented by 1 for every + * response/status sent on a connection, except for responses sent as a + * result of a retry or SNACK. In the case of responses sent due to a + * retransmission request, the StatSN MUST be the same as the first time + * the PDU was sent, unless the connection has since been restarted. + */ + uint32_t stat_sn; + + /** + * @brief ExpCmdSN - Next Expected CmdSN from This Initiator. + * + * The ExpCmdSN is a sequence number that the target iSCSI returns to + * the initiator to acknowledge command reception. It is used to update + * a local variable with the same name. An ExpCmdSN equal to + * MaxCmdSN + 1 indicates that the target cannot accept new commands. + */ + uint32_t exp_cmd_sn; + + /** + * @brief MaxCmdSN - Maximum CmdSN from This Initiator. + * + * The MaxCmdSN is a sequence number that the target iSCSI returns to + * the initiator to indicate the maximum CmdSN the initiator can send. + * It is used to update a local variable with the same name. If the + * MaxCmdSN is equal to ExpCmdSN - 1, this indicates to the initiator + * that the target cannot receive any additional commands. When the + * MaxCmdSN changes at the target while the target has no pending PDUs + * to convey this information to the initiator, it MUST generate a + * NOP-In to carry the new MaxCmdSN. + */ + uint32_t max_cmd_sn; + + /** + * @brief ExpDataSN or Reserved. + * + * This field indicates the number of Data-In (read) PDUs the target has + * sent for the command.\n + * This field MUST be 0 if the response code is not Command Completed at + * Target or the target sent no Data-In PDUs for the command. + */ + uint32_t exp_data_sn; + + /** + * @brief Bidirectional Read Residual Count or Reserved. + * + * The Bidirectional Read Residual Count field MUST be valid in the case + * where either the u bit or the o bit is set. If neither bit is set, + * the Bidirectional Read Residual Count field is reserved. Targets may + * set the Bidirectional Read Residual Count, and initiators may use it + * when the response code is Command Completed at Target. If the o bit + * is set, the Bidirectional Read Residual Count indicates the number of + * bytes that were not transferred to the initiator because the + * initiator's Bidirectional Read Expected Data Transfer Length was not + * sufficient. If the u bit is set, the Bidirectional Read Residual + * Count indicates the number of bytes that were not transferred to the + * initiator out of the number of bytes expected to be transferred. + */ + uint32_t bidi_read_res_cnt; + + /** + * @brief Residual Count or Reserved. + * + * The Residual Count field MUST be valid in the case where either the U + * bit or the O bit is set. If neither bit is set, the Residual Count + * field MUST be ignored on reception and SHOULD be set to 0 when + * sending. Targets may set the residual count, and initiators may use + * it when the response code is Command Completed at Target (even if the + * status returned is not GOOD). If the O bit is set, the Residual + * Count indicates the number of bytes that were not transferred because + * the initiator's Expected Data Transfer Length was not sufficient. If + * the U bit is set, the Residual Count indicates the number of bytes + * that were not transferred out of the number of bytes expected to be + * transferred. + */ + uint32_t res_cnt; + + /// Optional header digest. + iscsi_header_digest hdr_digest; + + /// Optional data segment, command data. + iscsi_scsi_ds_cmd_data ds_cmd_data; + + /// Optional data digest. + iscsi_data_digest data_digest; +} iscsi_scsi_response_packet; + + +/// Task management request function: ABORT TASK: aborts the task identified by the Referenced Task Tag field. +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_ABORT_TASK 0x01 + +/// Task management request function: ABORT TASK SET: aborts all tasks issued via this session on the LU. +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_ABORT_TASK_SET 0x02 + +/// Task management request function: CLEAR ACA - clears the Auto Contingent Allegiance condition. +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_CLEAR_ACA 0x03 + +/// Task management request function: CLEAR TASK SET - aborts all tasks in the appropriate task set as defined by the TST field in the Control mode page (see SPC3). +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_CLEAR_TASK_SET 0x04 + +/// Task management request function: LOGICAL UNIT RESET. +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_LOGICAL_UNIT_RESET 0x05 + +/// Task management request function: TARGET WARM RESET. +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_TARGET_WARM_RESET 0x06 + +/// Task management request function: TARGET COLD RESET. +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_TARGET_COLD_RESET 0x07 + +/// Task management request function: TASK REASSIGN - reassigns connection allegiance for the task identified by the Initiator Task Tag field to this connection, thus resuming the iSCSI exchanges for the task. +#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_TASK_REASSIGN 0x08 + + +/** + * @brief iSCSI Task Management Function Request packet data. + * + * This structure is used to explicity control the execution of one + * or more tasks (iSCSI and SCSI). + */ +typedef struct __attribute__((packed)) iscsi_task_mgmt_func_req_packet { + /// Always 2 according to iSCSI specification. + uint8_t opcode; + + /** + * @brief Function. + * + * The task management functions provide an initiator with a way to + * explicitly control the execution of one or more tasks (SCSI and iSCSI + * tasks). The task management function codes are listed below. For a + * more detailed description of SCSI task management, see SAM2. + */ + int8_t func; + + /// Reserved fot future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength (MUST be 0 for this PDU). + uint8_t total_ahs_len; + + /// DataSegmentLength (MUST be 0 for this PDU). + uint8_t ds_len[3]; + + /** + * @brief Logical Unit Number (LUN) or Reserved. + * + * This field is required for functions that address a specific LU + * (ABORT TASK, CLEAR TASK SET, ABORT TASK SET, CLEAR ACA, LOGICAL UNIT + * RESET) and is reserved in all others + */ + uint64_t lun; + + /** + * @brief Initiator Task Tag (ITT). + * + * This is the Initiator Task Tag of the task to be aborted for the + * ABORT TASK function or reassigned for the TASK REASSIGN function. + * For all the other functions, this field MUST be set to the reserved + * value 0xFFFFFFFF. + */ + uint32_t init_task_tag; + + /// Referenced task tag or 0xFFFFFFFF. + uint32_t ref_task_tag; + + /// CmdSN. + uint32_t cmd_sn; + + /// ExpStatSN + uint32_t exp_stat_sn; + + /** + * @brief RefCmdSN or Reserved. + * + * If an ABORT TASK is issued for a task created by an immediate + * command, then the RefCmdSN MUST be that of the task management + * request itself (i.e., the CmdSN and RefCmdSN are equal).\n + * For an ABORT TASK of a task created by a non-immediate command, the + * RefCmdSN MUST be set to the CmdSN of the task identified by the + * Referenced Task Tag field. Targets must use this field when the task + * identified by the Referenced Task Tag field is not with the target. + * Otherwise, this field is reserved. + */ + uint32_t ref_cmd_sn; + + /** + * @brief ExpDataSN or Reserved. + * + * For recovery purposes, the iSCSI target and initiator maintain a data + * acknowledgment reference number - the first input DataSN number + * unacknowledged by the initiator. When issuing a new command, this + * number is set to 0. If the function is TASK REASSIGN, which + * establishes a new connection allegiance for a previously issued read + * or bidirectional command, the ExpDataSN will contain an updated data + * acknowledgment reference number or the value 0; the latter indicates + * that the data acknowledgment reference number is unchanged. The + * initiator MUST discard any data PDUs from the previous execution that + * it did not acknowledge, and the target MUST transmit all Data-In PDUs + * (if any) starting with the data acknowledgment reference number. The + * number of retransmitted PDUs may or may not be the same as the + * original transmission, depending on if there was a change in + * MaxRecvDataSegmentLength in the reassignment. The target MAY also + * send no more Data-In PDUs if all data has been acknowledged. + * The value of ExpDataSN MUST be 0 or higher than the DataSN of the + * last acknowledged Data-In PDU, but not larger than DataSN + 1 of the + * last Data-IN PDU sent by the target. Any other value MUST be ignored + * by the target. + * For other functions, this field is reserved + */ + uint32_t exp_data_sn; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2; + + /// Optional header digest. + iscsi_header_digest hdr_digest; +} iscsi_task_mgmt_func_req_packet; + + +/// Task management function response: Function complete. +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_COMPLETE 0x00 + +/// Task management function response: Task does not exist. +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_NO_EXIST 0x01 + +/// Task management function response: LUN does not exist. +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_LUN_NO_EXIST 0x02 + +/// Task management function response: Task still allegiant. +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_ALLEGIANT 0x03 + +/// Task management function response: Task allegiance reassignment not supported. +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_UNSUPPORTED_ALLEGIANCE 0x04 + +/// Task management function response: Task management function not supported. +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_UNSUPPORTED_MGMT 0x05 + +/// Task management function response: Function authorization failed. +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_AUTH_FAILED 0x06 + +/// Task management function response: Function rejected. +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_REJECTED 0xFF + + +/** + * @brief iSCSI Task Management Function Response packet data. + * + * For the functions ABORT TASK, ABORT TASK SET, CLEAR ACA, CLEAR TASK + * SET, LOGICAL UNIT RESET, TARGET COLD RESET, TARGET WARM RESET, and + * TASK REASSIGN, the target performs the requested task management + * function and sends a task management response back to the initiator. + * For TASK REASSIGN, the new connection allegiance MUST ONLY become + * effective at the target after the target issues the task management + * response. + */ +typedef struct __attribute__((packed)) iscsi_task_mgmt_func_response_packet { + /// Always 0x22 according to specification. + uint8_t opcode; + + /// Reserved for future usage (always MUST be 0x80 for now). + int8_t flags; + + /** + * @brief Function response. + * + * For the TARGET COLD RESET and TARGET WARM RESET functions, the target + * cancels all pending operations across all LUs known to the issuing + * initiator. For the TARGET COLD RESET function, the target MUST then + * close all of its TCP connections to all initiators (terminates all + * sessions).\n + * The mapping of the response code into a SCSI service response code + * value, if needed, is outside the scope of this document. However, in + * symbolic terms, Response values 0 and 1 map to the SCSI service + * response of FUNCTION COMPLETE. Response value 2 maps to the SCSI + * service response of INCORRECT LOGICAL UNIT NUMBER. All other + * Response values map to the SCSI service response of FUNCTION + * REJECTED. If a Task Management Function Response PDU does not arrive + * before the session is terminated, the SCSI service response is + * SERVICE DELIVERY OR TARGET FAILURE.\n + * The response to ABORT TASK SET and CLEAR TASK SET MUST only be issued + * by the target after all of the commands affected have been received + * by the target, the corresponding task management functions have been + * executed by the SCSI target, and the delivery of all responses + * delivered until the task management function completion has been + * confirmed (acknowledged through the ExpStatSN) by the initiator on + * all connections of this session.\n + * For the ABORT TASK function,\n + * -# if the Referenced Task Tag identifies a valid task leading to a + * successful termination, then targets must return the "Function + * complete" response. + * -# if the Referenced Task Tag does not identify an existing task + * but the CmdSN indicated by the RefCmdSN field in the Task + * Management Function Request is within the valid CmdSN window + * and less than the CmdSN of the Task Management Function Request + * itself, then targets must consider the CmdSN as received and + * return the "Function complete" response. + * -# if the Referenced Task Tag does not identify an existing task + * and the CmdSN indicated by the RefCmdSN field in the Task + * Management Function Request is outside the valid CmdSN window, + * then targets must return the "Task does not exist" response + */ + uint8_t response; + + /// Reserved for future usage, always MUST be 0. + uint8_t reserved; + + /// TotalAHSLength (MUST be 0 for this PDU). + uint8_t total_ahs_len; + + /// DataSegmentLength (MUST be 0 for this PDU). + uint8_t ds_len[3]; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved3; + + /// StatSN. + uint32_t stat_sn; + + /// ExpCmdSN. + uint32_t exp_cmd_sn; + + /// MaxCmdSN. + uint32_t max_cmd_sn; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved4; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved5; + + /// Optional header digest. + iscsi_header_digest hdr_digest; +} iscsi_task_mgmt_func_response_packet; + +/// SCSI data out / in flags: Immediately process transfer. +#define ISCSI_SCSI_DATA_OUT_DATA_IN_FLAGS_IMMEDIATE (1 << 7) + +/** + * @brief iSCSI SCSI Data Out request packet data. + * + * THis structure is used by iSCSI for SCSI data output + * requests, i.e. write operations. + */ +typedef struct __attribute__((packed)) iscsi_scsi_data_out_req_packet { + /// Always 2 according to iSCSI specification. + uint8_t opcode; + + /// Flags. + int8_t flags; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /** + * @brief DataSegmentLength. + * + * This is the data payload length of a SCSI Data-In or SCSI Data-Out + * PDU. The sending of 0-length data segments should be avoided, but + * initiators and targets MUST be able to properly receive 0-length data + * segments.\n + * The data segments of Data-In and Data-Out PDUs SHOULD be filled to + * the integer number of 4-byte words (real payload), unless the F bit + * is set to 1. + */ + uint8_t ds_len[3]; + + /** + * @brief Logical Unit Number (LUN) or Reserved. + * + * If the Target Transfer Tag is provided, then the LUN field MUST hold a + * valid value and be consistent with whatever was specified with the command; + * otherwise, the LUN field is reserved. + */ + uint64_t lun; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /** + * @brief Target Transfer Tag or 0xFFFFFFFF. + * + * On outgoing data, the Target Transfer Tag is provided to the target + * if the transfer is honoring an R2T. In this case, the Target + * Transfer Tag field is a replica of the Target Transfer Tag provided + * with the R2T.\n + * The Target Transfer Tag values are not specified by this protocol, + * except that the value 0xFFFFFFFF is reserved and means that the + * Target Transfer Tag is not supplied. + */ + uint32_t target_xfer_tag; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved2; + + /// ExpStatSN. + uint32_t exp_stat_sn; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved3; + + /** + * @brief DataSN. + * + * For output (write) data PDUs, the DataSN is the Data-Out PDU number + * within the current output sequence. Either the current output + * sequence is identified by the Initiator Task Tag (for unsolicited + * data) or it is a data sequence generated for one R2T (for data + * solicited through R2T). + */ + uint32_t data_sn; + + /** + * @brief Buffer Offset. + * + * The Buffer Offset field contains the offset of this PDU payload data + * within the complete data transfer. The sum of the buffer offset and + * length should not exceed the expected transfer length for the + * command.\n + * The order of data PDUs within a sequence is determined by + * DataPDUInOrder. When set to Yes, it means that PDUs have to be in + * increasing buffer offset order and overlays are forbidden.\n + * The ordering between sequences is determined by DataSequenceInOrder. + * When set to Yes, it means that sequences have to be in increasing + * buffer offset order and overlays are forbidden. + */ + uint32_t buf_offset; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved4; + + /// Optional header digest. + iscsi_header_digest hdr_digest; + + /// Data segment. + iscsi_scsi_ds_cmd_data ds_cmd_data; + + /// Optional data digest. + iscsi_data_digest data_digest; +} iscsi_scsi_data_out_req_packet; + +/** + * @brief SCSI Data In reponse flags: Status. + * + * (S) set to indicate that the Command Status field + * contains status. If this bit is set to 1, the + * F bit MUST also be set to 1. + */ +#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS (1 << 0) + +/** + * @brief SCSI Data In reponse flags: Residual Underflow. + * + * (U) set for Residual Underflow. In this case, the Residual + * Count indicates the number of bytes that were not + * transferred out of the number of bytes that were expected + * to be transferred. For a bidirectional operation, the + * Residual Count contains the residual for the write + * operation. + */ +#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_UNDERFLOW (1 << 1) + +/** + * @brief SCSI Data In reponse flags: Residual Overflow. + * + * (O) set for Residual Overflow. In this case, the Residual + * Count indicates the number of bytes that were not + * transferred because the initiator's Expected Data + * Transfer Length was not sufficient. For a bidirectional + * operation, the Residual Count contains the residual for + * the write operation. + */ +#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_OVERFLOW (1 << 2) + +/** + * @brief SCSI Data In reponse flags: ACK. + * + * (A) for sessions with ErrorRecoveryLevel=1 or higher, the target sets + * this bit to 1 to indicate that it requests a positive acknowledgment + * from the initiator for the data received. The target should use the + * A bit moderately; it MAY only set the A bit to 1 once every + * MaxBurstLength bytes, or on the last Data-In PDU that concludes the + * entire requested read data transfer for the task from the target's + * perspective, and it MUST NOT do so more frequently. The target MUST + * NOT set to 1 the A bit for sessions with ErrorRecoveryLevel=0. The + * initiator MUST ignore the A bit set to 1 for sessions with + * ErrorRecoveryLevel=0.\n + * On receiving a Data-In PDU with the A bit set to 1 on a session with + * ErrorRecoveryLevel greater than 0, if there are no holes in the read + * data until that Data-In PDU, the initiator MUST issue a SNACK of type + * DataACK, except when it is able to acknowledge the status for the + * task immediately via the ExpStatSN on other outbound PDUs if the + * status for the task is also received. In the latter case + * (acknowledgment through the ExpStatSN), sending a SNACK of type + * DataACK in response to the A bit is OPTIONAL, but if it is done, it + * must not be sent after the status acknowledgment through the + * ExpStatSN. If the initiator has detected holes in the read data + * prior to that Data-In PDU, it MUST postpone issuing the SNACK of type + * DataACK until the holes are filled. An initiator also MUST NOT + * acknowledge the status for the task before those holes are filled. A + * status acknowledgment for a task that generated the Data-In PDUs is + * considered by the target as an implicit acknowledgment of the Data-In + * PDUs if such an acknowledgment was requested by the target. + */ +#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_ACK (1 << 6) + +/** + * @brief SCSI Data In reponse flags: Final. + * + * (F) for outgoing data, this bit is 1 for the last PDU of unsolicited + * data or the last PDU of a sequence that answers an R2T. + * For incoming data, this bit is 1 for the last input (read) data PDU + * of a sequence. Input can be split into several sequences, each + * having its own F bit. Splitting the data stream into sequences does + * not affect DataSN counting on Data-In PDUs. It MAY be used as a + * "change direction" indication for bidirectional operations that need + * such a change.\n + * DataSegmentLength MUST NOT exceed MaxRecvDataSegmentLength for the + * direction it is sent, and the total of all the DataSegmentLength of + * all PDUs in a sequence MUST NOT exceed MaxBurstLength (or + * FirstBurstLength for unsolicited data). However, the number of + * individual PDUs in a sequence (or in total) may be higher than the + * ratio of MaxBurstLength (or FirstBurstLength) to + * MaxRecvDataSegmentLength (as PDUs may be limited in length by the + * capabilities of the sender). Using a DataSegmentLength of 0 may + * increase beyond what is reasonable for the number of PDUs and should + * therefore be avoided.\n + * For bidirectional operations, the F bit is 1 for both the end of the + * input sequences and the end of the output sequences + */ +#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_FINAL (1 << 7) + +/** + * @brief iSCSI SCSI Data In response packet data. + * + * THis structure is used by iSCSI for SCSI data input + * responses, i.e. read operations. + */ +typedef struct __attribute__((packed)) iscsi_scsi_data_in_response_packet { + /// Always 0x25 according to iSCSI specification. + uint8_t opcode; + + /// Incoming data flags. The fields StatSN, Status, and Residual Count only have meaningful content if the S bit is set to 1. + int8_t flags; + + /// Rserved for future usage, always MUST be 0. + uint8_t reserved; + + /** + * @brief Status or Reserved. + * + * Status can accompany the last Data-In PDU if the command did not end + * with an exception (i.e., the status is "good status" - GOOD, + * CONDITION MET, or INTERMEDIATE-CONDITION MET). The presence of + * status (and of a residual count) is signaled via the S flag bit. + * Although targets MAY choose to send even non-exception status in + * separate responses, initiators MUST support non-exception status in + * Data-In PDUs. + */ + uint8_t status; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /** + * @brief DataSegmentLength. + * + * This is the data payload length of a SCSI Data-In or SCSI Data-Out + * PDU. The sending of 0-length data segments should be avoided, but + * initiators and targets MUST be able to properly receive 0-length data + * segments.\n + * The data segments of Data-In and Data-Out PDUs SHOULD be filled to + * the integer number of 4-byte words (real payload), unless the F bit + * is set to 1. + */ + uint8_t ds_len[3]; + + /** + * @brief Logical Unit Number (LUN) or Reserved. + * + * If the Target Transfer Tag is provided, then the LUN field MUST hold a + * valid value and be consistent with whatever was specified with the command; + * otherwise, the LUN field is reserved. + */ + uint64_t lun; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /** + * @brief Target Transfer Tag or 0xFFFFFFFF. + * + * On incoming data, the Target Transfer Tag and LUN MUST be provided by + * the target if the A bit is set to 1; otherwise, they are reserved. + * The Target Transfer Tag and LUN are copied by the initiator into the + * SNACK of type DataACK that it issues as a result of receiving a SCSI + * Data-In PDU with the A bit set to 1.\n + * The Target Transfer Tag values are not specified by this protocol, + * except that the value 0xFFFFFFFF is reserved and means that the + * Target Transfer Tag is not supplied. + */ + uint32_t target_xfer_tag; + + /// StatSN. + uint32_t stat_sn; + + /// ExpCmdSN. + + uint32_t exp_cmd_sn; + + /// MaxCmdSN. + uint32_t max_cmd_sn; + + /** + * @brief DataSN. + * + * For input (read) or bidirectional Data-In PDUs, the DataSN is the + * input PDU number within the data transfer for the command identified + * by the Initiator Task Tag.\n + * R2T and Data-In PDUs, in the context of bidirectional commands, share + * the numbering sequence. + */ + uint32_t data_sn; + + /** + * @brief Buffer Offset. + * + * The Buffer Offset field contains the offset of this PDU payload data + * within the complete data transfer. The sum of the buffer offset and + * length should not exceed the expected transfer length for the + * command.\n + * The order of data PDUs within a sequence is determined by + * DataPDUInOrder. When set to Yes, it means that PDUs have to be in + * increasing buffer offset order and overlays are forbidden.\n + * The ordering between sequences is determined by DataSequenceInOrder. + * When set to Yes, it means that sequences have to be in increasing + * buffer offset order and overlays are forbidden. + */ + uint32_t buf_offset; + + /// Residual Count or Reserved. + uint32_t res_cnt; + + /// Optional header digest. + iscsi_header_digest hdr_digest; + + /// Data segment. + iscsi_scsi_ds_cmd_data ds_cmd_data; + + /// Optional data digest. + iscsi_data_digest data_digest; +} iscsi_scsi_data_in_response_packet; + +/** + * @brief iSCSI Ready To Transfer packet data. + * + * When an initiator has submitted a SCSI command with data that passes + * from the initiator to the target (write), the target may specify + * which blocks of data it is ready to receive. The target may request + * that the data blocks be delivered in whichever order is convenient + * for the target at that particular instant. This information is + * passed from the target to the initiator in the Ready To Transfer + * (R2T) PDU. + * + * In order to allow write operations without an explicit initial R2T, + * the initiator and target MUST have negotiated the key InitialR2T to + * No during login. + * + * An R2T MAY be answered with one or more SCSI Data-Out PDUs with a + * matching Target Transfer Tag. If an R2T is answered with a single + * Data-Out PDU, the buffer offset in the data PDU MUST be the same as + * the one specified by the R2T, and the data length of the data PDU + * MUST be the same as the Desired Data Transfer Length specified in the + * R2T. If the R2T is answered with a sequence of data PDUs, the buffer + * offset and length MUST be within the range of those specified by the + * R2T, and the last PDU MUST have the F bit set to 1. If the last PDU + * (marked with the F bit) is received before the Desired Data Transfer + * Length is transferred, a target MAY choose to reject that PDU with + * the "Protocol Error" reason code. DataPDUInOrder governs the + * Data-Out PDU ordering. If DataPDUInOrder is set to Yes, the buffer + * offsets and lengths for consecutive PDUs MUST form a continuous + * non-overlapping range, and the PDUs MUST be sent in increasing offset + * order. + * + * The target may send several R2T PDUs. It therefore can have a number + * of pending data transfers. The number of outstanding R2T PDUs is + * limited by the value of the negotiated key MaxOutstandingR2T. Within + * a task, outstanding R2Ts MUST be fulfilled by the initiator in the + * order in which they were received. + * + * R2T PDUs MAY also be used to recover Data-Out PDUs. Such an R2T + * (Recovery-R2T) is generated by a target upon detecting the loss of + * one or more Data-Out PDUs due to: + * + * - Digest error + * + * - Sequence error + * + * - Sequence reception timeout + * + * A Recovery-R2T carries the next unused R2TSN but requests part of or + * the entire data burst that an earlier R2T (with a lower R2TSN) had + * already requested. + * + * DataSequenceInOrder governs the buffer offset ordering in consecutive + * R2Ts. If DataSequenceInOrder is Yes, then consecutive R2Ts MUST + * refer to continuous non-overlapping ranges, except for Recovery-R2Ts. + */ +typedef struct __attribute__((packed)) iscsi_r2t_packet { + /// Always 0x31 according to iSCSI specification. + uint8_t opcode; + + /// Reserved for future usage (always MUST be 0x80 for now). + int8_t flags; + + /// Reserved for future usage, always MUST be 0 for now. + uint16_t reserved; + + /// TotalAHSLength, MUST be 0 for this PDU. + uint8_t total_ahs_len; + + /// DataSegmentLength, MUST be 0 0 for this PDU. + uint8_t ds_len[3]; + + /// Logical Unit Number (LUN) or Reserved. + uint64_t lun; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /// Target Transfer Tag (TTT). + uint32_t target_xfer_tag; + + /// The StatSN field will contain the next StatSN. The StatSN for this connection is not advanced after this PDU is sent. + uint32_t stat_sn; + + /// ExpCmdSN. + uint32_t exp_cmd_sn; + + /// MaxCmdSN. + uint32_t max_cmd_sn; + + /// DataSN. + uint32_t data_sn; + + /// Ready To Transfer Sequence Number (R2TSN) is the R2T PDU input PDU number within the command identified by the Initiator Task Tag. For bidirectional commands, R2T and Data-In PDUs share the input PDU numbering sequence. + uint32_t r2t_sn; + + /** + * @brief Buffer Offset. + * + * The target therefore also specifies a buffer offset that indicates + * the point at which the data transfer should begin, relative to the + * beginning of the total data transfer. + */ + uint32_t buf_offset; + + /** + * @brief Desired Data Transfer Length. + * + * The target specifies how many bytes it wants the initiator to send + * because of this R2T PDU. The target may request the data from the + * initiator in several chunks, not necessarily in the original order of + * the data. The Desired Data Transfer Length MUST NOT be 0 and MUST NOT + * exceed MaxBurstLength. + */ + uint32_t des_data_xfer_len; + + /// Optional header digest. + iscsi_header_digest hdr_digest; +} iscsi_r2t_packet; + + +/** + * @brief SCSI Asynchronous Message Event: SCSI Async Event. + * + * A SCSI asynchronous event is reported in the sense data. + * Sense Data that accompanies the report, in the data + * segment, identifies the condition. The sending of a + * SCSI event ("asynchronous event reporting" in SCSI + * terminology) is dependent on the target support for SCSI + * asynchronous event reporting as indicated in the + * standard INQUIRY data. Its use may be enabled by + * parameters in the SCSI Control mode page. + */ +#define ISCSI_ASYNC_MSG_EVENT_SCSI_ASYNC_EVENT 0x00 + +/** + * @brief SCSI Asynchronous Message Event: Logout Request. + * + * The target requests Logout. This Async Message MUST + * be sent on the same connection as the one requesting + * to be logged out. The initiator MUST honor this request + * by issuing a Logout as early as possible but no later + * than Parameter3 seconds. The initiator MUST send a Logout + * with a reason code of "close the connection" OR "close the + * session" to close all the connections. Once this message is + * received, the initiator SHOULD NOT issue new iSCSI commands on + * the connection to be logged out. The target MAY reject any + * new I/O requests that it receives after this message with the + * reason code "Waiting for Logout". If the initiator does not + * log out in Parameter3 seconds, the target should send an Async + * PDU with iSCSI event code "Dropped the connection" if possible + * or simply terminate the transport connection. Parameter1 and + * Parameter2 are reserved. + */ +#define ISCSI_ASYNC_MSG_EVENT_LOGOUT_REQUEST 0x01 + +/** + * @brief SCSI Asynchronous Message Event: Connection Drop Notification. + * + * The target indicates that it will drop the connection. + * The Parameter1 field indicates the CID of the connection that + * is going to be dropped.\n + * The Parameter2 field (Time2Wait) indicates, in seconds, the + * minimum time to wait before attempting to reconnect or + * reassign.\n + * The Parameter3 field (Time2Retain) indicates the maximum time + * allowed to reassign commands after the initial wait (in + * Parameter2).\n + * If the initiator does not attempt to reconnect and/or reassign + * the outstanding commands within the time specified by + * Parameter3, or if Parameter3 is 0, the target will terminate + * all outstanding commands on this connection. In this case, no + * other responses should be expected from the target for the + * outstanding commands on this connection.\n + * A value of 0 for Parameter2 indicates that reconnect can be + * attempted immediately. + */ +#define ISCSI_ASYNC_MSG_EVENT_CONNECT_DROP_NOTIFY 0x02 + +/** + * @brief SCSI Asynchronous Message Event: Session Drop Notification. + * + * The target indicates that it will drop all the connections + * of this session.\n + * The Parameter1 field is reserved.\n + * The Parameter2 field (Time2Wait) indicates, in seconds, the + * minimum time to wait before attempting to reconnect.\n + * The Parameter3 field (Time2Retain) indicates the maximum time + * allowed to reassign commands after the initial wait (in + * Parameter2).\n + * If the initiator does not attempt to reconnect and/or reassign + * the outstanding commands within the time specified by + * Parameter3, or if Parameter3 is 0, the session is terminated.\n + * In this case, the target will terminate all outstanding + * commands in this session; no other responses should be + * expected from the target for the outstanding commands in this + * session. A value of 0 for Parameter2 indicates that reconnect + * can be attempted immediately. + */ +#define ISCSI_ASYNC_MSG_EVENT_SESSION_DROP_NOTIFY 0x03 + +/** + * @brief SCSI Asynchronous Message Event: Negotiation Request. + * + * The target requests parameter negotiation on this connection. + * The initiator MUST honor this request by issuing a Text + * Request (that can be empty) on the same connection as early + * as possible, but no later than Parameter3 seconds, unless a + * Text Request is already pending on the connection, or by + * issuing a Logout Request. If the initiator does not issue a + * Text Request, the target may reissue the Asynchronous Message + * requesting parameter negotiation. + */ +#define ISCSI_ASYNC_MSG_EVENT_NEGOTIATION_REQUEST 0x04 + +/** + * @brief SCSI Asynchronous Message Event: Task Termination. + * + * All active tasks for a LU with a matching LUN field in the + * Async Message PDU are being terminated. The receiving + * initiator iSCSI layer MUST respond to this message by + * taking the following steps, in order: + * - Stop Data-Out transfers on that connection for all active + * TTTs for the affected LUN quoted in the Async Message PDU. + * - Acknowledge the StatSN of the Async Message PDU via a + * NOP-Out PDU with ITT=0xFFFFFFFF (i.e., non-ping flavor), + * while copying the LUN field from the Async Message to + * NOP-Out. + * This value of AsyncEvent, however, MUST NOT be used on an + * iSCSI session unless the new TaskReporting text key was + * negotiated to FastAbort on the session. + */ +#define ISCSI_ASYNC_MSG_EVENT_TASK_TERMINATION 0x05 + +/// SCSI Asynchronous Message Event: First vendor-specific iSCSI event. The AsyncVCode details the vendor code, and data MAY accompany the report. +#define ISCSI_ASYNC_MSG_EVENT_VENDOR_FIRST 0xF8 + +/// SCSI Asynchronous Message Event: Last vendor-specific iSCSI event. The AsyncVCode details the vendor code, and data MAY accompany the report. +#define ISCSI_ASYNC_MSG_EVENT_VENDOR_LAST 0xFF + +/** + * @brief iSCSI Asynchronous Message packet data. + * + * An Asynchronous Message may be sent from the target to the initiator + * without corresponding to a particular command. The target specifies + * the reason for the event and sense data.\n + * Some Asynchronous Messages are strictly related to iSCSI, while + * others are related to SCSI + */ +typedef struct __attribute__((packed)) iscsi_async_msg_packet { + /// Always 0x32 according to iSCSI specification. + uint8_t opcode; + + /// Reserved for future usage (always MUST be 0x80 for now). + int8_t flags; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength, MUST be 0 for this PDU. + uint8_t total_ahs_len; + + /// DataSegmentLength, MUST be 0 0 for this PDU. + uint8_t ds_len[3]; + + /// The LUN field MUST be valid if AsyncEvent is 0. Otherwise, this field is reserved. + uint64_t lun; + + /// Tag (always 0xFFFFFFFF for now). + uint32_t tag; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved2; + + /** + * @brief StatSN. + * + * The StatSN counts this PDU as an acknowledgeable event (the StatSN is + * advanced), which allows for initiator and target state synchronization. + */ + uint32_t stat_sn; + + /// ExpCmdSN. + uint32_t exp_cmd_sn; + + /// MaxCmdSN. + uint32_t max_cmd_sn; + + /// AsyncEvent. + uint8_t async_event; + + /// AsyncVCode is a vendor-specific detail code that is only valid if the AsyncEvent field indicates a vendor-specific event. Otherwise, it is reserved. + uint8_t async_vcode; + + /// Parameter1 or Reserved. + uint16_t param_1; + + /// Parameter2 or Reserved. + uint16_t param_2; + + /// Parameter3 or Reserved. + uint16_t param_3; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved3; + + /// Optional header digest. + iscsi_header_digest hdr_digest; + + /// Data segment. + iscsi_scsi_ds_cmd_data ds_cmd_data; + + /// Optional data digest. + iscsi_data_digest data_digest; +} iscsi_async_msg_packet; + + +/** + * @brief Text Request flags: Continue. + * + * (C) When set to 1, this bit indicates that the text (set of key=value + * pairs) in this Text Request is not complete (it will be continued on + * subsequent Text Requests); otherwise, it indicates that this Text + * Request ends a set of key=value pairs. A Text Request with the C bit + * set to 1 MUST have the F bit set to 0. + */ +#define ISCSI_TEXT_REQ_FLAGS_CONTINUE (1 << 6) + +/** + * @brief Text Request flags: Final. + * + * (F) When set to 1, this bit indicates that this is the last or only Text + * Request in a sequence of Text Requests; otherwise, it indicates that + * more Text Requests will follow. + */ +#define ISCSI_TEXT_REQ_FLAGS_FINAL (1 << 7) + +/** + * @brief iSCSI Text Request packet data. + * + * The Text Request is provided to allow for the exchange of information + * and for future extensions. It permits the initiator to inform a + * target of its capabilities or request some special operations. + * + * An initiator MUST NOT have more than one outstanding Text Request on + * a connection at any given time. + * + * On a connection failure, an initiator must either explicitly abort + * any active allegiant text negotiation task or cause such a task to be + * implicitly terminated by the target. + */ +typedef struct __attribute__((packed)) iscsi_text_req_packet { + /// Always 0x04 according to iSCSI specification. + uint8_t opcode; + + /// Text request flags. + int8_t flags; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// Logical Unit Number (LUN) or Reserved. + uint64_t lun; + + /** + * @brief Initiator Task Tag (ITT). + * + * This is the initiator-assigned identifier for this Text Request. If + * the command is sent as part of a sequence of Text Requests and + * responses, the Initiator Task Tag MUST be the same for all the + * requests within the sequence (similar to linked SCSI commands). The + * I bit for all requests in a sequence also MUST be the same. + */ + uint32_t init_task_tag; + + /** + * @brief Target Transfer Tag (TTT). + * + * When the Target Transfer Tag is set to the reserved value 0xFFFFFFFF, + * it tells the target that this is a new request, and the target resets + * any internal state associated with the Initiator Task Tag (resets the + * current negotiation state).\n + * The target sets the Target Transfer Tag in a Text Response to a value + * other than the reserved value 0xFFFFFFFF whenever it indicates that + * it has more data to send or more operations to perform that are + * associated with the specified Initiator Task Tag. It MUST do so + * whenever it sets the F bit to 0 in the response. By copying the + * Target Transfer Tag from the response to the next Text Request, the + * initiator tells the target to continue the operation for the specific + * Initiator Task Tag. The initiator MUST ignore the Target Transfer + * Tag in the Text Response when the F bit is set to 1.\n + * This mechanism allows the initiator and target to transfer a large + * amount of textual data over a sequence of text-command/text-response + * exchanges or to perform extended negotiation sequences.\n + * If the Target Transfer Tag is not 0xFFFFFFFF, the LUN field MUST be + * sent by the target in the Text Response.\n + * A target MAY reset its internal negotiation state if an exchange is + * stalled by the initiator for a long time or if it is running out of + * resources.\n + * Long Text Responses are handled as shown in the following example:\n + * @verbatim + * I->T Text SendTargets=All (F = 1, TTT = 0xFFFFFFFF) + * T->I Text (F = 0, TTT = 0x12345678) + * I->T Text (F = 1, TTT = 0x12345678) + * T->I Text (F = 0, TTT = 0x12345678) + * I->T Text (F = 1, TTT = 0x12345678) + * ... + * T->I Text (F = 1, TTT = 0xFFFFFFFF) + * @endverbatim + */ + uint32_t target_xfer_tag; + + /// CmdSN. + uint32_t cmd_sn; + + /// ExpStatSN. + uint32_t exp_stat_sn; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2[2]; + + /// Optional header digest. + iscsi_header_digest hdr_digest; + + /** + * @brief Data segment. + * + * The data lengths of a Text Request MUST NOT exceed the iSCSI target + * MaxRecvDataSegmentLength (a parameter that is negotiated per + * connection and per direction).\n + * A key=value pair can span Text Request or Text Response boundaries. + * A key=value pair can start in one PDU and continue on the next. In + * other words, the end of a PDU does not necessarily signal the end of + * a key=value pair.\n + * The target responds by sending its response back to the initiator. + * The response text format is similar to the request text format. The + * Text Response MAY refer to key=value pairs presented in an earlier + * Text Request, and the text in the request may refer to earlier + * responses.\n + * Text operations are usually meant for parameter setting/negotiations + * but can also be used to perform some long-lasting operations. + */ + iscsi_scsi_ds_cmd_data ds_cmd_data; + + /// Optional data digest. + iscsi_data_digest data_digest; +} iscsi_text_req_packet; + + +/** + * @brief Text Response flags: Continue. + * + * (C) When set to 1, this bit indicates that the text (set of key=value + * pairs) in this Text Response is not complete (it will be continued on + * subsequent Text Responses); otherwise, it indicates that this Text + * Response ends a set of key=value pairs. A Text Response with the + * C bit set to 1 MUST have the F bit set to 0. + */ +#define ISCSI_TEXT_RESPONSE_FLAGS_CONTINUE (1 << 6) + +/** + * @brief Text Response flags: Final. + * + * (F) When set to 1, in response to a Text Request with the Final bit set + * to 1, the F bit indicates that the target has finished the whole + * operation. Otherwise, if set to 0 in response to a Text Request with + * the Final Bit set to 1, it indicates that the target has more work to + * do (invites a follow-on Text Request). A Text Response with the + * F bit set to 1 in response to a Text Request with the F bit set to 0 + * is a protocol error.\n + * A Text Response with the F bit set to 1 MUST NOT contain key=value + * pairs that may require additional answers from the initiator. + * A Text Response with the F bit set to 1 MUST have a Target Transfer + * Tag field set to the reserved value 0xFFFFFFFF.\n + * A Text Response with the F bit set to 0 MUST have a Target Transfer + * Tag field set to a value other than the reserved value 0xFFFFFFFF. + */ +#define ISCSI_TEXT_RESPONSE_FLAGS_FINAL (1 << 7) + +/** + * @brief iSCSI Text Response packet data. + * + * The Text Response PDU contains the target's responses to the + * initiator's Text Request. The format of the Text field matches that + * of the Text Request. + */ +typedef struct __attribute__((packed)) iscsi_text_response_packet { + /// Always 0x24 according to iSCSI specification. + uint8_t opcode; + + /// Text response flags. + int8_t flags; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// Logical Unit Number (LUN) or Reserved. + uint64_t lun; + + /// The Initiator Task Tag matches the tag used in the initial Text Request. + uint32_t init_task_tag; + + /** + * @brief Target Transfer Tag (TTT). + * + * When a target has more work to do (e.g., cannot transfer all the + * remaining text data in a single Text Response or has to continue the + * negotiation) and has enough resources to proceed, it MUST set the + * Target Transfer Tag to a value other than the reserved value + * 0xFFFFFFFF. Otherwise, the Target Transfer Tag MUST be set to + * 0xFFFFFFFF.\n + * When the Target Transfer Tag is not 0xFFFFFFFF, the LUN field may be + * significant.\n + * The initiator MUST copy the Target Transfer Tag and LUN in its next + * request to indicate that it wants the rest of the data.\n + * When the target receives a Text Request with the Target Transfer Tag + * set to the reserved value 0xFFFFFFFF, it resets its internal + * information (resets state) associated with the given Initiator Task + * Tag (restarts the negotiation).\n + * When a target cannot finish the operation in a single Text Response + * and does not have enough resources to continue, it rejects the Text + * Request with the appropriate Reject code.\n + * A target may reset its internal state associated with an Initiator + * Task Tag (the current negotiation state) as expressed through the + * Target Transfer Tag if the initiator fails to continue the exchange + * for some time. The target may reject subsequent Text Requests with + * the Target Transfer Tag set to the "stale" value. + */ + uint32_t target_xfer_tag; + + /// StatSN. The target StatSN variable is advanced by each Text Response sent. + uint32_t stat_sn; + + /// ExpCmdSN. + uint32_t exp_cmd_sn; + + /// MaxCmdSN. + uint32_t max_cmd_sn; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2[2]; + + /// Optional header digest. + iscsi_header_digest hdr_digest; + + /** + * @brief Data segment. + * + * The data lengths of a Text Response MUST NOT exceed the iSCSI + * initiator MaxRecvDataSegmentLength (a parameter that is negotiated + * per connection and per direction).\n + * The text in the Text Response Data is governed by the same rules as + * the text in the Text Request Data.\n + * Although the initiator is the requesting party and controls the + * request-response initiation and termination, the target can offer + * key=value pairs of its own as part of a sequence and not only in + * response to the initiator. + */ + iscsi_scsi_ds_cmd_data ds_cmd_data; + + /// Optional data digest. + iscsi_data_digest data_digest; +} iscsi_text_response_packet; + + +/** + * @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 + +/** + * @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 + +/** + * @brief iSCSI Initiator Session ID (ISID) type: Random. + * + * A: Reserved + * B and C: Random + * D: Qualifier + */ +#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. + * + * This is an initiator-defined component of the session identifier and + * is structured as follows: + * + * For the T field values 00b and 01b, a combination of A and B (for + * 00b) or B and C (for 01b) identifies the vendor or organization whose + * component (software or hardware) generates this ISID. A vendor or + * organization with one or more OUIs, or one or more Enterprise + * Numbers, MUST use at least one of these numbers and select the + * appropriate value for the T field when its components generate ISIDs. + * An OUI or EN MUST be set in the corresponding fields in network byte + * order (byte big-endian). + * + * If the T field is 10b, B and C are set to a random 24-bit unsigned + * integer value in network byte order (byte big-endian). + * + * The Qualifier field is a 16-bit or 24-bit unsigned integer value that + * provides a range of possible values for the ISID within the selected + * namespace. It may be set to any value within the constraints + * specified in the iSCSI protocol. + * + * If the ISID is derived from something assigned to a hardware adapter + * or interface by a vendor as a preset default value, it MUST be + * configurable to a value assigned according to the SCSI port behavior + * desired by the system in which it is installed. The resultant ISID + * MUST also be persistent over power cycles, reboot, card swap, etc. + */ +typedef struct __attribute__((packed)) iscsi_isid { + /// Meaning depends on T bit, either 22-bit OUI or reserved. + uint8_t a; + + /// Meaning depends on T bit, either 22-bit OUI, EN (IANA Enterprise Number) or random. + uint16_t b; + + /// Meaning depends on T bit, either 24-bit Qualifier, EN (IANA Enterprise Number) or random. + uint8_t c; + + /// Meaning depends on T bit, either 24-bit Qualifier or Qualifier. + uint16_t d; +} iscsi_isid; + + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Session type. + * + * @verbatim + * Use: LO, Declarative, Any-Stage + * Senders: Initiator + * Scope: SW + * SessionType= + * Default is Normal. + * @endverbatim + * The initiator indicates the type of session it wants to create. The + * target can either accept it or reject it.\n + * A Discovery session indicates to the target that the only purpose of + * this session is discovery. The only requests a target accepts in + * this type of session are a Text Request with a SendTargets key and a + * Logout Request with reason "close the session".\n + * The Discovery session implies MaxConnections = 1 and overrides both + * the default and an explicit setting. ErrorRecoveryLevel MUST be 0 + * (zero) for Discovery sessions.\n + * Depending on the type of session, a target may decide on resources to + * allocate, the security to enforce, etc., for the session. If the + * 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 ((const uint8_t *) "SessionType\0\0\0\0") + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Initiator name. + * + * @verbatim + * Use: IO, Declarative, Any-Stage + * Senders: Initiator + * Scope: SW + * InitiatorName= + * Examples: + * InitiatorName=iqn.1992-04.de.uni-freiburg.bwlehrpool:qcow2.5003 + * InitiatorName=iqn.2001-02.de.uni-freiburg.matrix:basty.eduroam + * InitiatorName=naa.52004567BA64678D + * @endverbatim + * The initiator of the TCP connection MUST provide this key to the + * remote endpoint at the first login of the Login Phase for every + * connection. The InitiatorName key enables the initiator to identify + * 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 ((const uint8_t *) "InitiatorName\0\0") + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Target name. + * + * @verbatim + * Use: IO by initiator, FFPO by target - only as response to a + * SendTargets, Declarative, Any-Stage + * Senders: Initiator and target + * Scope: SW + * TargetName= + * Examples: + * TargetName=iqn.1993-11.de.uni-freiburg:diskarrays.sn.5003 + * TargetName=eui.020000023B040506 + * TargetName=naa.62004567BA64678D0123456789ABCDEF + * @endverbatim + * The initiator of the TCP connection MUST provide this key to the + * remote endpoint in the first Login Request if the initiator is not + * establishing a Discovery session. The iSCSI Target Name specifies + * the worldwide unique name of the target.\n + * The TargetName key may also be returned by the SendTargets Text + * 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 ((const uint8_t *) "TargetName\0\0\0\0\0") + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Target address. + * + * @verbatim + * Use: ALL, Declarative, Any-Stage + * Senders: Target + * Scope: SW + * TargetAddress=domainname[:port][,portal-group-tag] + * @endverbatim + * The domainname can be specified as either a DNS host name, a dotted- + * decimal IPv4 address, or a bracketed IPv6 address as specified in + * RFC3986.\n + * If the TCP port is not specified, it is assumed to be the IANA- + * assigned default port for iSCSI.\n + * If the TargetAddress is returned as the result of a redirect status + * in a Login Response, the comma and portal-group-tag MUST be omitted. + * If the TargetAddress is returned within a SendTargets response, the + * portal-group-tag MUST be included.\n + * @verbatim + * Examples: + * TargetAddress=10.0.0.1:5003,1 + * TargetAddress=[1080:0:0:0:8:800:200C:417A],65 + * TargetAddress=[1080::8:800:200C:417A]:5003,1 + * TargetAddress=gitlab.uni-freiburg.de,443 + * @endverbatim + * 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 ((const uint8_t *) "TargetAddress\0\0") + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Initiator alias. + * + * @verbatim + * Use: ALL, Declarative, Any-Stage + * Senders: Initiator + * Scope: SW + * InitiatorAlias= + * Examples: + * InitiatorAlias=Web Server 5 + * InitiatorAlias=matrix.uni-freiburg.de + * InitiatorAlias=Matrix Server + * @endverbatim + * If an initiator has been configured with a human-readable name or + * description, it SHOULD be communicated to the target during a Login + * Request PDU. If not, the host name can be used instead. This string + * is not used as an identifier, nor is it meant to be used for + * authentication or authorization decisions. It can be displayed by + * the target's user interface in a list of initiators to which it is + * connected. + */ +#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. + * + * @verbatim + * Use: ALL, Declarative, Any-Stage + * Senders: Target + * Scope: SW + * TargetAlias= + * Examples: + * TargetAlias=Bob-s Disk + * TargetAlias=Database Server 1 Log Disk + * TargetAlias=Web Server 3 Disk 20 + * @endverbatim + * If a target has been configured with a human-readable name or + * description, this name SHOULD be communicated to the initiator during + * a Login Response PDU if SessionType=Normal. This string is not used + * as an identifier, nor is it meant to be used for authentication or + * 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 ((const uint8_t *) "TargetAlias\0\0\0\0") + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Target portal group tag. + * + * @verbatim + * Use: IO by target, Declarative, Any-Stage + * Senders: Target + * Scope: SW + * TargetPortalGroupTag=<16-bit-binary-value> + * Example: + * TargetPortalGroupTag=1 + * @endverbatim + * The TargetPortalGroupTag key is a 16-bit binary-value that uniquely + * identifies a portal group within an iSCSI target node. This key + * carries the value of the tag of the portal group that is servicing + * the Login Request. The iSCSI target returns this key to the + * initiator in the Login Response PDU to the first Login Request PDU + * that has the C bit set to 0 when TargetName is given by the + * initiator.\n + * SAM2 notes in its informative text that the TPGT value should be + * non-zero; note that this is incorrect. A zero value is allowed as a + * legal value for the TPGT. This discrepancy currently stands + * corrected in SAM4. + */ +#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. + * + * @verbatim + * Use: During Login - Security Negotiation + * Senders: Initiator and target + * Scope: connection + * AuthMethod = + * @endverbatim + * The main item of security negotiation is the authentication method + * (AuthMethod).\n + * The authentication methods that can be used (appear in the list-of- + * values) are either vendor-unique methods or those listed in the + * following table: + * Name | Description + * :--- | :--------------------------------------------------------------- + * KRB5 | Kerberos V5 - defined in RFC4120 + * SRP | Secure Remote Password - defined in RFC2945 + * CHAP | Challenge Handshake Authentication Protocol - defined in RFC1994 + * None | No authentication + * + * The AuthMethod selection is followed by an "authentication exchange" + * specific to the authentication method selected.\n + * The authentication method proposal may be made by either the + * initiator or the target. However, the initiator MUST make the first + * step specific to the selected authentication method as soon as it is + * selected. It follows that if the target makes the authentication + * method proposal, the initiator sends the first key(s) of the exchange + * together with its authentication method selection.\n + * The authentication exchange authenticates the initiator to the target + * and, optionally, the target to the initiator. Authentication is + * OPTIONAL to use but MUST be supported by the target and initiator. + * The initiator and target MUST implement CHAP. All other + * authentication methods are OPTIONAL.\n + * Private or public extension algorithms MAY also be negotiated for + * authentication methods. Whenever a private or public extension + * algorithm is part of the default offer (the offer made in the absence + * of explicit administrative action), the implementer MUST ensure that + * CHAP is listed as an alternative in the default offer and "None" is + * not part of the default offer.\n + * Extension authentication methods MUST be named using one of the + * following two formats: + * -# Z-reversed.vendor.dns_name.do_something= + * -# New public key with no name prefix constraints + * + * Authentication methods named using the Z- format are used as private + * extensions. New public keys must be registered with IANA using the + * IETF Review process RFC5226. New public extensions for + * authentication methods MUST NOT use the Z# name prefix.\n + * For all of the public or private extension authentication methods, + * the method-specific keys MUST conform to the format specified for + * standard-label.\n + * To identify the vendor for private extension authentication methods, + * we suggest using the reversed DNS-name as a prefix to the proper + * digest names.\n + * The part of digest-name following Z- MUST conform to the format for + * standard-label.\n + * Support for public or private extension authentication methods is + * OPTIONAL. + */ +#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD ((const uint8_t *) "AuthMethod\0\0\0\0\0") + + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Kerberos V5 (KRB5): KRB_AP_REQ. + * + * For KRB5 (Kerberos V5) (see RFC4120 and RFC1964), the initiator MUST use: + * @verbatim + * KRB_AP_REQ= + * @endverbatim + * where KRB_AP_REQ is the client message as defined in RFC4120. + * The default principal name assumed by an iSCSI initiator or target + * (prior to any administrative configuration action) MUST be the iSCSI + * Initiator Name or iSCSI Target Name, respectively, prefixed by the + * string "iscsi/".\n + * If the initiator authentication fails, the target MUST respond with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator has selected the mutual authentication option (by setting + * MUTUAL-REQUIRED in the ap-options field of the KRB_AP_REQ), the + * target MUST reply with: + * @verbatim + * KRB_AP_REP= + * @endverbatim + * where KRB_AP_REP is the server's response message as defined in + * RFC4120.\n + * If mutual authentication was selected and target authentication + * fails, the initiator MUST close the connection.\n + * KRB_AP_REQ and KRB_AP_REP are binary-values, and their binary length + * (not the length of the character string that represents them in + * 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 ((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. + * + * For KRB5 (Kerberos V5) (see RFC4120 and RFC1964), the initiator MUST use: + * @verbatim + * KRB_AP_REQ= + * @endverbatim + * where KRB_AP_REQ is the client message as defined in RFC4120. + * The default principal name assumed by an iSCSI initiator or target + * (prior to any administrative configuration action) MUST be the iSCSI + * Initiator Name or iSCSI Target Name, respectively, prefixed by the + * string "iscsi/".\n + * If the initiator authentication fails, the target MUST respond with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator has selected the mutual authentication option (by setting + * MUTUAL-REQUIRED in the ap-options field of the KRB_AP_REQ), the + * target MUST reply with: + * @verbatim + * KRB_AP_REP= + * @endverbatim + * where KRB_AP_REP is the server's response message as defined in + * RFC4120.\n + * If mutual authentication was selected and target authentication + * fails, the initiator MUST close the connection.\n + * KRB_AP_REQ and KRB_AP_REP are binary-values, and their binary length + * (not the length of the character string that represents them in + * 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 ((const uint8_t *) "KRB_AP_REP\0\0\0\0\0") + + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_U. + * + * For SRP RFC2945, the initiator MUST use: + * @verbatim + * SRP_U= TargetAuth=Yes or TargetAuth=No + * @endverbatim + * The target MUST answer with a Login reject with the "Authorization + * Failure" status or reply with: + * @verbatim + * SRP_GROUP= SRP_s= + * @endverbatim + * where G1,G2... are proposed groups, in order of preference. + * The initiator MUST either close the connection or continue with: + * @verbatim + * SRP_A= + * SRP_GROUP= + * @endverbatim + * where G is one of G1,G2... that were proposed by the target. + * The target MUST answer with a Login reject with the "Authentication + * Failure" status or reply with: + * @verbatim + * SRP_B= + * @endverbatim + * The initiator MUST close the connection or continue with: + * @verbatim + * SRP_M= + * @endverbatim + * If the initiator authentication fails, the target MUST answer with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator sent TargetAuth=Yes in the first message (requiring target + * authentication), the target MUST reply with: + * @verbatim + * SRP_HM= + * @endverbatim + * If the target authentication fails, the initiator MUST close the + * connection:\n + * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using + * the SHA1 hash function, such as SRP-SHA1) and + * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups + * specified in RFC3723.\n + * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are + * binary-values. The length of s,A,B,M and H(A | M | K) in binary form + * (not the length of the character string that represents them in + * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may + * be used for s,A,B,M and H(A | M | K).\n + * For the SRP_GROUP, all the groups specified in RFC3723 up to + * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be + * 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 ((const uint8_t *) "SRP_U\0\0") + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_GROUP. + * + * For SRP RFC2945, the initiator MUST use: + * @verbatim + * SRP_U= TargetAuth=Yes or TargetAuth=No + * @endverbatim + * The target MUST answer with a Login reject with the "Authorization + * Failure" status or reply with: + * @verbatim + * SRP_GROUP= SRP_s= + * @endverbatim + * where G1,G2... are proposed groups, in order of preference. + * The initiator MUST either close the connection or continue with: + * @verbatim + * SRP_A= + * SRP_GROUP= + * @endverbatim + * where G is one of G1,G2... that were proposed by the target. + * The target MUST answer with a Login reject with the "Authentication + * Failure" status or reply with: + * @verbatim + * SRP_B= + * @endverbatim + * The initiator MUST close the connection or continue with: + * @verbatim + * SRP_M= + * @endverbatim + * If the initiator authentication fails, the target MUST answer with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator sent TargetAuth=Yes in the first message (requiring target + * authentication), the target MUST reply with: + * @verbatim + * SRP_HM= + * @endverbatim + * If the target authentication fails, the initiator MUST close the + * connection:\n + * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using + * the SHA1 hash function, such as SRP-SHA1) and + * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups + * specified in RFC3723.\n + * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are + * binary-values. The length of s,A,B,M and H(A | M | K) in binary form + * (not the length of the character string that represents them in + * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may + * be used for s,A,B,M and H(A | M | K).\n + * For the SRP_GROUP, all the groups specified in RFC3723 up to + * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be + * 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 ((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. + * + * For SRP RFC2945, the initiator MUST use: + * @verbatim + * SRP_U= TargetAuth=Yes or TargetAuth=No + * @endverbatim + * The target MUST answer with a Login reject with the "Authorization + * Failure" status or reply with: + * @verbatim + * SRP_GROUP= SRP_s= + * @endverbatim + * where G1,G2... are proposed groups, in order of preference. + * The initiator MUST either close the connection or continue with: + * @verbatim + * SRP_A= + * SRP_GROUP= + * @endverbatim + * where G is one of G1,G2... that were proposed by the target. + * The target MUST answer with a Login reject with the "Authentication + * Failure" status or reply with: + * @verbatim + * SRP_B= + * @endverbatim + * The initiator MUST close the connection or continue with: + * @verbatim + * SRP_M= + * @endverbatim + * If the initiator authentication fails, the target MUST answer with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator sent TargetAuth=Yes in the first message (requiring target + * authentication), the target MUST reply with: + * @verbatim + * SRP_HM= + * @endverbatim + * If the target authentication fails, the initiator MUST close the + * connection:\n + * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using + * the SHA1 hash function, such as SRP-SHA1) and + * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups + * specified in RFC3723.\n + * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are + * binary-values. The length of s,A,B,M and H(A | M | K) in binary form + * (not the length of the character string that represents them in + * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may + * be used for s,A,B,M and H(A | M | K).\n + * For the SRP_GROUP, all the groups specified in RFC3723 up to + * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be + * 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 ((const uint8_t *) "SRP_A\0\0") + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_B. + * + * For SRP RFC2945, the initiator MUST use: + * @verbatim + * SRP_U= TargetAuth=Yes or TargetAuth=No + * @endverbatim + * The target MUST answer with a Login reject with the "Authorization + * Failure" status or reply with: + * @verbatim + * SRP_GROUP= SRP_s= + * @endverbatim + * where G1,G2... are proposed groups, in order of preference. + * The initiator MUST either close the connection or continue with: + * @verbatim + * SRP_A= + * SRP_GROUP= + * @endverbatim + * where G is one of G1,G2... that were proposed by the target. + * The target MUST answer with a Login reject with the "Authentication + * Failure" status or reply with: + * @verbatim + * SRP_B= + * @endverbatim + * The initiator MUST close the connection or continue with: + * @verbatim + * SRP_M= + * @endverbatim + * If the initiator authentication fails, the target MUST answer with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator sent TargetAuth=Yes in the first message (requiring target + * authentication), the target MUST reply with: + * @verbatim + * SRP_HM= + * @endverbatim + * If the target authentication fails, the initiator MUST close the + * connection:\n + * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using + * the SHA1 hash function, such as SRP-SHA1) and + * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups + * specified in RFC3723.\n + * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are + * binary-values. The length of s,A,B,M and H(A | M | K) in binary form + * (not the length of the character string that represents them in + * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may + * be used for s,A,B,M and H(A | M | K).\n + * For the SRP_GROUP, all the groups specified in RFC3723 up to + * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be + * 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 ((const uint8_t *) "SRP_B\0\0") + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_M. + * + * For SRP RFC2945, the initiator MUST use: + * @verbatim + * SRP_U= TargetAuth=Yes or TargetAuth=No + * @endverbatim + * The target MUST answer with a Login reject with the "Authorization + * Failure" status or reply with: + * @verbatim + * SRP_GROUP= SRP_s= + * @endverbatim + * where G1,G2... are proposed groups, in order of preference. + * The initiator MUST either close the connection or continue with: + * @verbatim + * SRP_A= + * SRP_GROUP= + * @endverbatim + * where G is one of G1,G2... that were proposed by the target. + * The target MUST answer with a Login reject with the "Authentication + * Failure" status or reply with: + * @verbatim + * SRP_B= + * @endverbatim + * The initiator MUST close the connection or continue with: + * @verbatim + * SRP_M= + * @endverbatim + * If the initiator authentication fails, the target MUST answer with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator sent TargetAuth=Yes in the first message (requiring target + * authentication), the target MUST reply with: + * @verbatim + * SRP_HM= + * @endverbatim + * If the target authentication fails, the initiator MUST close the + * connection:\n + * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using + * the SHA1 hash function, such as SRP-SHA1) and + * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups + * specified in RFC3723.\n + * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are + * binary-values. The length of s,A,B,M and H(A | M | K) in binary form + * (not the length of the character string that represents them in + * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may + * be used for s,A,B,M and H(A | M | K).\n + * For the SRP_GROUP, all the groups specified in RFC3723 up to + * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be + * 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 ((const uint8_t *) "SRP_M\0\0") + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_HM. + * + * For SRP RFC2945, the initiator MUST use: + * @verbatim + * SRP_U= TargetAuth=Yes or TargetAuth=No + * @endverbatim + * The target MUST answer with a Login reject with the "Authorization + * Failure" status or reply with: + * @verbatim + * SRP_GROUP= SRP_s= + * @endverbatim + * where G1,G2... are proposed groups, in order of preference. + * The initiator MUST either close the connection or continue with: + * @verbatim + * SRP_A= + * SRP_GROUP= + * @endverbatim + * where G is one of G1,G2... that were proposed by the target. + * The target MUST answer with a Login reject with the "Authentication + * Failure" status or reply with: + * @verbatim + * SRP_B= + * @endverbatim + * The initiator MUST close the connection or continue with: + * @verbatim + * SRP_M= + * @endverbatim + * If the initiator authentication fails, the target MUST answer with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator sent TargetAuth=Yes in the first message (requiring target + * authentication), the target MUST reply with: + * @verbatim + * SRP_HM= + * @endverbatim + * If the target authentication fails, the initiator MUST close the + * connection:\n + * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using + * the SHA1 hash function, such as SRP-SHA1) and + * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups + * specified in RFC3723.\n + * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are + * binary-values. The length of s,A,B,M and H(A | M | K) in binary form + * (not the length of the character string that represents them in + * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may + * be used for s,A,B,M and H(A | M | K).\n + * For the SRP_GROUP, all the groups specified in RFC3723 up to + * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be + * 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 ((const uint8_t *) "SRP_HM\0") + + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_A. + * + * For CHAP RFC1994, the initiator MUST use: + * @verbatim + * CHAP_A= + * @endverbatim + * where A1,A2... are proposed algorithms, in order of preference. + * The target MUST answer with a Login reject with the "Authentication + * Failure" status or reply with: + * @verbatim + * CHAP_A= + * CHAP_I= + * CHAP_C= + * @endverbatim + * where A is one of A1,A2... that were proposed by the initiator. + * The initiator MUST continue with: + * @verbatim + * CHAP_N= + * CHAP_R= + * @endverbatim + * or, if it requires target authentication, with: + * @verbatim + * CHAP_N= + * CHAP_R= + * CHAP_I= + * CHAP_C= + * @endverbatim + * If the initiator authentication fails, the target MUST answer with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator required target authentication, the target MUST either + * answer with a Login reject with "Authentication Failure" or reply + * with: + * @verbatim + * CHAP_N= + * CHAP_R= + * @endverbatim + * If the target authentication fails, the initiator MUST close the + * connection:\n + * where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, + * Algorithm, Identifier, Challenge, and Response as defined in + * RFC1994.\n + * N is a text string; A,A1,A2, and I are numbers; C and R are + * binary-values. Their binary length (not the length of the character + * string that represents them in encoded form) MUST NOT exceed + * 1024 bytes. Hex or Base64 encoding may be used for C and R.\n + * For the Algorithm, as stated in [RFC1994], one value is required to + * be implemented: + * @verbatim + * 5 (CHAP with MD5) + * @endverbatim + * 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 ((const uint8_t *) "CHAP_A\0") + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_I. + * + * For CHAP RFC1994, the initiator MUST use: + * @verbatim + * CHAP_A= + * @endverbatim + * where A1,A2... are proposed algorithms, in order of preference. + * The target MUST answer with a Login reject with the "Authentication + * Failure" status or reply with: + * @verbatim + * CHAP_A= + * CHAP_I= + * CHAP_C= + * @endverbatim + * where A is one of A1,A2... that were proposed by the initiator. + * The initiator MUST continue with: + * @verbatim + * CHAP_N= + * CHAP_R= + * @endverbatim + * or, if it requires target authentication, with: + * @verbatim + * CHAP_N= + * CHAP_R= + * CHAP_I= + * CHAP_C= + * @endverbatim + * If the initiator authentication fails, the target MUST answer with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator required target authentication, the target MUST either + * answer with a Login reject with "Authentication Failure" or reply + * with: + * @verbatim + * CHAP_N= + * CHAP_R= + * @endverbatim + * If the target authentication fails, the initiator MUST close the + * connection:\n + * where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, + * Algorithm, Identifier, Challenge, and Response as defined in + * RFC1994.\n + * N is a text string; A,A1,A2, and I are numbers; C and R are + * binary-values. Their binary length (not the length of the character + * string that represents them in encoded form) MUST NOT exceed + * 1024 bytes. Hex or Base64 encoding may be used for C and R.\n + * For the Algorithm, as stated in [RFC1994], one value is required to + * be implemented: + * @verbatim + * 5 (CHAP with MD5) + * @endverbatim + * 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 ((const uint8_t *) "CHAP_I\0") + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_C. + * + * For CHAP RFC1994, the initiator MUST use: + * @verbatim + * CHAP_A= + * @endverbatim + * where A1,A2... are proposed algorithms, in order of preference. + * The target MUST answer with a Login reject with the "Authentication + * Failure" status or reply with: + * @verbatim + * CHAP_A= + * CHAP_I= + * CHAP_C= + * @endverbatim + * where A is one of A1,A2... that were proposed by the initiator. + * The initiator MUST continue with: + * @verbatim + * CHAP_N= + * CHAP_R= + * @endverbatim + * or, if it requires target authentication, with: + * @verbatim + * CHAP_N= + * CHAP_R= + * CHAP_I= + * CHAP_C= + * @endverbatim + * If the initiator authentication fails, the target MUST answer with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator required target authentication, the target MUST either + * answer with a Login reject with "Authentication Failure" or reply + * with: + * @verbatim + * CHAP_N= + * CHAP_R= + * @endverbatim + * If the target authentication fails, the initiator MUST close the + * connection:\n + * where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, + * Algorithm, Identifier, Challenge, and Response as defined in + * RFC1994.\n + * N is a text string; A,A1,A2, and I are numbers; C and R are + * binary-values. Their binary length (not the length of the character + * string that represents them in encoded form) MUST NOT exceed + * 1024 bytes. Hex or Base64 encoding may be used for C and R.\n + * For the Algorithm, as stated in [RFC1994], one value is required to + * be implemented: + * @verbatim + * 5 (CHAP with MD5) + * @endverbatim + * 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 ((const uint8_t *) "CHAP_C\0") + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_N. + * + * For CHAP RFC1994, the initiator MUST use: + * @verbatim + * CHAP_A= + * @endverbatim + * where A1,A2... are proposed algorithms, in order of preference. + * The target MUST answer with a Login reject with the "Authentication + * Failure" status or reply with: + * @verbatim + * CHAP_A= + * CHAP_I= + * CHAP_C= + * @endverbatim + * where A is one of A1,A2... that were proposed by the initiator. + * The initiator MUST continue with: + * @verbatim + * CHAP_N= + * CHAP_R= + * @endverbatim + * or, if it requires target authentication, with: + * @verbatim + * CHAP_N= + * CHAP_R= + * CHAP_I= + * CHAP_C= + * @endverbatim + * If the initiator authentication fails, the target MUST answer with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator required target authentication, the target MUST either + * answer with a Login reject with "Authentication Failure" or reply + * with: + * @verbatim + * CHAP_N= + * CHAP_R= + * @endverbatim + * If the target authentication fails, the initiator MUST close the + * connection:\n + * where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, + * Algorithm, Identifier, Challenge, and Response as defined in + * RFC1994.\n + * N is a text string; A,A1,A2, and I are numbers; C and R are + * binary-values. Their binary length (not the length of the character + * string that represents them in encoded form) MUST NOT exceed + * 1024 bytes. Hex or Base64 encoding may be used for C and R.\n + * For the Algorithm, as stated in [RFC1994], one value is required to + * be implemented: + * @verbatim + * 5 (CHAP with MD5) + * @endverbatim + * 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 ((const uint8_t *) "CHAP_N\0") + +/** + * @brief Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_R. + * + * For CHAP RFC1994, the initiator MUST use: + * @verbatim + * CHAP_A= + * @endverbatim + * where A1,A2... are proposed algorithms, in order of preference. + * The target MUST answer with a Login reject with the "Authentication + * Failure" status or reply with: + * @verbatim + * CHAP_A= + * CHAP_I= + * CHAP_C= + * @endverbatim + * where A is one of A1,A2... that were proposed by the initiator. + * The initiator MUST continue with: + * @verbatim + * CHAP_N= + * CHAP_R= + * @endverbatim + * or, if it requires target authentication, with: + * @verbatim + * CHAP_N= + * CHAP_R= + * CHAP_I= + * CHAP_C= + * @endverbatim + * If the initiator authentication fails, the target MUST answer with a + * Login reject with "Authentication Failure" status. Otherwise, if the + * initiator required target authentication, the target MUST either + * answer with a Login reject with "Authentication Failure" or reply + * with: + * @verbatim + * CHAP_N= + * CHAP_R= + * @endverbatim + * If the target authentication fails, the initiator MUST close the + * connection:\n + * where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, + * Algorithm, Identifier, Challenge, and Response as defined in + * RFC1994.\n + * N is a text string; A,A1,A2, and I are numbers; C and R are + * binary-values. Their binary length (not the length of the character + * string that represents them in encoded form) MUST NOT exceed + * 1024 bytes. Hex or Base64 encoding may be used for C and R.\n + * For the Algorithm, as stated in [RFC1994], one value is required to + * be implemented: + * @verbatim + * 5 (CHAP with MD5) + * @endverbatim + * 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 ((const uint8_t *) "CHAP_R\0") + +/* Login/Text Operational Text Keys + + Some session-specific parameters MUST only be carried on the leading + connection and cannot be changed after the leading connection login + (e.g., MaxConnections - the maximum number of connections). This + holds for a single connection session with regard to connection + restart. The keys that fall into this category have the "use: LO" + (Leading Only). + + Keys that can only be used during login have the "use: IO" + (Initialize Only), while those that can be used in both the Login + Phase and Full Feature Phase have the "use: ALL". + + Keys that can only be used during the Full Feature Phase use FFPO + (Full Feature Phase Only). + + Keys marked as Any-Stage may also appear in the SecurityNegotiation + stage, while all other keys described in this section are + operational keys. + + Keys that do not require an answer are marked as Declarative. + + Key scope is indicated as session-wide (SW) or connection-only (CO). + + "Result function", wherever mentioned, states the function that can + be applied to check the validity of the responder selection. + "Minimum" means that the selected value cannot exceed the offered + value. "Maximum" means that the selected value cannot be lower than + the offered value. "AND" means that the selected value must be a + possible result of a Boolean "and" function with an arbitrary Boolean + value (e.g., if the offered value is No the selected value must be + No). "OR" means that the selected value must be a possible result of + a Boolean "or" function with an arbitrary Boolean value (e.g., if the + offered value is Yes the selected value must be Yes). +*/ + +/** + * @brief Login/Text Operational Session Text Key: Header digest. + * + * @verbatim + * Use: IO + * Senders: Initiator and target + * Scope: CO + * HeaderDigest = + * Default is None for HeaderDigest. + * @endverbatim + * Digests enable the checking of end-to-end, non-cryptographic data + * integrity beyond the integrity checks provided by the link layers and + * the covering of the whole communication path, including all elements + * that may change the network-level PDUs, such as routers, switches, + * and proxies.\n + * The following table lists cyclic integrity checksums that can be + * negotiated for the digests and MUST be implemented by every iSCSI + * initiator and target. These digest options only have error detection + * significance. + * Name | Description | Generator + * :----- | :---------- | :---------- + * CRC32C | 32-bit CRC | 0x11EDC6F41 + * None | no digest || + * + * The generator polynomial G(x) for this digest is given in hexadecimal + * notation (e.g. "0x3b" stands for 0011 1011, and the polynomial is + * x**5 + x**4 + x**3 + x + 1).\n + * When the initiator and target agree on a digest, this digest MUST be + * used for every PDU in the Full Feature Phase.\n + * Padding bytes, when present in a segment covered by a CRC, SHOULD be + * set to 0 and are included in the CRC.\n + * The CRC MUST be calculated by a method that produces the same results + * as the following process: + * - The PDU bits are considered as the coefficients of a polynomial + * M(x) of degree n - 1; bit 7 of the lowest numbered byte is + * considered the most significant bit (x**n - 1), followed by bit 6 + * of the lowest numbered byte through bit 0 of the highest numbered + * byte (x**0). + * - The most significant 32 bits are complemented. + * - The polynomial is multiplied by x**32, then divided by G(x). The + * generator polynomial produces a remainder R(x) of degree <= 31. + * - The coefficients of R(x) are formed into a 32-bit sequence. + * - The bit sequence is complemented, and the result is the CRC. + * - The CRC bits are mapped into the digest word. The x**31 + * coefficient is mapped to bit 7 of the lowest numbered byte of the + * digest, and the mapping continues with successive coefficients and + * bits so that the x**24 coefficient is mapped to bit 0 of the lowest + * numbered byte. The mapping continues further with the x**23 + * coefficient mapped to bit 7 of the next byte in the digest until + * the x**0 coefficient is mapped to bit 0 of the highest numbered + * byte of the digest. + * - Computing the CRC over any segment (data or header) extended to + * include the CRC built using the generator 0x11edc6f41 will always + * get the value 0x1c2d19ed as its final remainder (R(x)). This value + * is given here in its polynomial form (i.e., not mapped as the + * digest word). + * + * For a discussion about selection criteria for the CRC, see RFC3385.\n + * For a detailed analysis of the iSCSI polynomial, see Castagnoli93.\n + * Private or public extension algorithms MAY also be negotiated for + * digests. Whenever a private or public digest extension algorithm is + * part of the default offer (the offer made in the absence of explicit + * administrative action), the implementer MUST ensure that CRC32C is + * listed as an alternative in the default offer and "None" is not part + * of the default offer.\n + * Extension digest algorithms MUST be named using one of the following + * two formats: + * 1. Y-reversed.vendor.dns_name.do_something= + * 2. New public key with no name prefix constraints + * + * Digests named using the Y- format are used for private purposes + * (unregistered). New public keys must be registered with IANA using + * the IETF Review process (RFC5226). New public extensions for + * digests MUST NOT use the Y# name prefix.\n + * For private extension digests, to identify the vendor we suggest + * using the reversed DNS-name as a prefix to the proper digest names.\n + * The part of digest-name following Y- MUST conform to the format for + * standard-label specified.\n + * Support for public or private extension digests is OPTIONAL. + */ +#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. + * + * @verbatim + * Use: IO + * Senders: Initiator and target + * Scope: CO + * DataDigest = + * Default is None for DataDigest. + * @endverbatim + * Digests enable the checking of end-to-end, non-cryptographic data + * integrity beyond the integrity checks provided by the link layers and + * the covering of the whole communication path, including all elements + * that may change the network-level PDUs, such as routers, switches, + * and proxies.\n + * The following table lists cyclic integrity checksums that can be + * negotiated for the digests and MUST be implemented by every iSCSI + * initiator and target. These digest options only have error detection + * significance. + * Name | Description | Generator + * :----- | :---------- | :---------- + * CRC32C | 32-bit CRC | 0x11EDC6F41 + * None | no digest || + * + * The generator polynomial G(x) for this digest is given in hexadecimal + * notation (e.g. "0x3b" stands for 0011 1011, and the polynomial is + * x**5 + x**4 + x**3 + x + 1).\n + * When the initiator and target agree on a digest, this digest MUST be + * used for every PDU in the Full Feature Phase.\n + * Padding bytes, when present in a segment covered by a CRC, SHOULD be + * set to 0 and are included in the CRC.\n + * The CRC MUST be calculated by a method that produces the same results + * as the following process: + * - The PDU bits are considered as the coefficients of a polynomial + * M(x) of degree n - 1; bit 7 of the lowest numbered byte is + * considered the most significant bit (x**n - 1), followed by bit 6 + * of the lowest numbered byte through bit 0 of the highest numbered + * byte (x**0). + * - The most significant 32 bits are complemented. + * - The polynomial is multiplied by x**32, then divided by G(x). The + * generator polynomial produces a remainder R(x) of degree <= 31. + * - The coefficients of R(x) are formed into a 32-bit sequence. + * - The bit sequence is complemented, and the result is the CRC. + * - The CRC bits are mapped into the digest word. The x**31 + * coefficient is mapped to bit 7 of the lowest numbered byte of the + * digest, and the mapping continues with successive coefficients and + * bits so that the x**24 coefficient is mapped to bit 0 of the lowest + * numbered byte. The mapping continues further with the x**23 + * coefficient mapped to bit 7 of the next byte in the digest until + * the x**0 coefficient is mapped to bit 0 of the highest numbered + * byte of the digest. + * - Computing the CRC over any segment (data or header) extended to + * include the CRC built using the generator 0x11edc6f41 will always + * get the value 0x1c2d19ed as its final remainder (R(x)). This value + * is given here in its polynomial form (i.e., not mapped as the + * digest word). + * + * For a discussion about selection criteria for the CRC, see RFC3385.\n + * For a detailed analysis of the iSCSI polynomial, see Castagnoli93.\n + * Private or public extension algorithms MAY also be negotiated for + * digests. Whenever a private or public digest extension algorithm is + * part of the default offer (the offer made in the absence of explicit + * administrative action), the implementer MUST ensure that CRC32C is + * listed as an alternative in the default offer and "None" is not part + * of the default offer.\n + * Extension digest algorithms MUST be named using one of the following + * two formats: + * 1. Y-reversed.vendor.dns_name.do_something= + * 2. New public key with no name prefix constraints + * + * Digests named using the Y- format are used for private purposes + * (unregistered). New public keys must be registered with IANA using + * the IETF Review process (RFC5226). New public extensions for + * digests MUST NOT use the Y# name prefix.\n + * For private extension digests, to identify the vendor we suggest + * using the reversed DNS-name as a prefix to the proper digest names.\n + * The part of digest-name following Y- MUST conform to the format for + * standard-label specified.\n + * Support for public or private extension digests is OPTIONAL. + */ +#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. + * + * @verbatim + * Use: LO + * Senders: Initiator and target + * Scope: SW + * Irrelevant when: SessionType=Discovery + * MaxConnections= + * Default is 1. + * @endverbatim + * Result function is Minimum.\n + * The initiator and target negotiate the maximum number of connections + * requested/acceptable. + */ +#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS ((const uint8_t *) "MaxConnections\0") + +/** + * @brief Login/Text Operational Session Text Key: Send targets. + * + * @verbatim + * Use: FFPO + * Senders: Initiator + * Scope: SW + * @endverbatim + * The text in this appendix is a normative part of this document.\n + * To reduce the amount of configuration required on an initiator, iSCSI + * provides the SendTargets Text Request. The initiator uses the + * SendTargets request to get a list of targets to which it may have + * access, as well as the list of addresses (IP address and TCP port) on + * which these targets may be accessed.\n * To make use of SendTargets, an initiator must first establish one of * two types of sessions. If the initiator establishes the session * using the key "SessionType=Discovery", the session is a Discovery @@ -3667,2305 +7752,3203 @@ typedef struct __attribute__((packed)) iscsi_isid { * session.\n * The value must be one of: * @verbatim - * All - * The initiator is requesting that information on all relevant - * targets known to the implementation be returned. This value - * MUST be supported on a Discovery session and MUST NOT be - * supported on an operational session. - * - * If an iSCSI Target Name is specified, the session should - * respond with addresses for only the named target, if possible. - * This value MUST be supported on Discovery sessions. A - * Discovery session MUST be capable of returning addresses for - * those targets that would have been returned had value=All been - * designated. - * - * The session should only respond with addresses for the target - * to which the session is logged in. This MUST be supported on - * operational sessions and MUST NOT return targets other than the - * one to which the session is logged in. + * All + * The initiator is requesting that information on all relevant + * targets known to the implementation be returned. This value + * MUST be supported on a Discovery session and MUST NOT be + * supported on an operational session. + * + * If an iSCSI Target Name is specified, the session should + * respond with addresses for only the named target, if possible. + * This value MUST be supported on Discovery sessions. A + * Discovery session MUST be capable of returning addresses for + * those targets that would have been returned had value=All been + * designated. + * + * The session should only respond with addresses for the target + * to which the session is logged in. This MUST be supported on + * operational sessions and MUST NOT return targets other than the + * one to which the session is logged in. + * @endverbatim + * The response to this command is a Text Response that contains a list + * of zero or more targets and, optionally, their addresses. Each + * target is returned as a target record. A target record begins with + * the TargetName text key, followed by a list of TargetAddress text + * keys, and bounded by the end of the Text Response or the next + * TargetName key, which begins a new record. No text keys other than + * TargetName and TargetAddress are permitted within a SendTargets + * response.\n + * A Discovery session MAY respond to a SendTargets request with its + * complete list of targets, or with a list of targets that is based on + * the name of the initiator logged in to the session.\n + * A SendTargets response MUST NOT contain target names if there are no + * targets for the requesting initiator to access.\n + * Each target record returned includes zero or more TargetAddress + * fields.\n + * Each target record starts with one text key of the form: + * @verbatim + * TargetName= + * @endverbatim + * followed by zero or more address keys of the form: + * @verbatim + * TargetAddress=[:], + * + * @endverbatim + * The hostname-or-ipaddress contains a domain name, IPv4 address, or + * IPv6 address (RFC4291), as specified for the TargetAddress key.\n + * A hostname-or-ipaddress duplicated in TargetAddress responses for a + * given node (the port is absent or equal) would probably indicate that + * multiple address families are in use at once (IPv6 and IPv4).\n + * Each TargetAddress belongs to a portal group, identified by its + * numeric Target Portal Group Tag. The iSCSI Target Name, together with + * this tag, constitutes the SCSI port identifier; the tag only needs to + * be unique within a given target's name list of addresses.\n + * Multiple-connection sessions can span iSCSI addresses that belong to + * the same portal group.\n + * Multiple-connection sessions cannot span iSCSI addresses that belong + * to different portal groups.\n + * If a SendTargets response reports an iSCSI address for a target, it + * SHOULD also report all other addresses in its portal group in the + * same response.\n + * A SendTargets Text Response can be longer than a single Text Response + * PDU and makes use of the long Text Responses as specified.\n + * After obtaining a list of targets from the Discovery session, an + * iSCSI initiator may initiate new sessions to log in to the discovered + * targets for full operation. The initiator MAY keep the Discovery + * session open and MAY send subsequent SendTargets commands to discover + * new targets.\n + * Examples:\n + * This example is the SendTargets response from a single target that + * has no other interface ports.\n + * The initiator sends a Text Request that contains: + * @verbatim + * SendTargets=All + * @endverbatim + * The target sends a Text Response that contains: + * @verbatim + * TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.8675309 + * @endverbatim + * All the target had to return in this simple case was the target name.\n + * It is assumed by the initiator that the IP address and TCP port for + * this target are the same as those used on the current connection to + * the default iSCSI target.\n + * The next example has two internal iSCSI targets, each accessible via + * two different ports with different IP addresses. The following is + * the Text Response: + * @verbatim + * TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.8675309 + * TargetAddress=10.1.0.45:5300,1 + * TargetAddress=10.1.1.45:5300,2 + * TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.1234567 + * TargetAddress=10.1.0.45:5300,1 + * TargetAddress=10.1.1.45:5300,2 + * @endverbatim + * Both targets share both addresses; the multiple addresses are likely + * used to provide multi-path support. The initiator may connect to + * either target name on either address. Each of the addresses has its + * own Target Portal Group Tag; they do not support spanning multiple- + * connection sessions with each other. Keep in mind that the Target + * Portal Group Tags for the two named targets are independent of one + * another; portal group "1" on the first target is not necessarily the + * same as portal group "1" on the second target.\n + * In the above example, a DNS host name or an IPv6 address could have + * been returned instead of an IPv4 address.\n + * The next Text Response shows a target that supports spanning sessions + * across multiple addresses and further illustrates the use of the + * Target Portal Group Tags: + * @verbatim + * TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.8675309 + * TargetAddress=10.1.0.45:5300,1 + * TargetAddress=10.1.1.46:5300,1 + * TargetAddress=10.1.0.47:5300,2 + * TargetAddress=10.1.1.48:5300,2 + * TargetAddress=10.1.1.49:5300,3 + * @endverbatim + * In this example, any of the target addresses can be used to reach the + * same target. A single-connection session can be established to any + * of these TCP addresses. A multiple-connection session could span + * addresses .45 and .46 or .47 and .48 but cannot span any other + * combination. A TargetAddress with its own tag (.49) cannot be + * combined with any other address within the same session.\n + * This SendTargets response does not indicate whether .49 supports + * 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 ((const uint8_t *) "SendTargets\0\0\0\0") + +/** + * @brief Login/Text Operational Session Text Key: Initial Ready To Transfer. + * + * @verbatim + * Use: LO + * Senders: Initiator and target + * Scope: SW + * Irrelevant when: SessionType=Discovery + * InitialR2T= + * Examples: + * I->InitialR2T=No + * T->InitialR2T=No + * Default is Yes. + * @endverbatim + * Result function is OR.\n + * The InitialR2T key is used to turn off the default use of R2T for + * unidirectional operations and the output part of bidirectional + * commands, thus allowing an initiator to start sending data to a + * target as if it has received an initial R2T with Buffer + * Offset=Immediate Data Length and Desired Data Transfer + * Length=(min(FirstBurstLength, Expected Data Transfer Length) - + * Received Immediate Data Length).\n + * The default action is that R2T is required, unless both the initiator + * and the target send this key-pair attribute specifying InitialR2T=No. + * 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 ((const uint8_t *) "InitialR2T\0\0\0\0\0") + +/** + * @brief Login/Text Operational Session Text Key: Immediate data. + * + * @verbatim + * Use: LO + * Senders: Initiator and target + * Scope: SW + * Irrelevant when: SessionType=Discovery + * ImmediateData= + * Default is Yes. + * @endverbatim + * Result function is AND.\n + * 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 + * If ImmediateData is set to Yes and InitialR2T is set to Yes + * (default), then only immediate data are accepted in the first burst. + * If ImmediateData is set to No and InitialR2T is set to Yes, then the + * initiator MUST NOT send unsolicited data and the target MUST reject + * unsolicited data with the corresponding response code.\n + * If ImmediateData is set to No and InitialR2T is set to No, then the + * initiator MUST NOT send unsolicited immediate data but MAY send one + * unsolicited burst of Data-OUT PDUs.\n + * If ImmediateData is set to Yes and InitialR2T is set to No, then the + * initiator MAY send unsolicited immediate data and/or one unsolicited + * burst of Data-OUT PDUs.\n + * The following table is a summary of unsolicited data options: + * InitialR2T | ImmediateData | Unsolicited Data-Out PDUs | ImmediateData + * :--------- | :------------ | :------------------------ | :------------ + * | No | No | Yes | No | + * | No | Yes | Yes | Yes | + * | Yes | No | No | No | + * | Yes | Yes | No | Yes | + */ +#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. + * + * @verbatim + * Use: ALL, Declarative + * Senders: Initiator and target + * Scope: CO + * MaxRecvDataSegmentLength= + * Default is 8192 bytes. + * @endverbatim + * The initiator or target declares the maximum data segment length in + * bytes it can receive in an iSCSI PDU.\n + * The transmitter (initiator or target) is required to send PDUs with a + * data segment that does not exceed MaxRecvDataSegmentLength of the + * receiver.\n + * A target receiver is additionally limited by MaxBurstLength for + * solicited data and FirstBurstLength for unsolicited dataAn + * initiator MUST NOT send solicited PDUs exceeding MaxBurstLength nor + * 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 ((const uint8_t *) "MaxRecvDataSegmentLength\0\0\0\0\0\0\0") + +/** + * @brief Login/Text Operational Session Text Key: Maximum burst length. + * + * @verbatim + * Use: LO + * Senders: Initiator and target + * Scope: SW + * Irrelevant when: SessionType=Discovery + * MaxBurstLength= + * Default is 262144 (256 KB). + * @endverbatim + * Result function is Minimum.\n + * The initiator and target negotiate the maximum SCSI data payload in + * bytes in a Data-In or a solicited Data-Out iSCSI sequence. A + * 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 ((const uint8_t *) "MaxBurstLength\0") + +/** + * @brief Login/Text Operational Session Text Key: First burst length. + * + * @verbatim + * Use: LO + * Senders: Initiator and target + * Scope: SW + * Irrelevant when: SessionType=Discovery + * Irrelevant when: ( InitialR2T=Yes and ImmediateData=No ) + * FirstBurstLength= + * Default is 65536 (64 KB). * @endverbatim - * The response to this command is a Text Response that contains a list - * of zero or more targets and, optionally, their addresses. Each - * target is returned as a target record. A target record begins with - * the TargetName text key, followed by a list of TargetAddress text - * keys, and bounded by the end of the Text Response or the next - * TargetName key, which begins a new record. No text keys other than - * TargetName and TargetAddress are permitted within a SendTargets - * response.\n - * A Discovery session MAY respond to a SendTargets request with its - * complete list of targets, or with a list of targets that is based on - * the name of the initiator logged in to the session.\n - * A SendTargets response MUST NOT contain target names if there are no - * targets for the requesting initiator to access.\n - * Each target record returned includes zero or more TargetAddress - * fields.\n - * Each target record starts with one text key of the form: + * Result function is Minimum.\n + * The initiator and target negotiate the maximum amount in bytes of + * unsolicited data an iSCSI initiator may send to the target during the + * execution of a single SCSI command. This covers the immediate data + * (if any) and the sequence of unsolicited Data-Out PDUs (if any) that + * follow the command.\n + * FirstBurstLength MUST NOT exceed MaxBurstLength. + */ +#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. + * * @verbatim - * TargetName= + * Use: LO + * Senders: Initiator and target + * Scope: SW + * DefaultTime2Wait= + * Default is 2. * @endverbatim - * followed by zero or more address keys of the form: + * Result function is Maximum.\n + * The initiator and target negotiate the minimum time, in seconds, to + * wait before attempting an explicit/implicit logout or an active task + * reassignment after an unexpected connection termination or a + * connection reset.\n + * 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 ((const uint8_t *) "DefaultTime2Wait\0\0\0\0\0\0\0") + +/** + * @brief Login/Text Operational Session Text Key: Default time to retain. + * * @verbatim - * TargetAddress=[:], - * + * Use: LO + * Senders: Initiator and target + * Scope: SW + * DefaultTime2Retain= + * Default is 20. * @endverbatim - * The hostname-or-ipaddress contains a domain name, IPv4 address, or - * IPv6 address (RFC4291), as specified for the TargetAddress key.\n - * A hostname-or-ipaddress duplicated in TargetAddress responses for a - * given node (the port is absent or equal) would probably indicate that - * multiple address families are in use at once (IPv6 and IPv4).\n - * Each TargetAddress belongs to a portal group, identified by its - * numeric Target Portal Group Tag. The iSCSI Target Name, together with - * this tag, constitutes the SCSI port identifier; the tag only needs to - * be unique within a given target's name list of addresses.\n - * Multiple-connection sessions can span iSCSI addresses that belong to - * the same portal group.\n - * Multiple-connection sessions cannot span iSCSI addresses that belong - * to different portal groups.\n - * If a SendTargets response reports an iSCSI address for a target, it - * SHOULD also report all other addresses in its portal group in the - * same response.\n - * A SendTargets Text Response can be longer than a single Text Response - * PDU and makes use of the long Text Responses as specified.\n - * After obtaining a list of targets from the Discovery session, an - * iSCSI initiator may initiate new sessions to log in to the discovered - * targets for full operation. The initiator MAY keep the Discovery - * session open and MAY send subsequent SendTargets commands to discover - * new targets.\n - * Examples:\n - * This example is the SendTargets response from a single target that - * has no other interface ports.\n - * The initiator sends a Text Request that contains: + * Result function is Minimum.\n + * The initiator and target negotiate the maximum time, in seconds, + * after an initial wait (Time2Wait), before which an active task + * reassignment is still possible after an unexpected connection + * termination or a connection reset.\n + * This value is also the session state timeout if the connection in + * question is the last LOGGED_IN connection in the session.\n + * 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 ((const uint8_t *) "DefaultTime2Retain\0\0\0\0\0") + +/** + * @brief Login/Text Operational Session Text Key: Maximum outstanding Ready To Transfer. + * * @verbatim - * SendTargets=All + * Use: LO + * Senders: Initiator and target + * Scope: SW + * MaxOutstandingR2T= + * Irrelevant when: SessionType=Discovery + * Default is 1. * @endverbatim - * The target sends a Text Response that contains: + * Result function is Minimum.\n + * The initiator and target negotiate the maximum number of outstanding + * R2Ts per task, excluding any implied initial R2T that might be part + * of that task. An R2T is considered outstanding until the last data + * 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 ((const uint8_t *) "MaxOutstandingR2T\0\0\0\0\0\0") + +/** + * @brief Login/Text Operational Session Text Key: Data Protocol Data Unit (PDU) in order. + * * @verbatim - * TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.8675309 + * Use: LO + * Senders: Initiator and target + * Scope: SW + * Irrelevant when: SessionType=Discovery + * DataPDUInOrder= + * Default is Yes. * @endverbatim - * All the target had to return in this simple case was the target name.\n - * It is assumed by the initiator that the IP address and TCP port for - * this target are the same as those used on the current connection to - * the default iSCSI target.\n - * The next example has two internal iSCSI targets, each accessible via - * two different ports with different IP addresses. The following is - * the Text Response: + * Result function is OR.\n + * "No" is used by iSCSI to indicate that the data PDUs within sequences + * can be in any order. "Yes" is used to indicate that data PDUs within + * sequences have to be at continuously increasing addresses and + * overlays are forbidden. + */ +#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. + * * @verbatim - * TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.8675309 - * TargetAddress=10.1.0.45:5300,1 - * TargetAddress=10.1.1.45:5300,2 - * TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.1234567 - * TargetAddress=10.1.0.45:5300,1 - * TargetAddress=10.1.1.45:5300,2 + * Use: LO + * Senders: Initiator and target + * Scope: SW + * Irrelevant when: SessionType=Discovery + * DataSequenceInOrder= + * Default is Yes. * @endverbatim - * Both targets share both addresses; the multiple addresses are likely - * used to provide multi-path support. The initiator may connect to - * either target name on either address. Each of the addresses has its - * own Target Portal Group Tag; they do not support spanning multiple- - * connection sessions with each other. Keep in mind that the Target - * Portal Group Tags for the two named targets are independent of one - * another; portal group "1" on the first target is not necessarily the - * same as portal group "1" on the second target.\n - * In the above example, a DNS host name or an IPv6 address could have - * been returned instead of an IPv4 address.\n - * The next Text Response shows a target that supports spanning sessions - * across multiple addresses and further illustrates the use of the - * Target Portal Group Tags: + * Result function is OR.\n + * A data sequence is a sequence of Data-In or Data-Out PDUs that end + * with a Data-In or Data-Out PDU with the F bit set to 1. A Data-Out + * sequence is sent either unsolicited or in response to an R2T.\n + * Sequences cover an offset-range.\n + * If DataSequenceInOrder is set to No, data PDU sequences may be + * transferred in any order.\n + * If DataSequenceInOrder is set to Yes, data sequences MUST be + * transferred using continuously non-decreasing sequence offsets (R2T + * buffer offset for writes, or the smallest SCSI Data-In buffer offset + * within a read data sequence).\n + * If DataSequenceInOrder is set to Yes, a target may retry at most the + * last R2T, and an initiator may at most request retransmission for the + * last read data sequence. For this reason, if ErrorRecoveryLevel is + * 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 ((const uint8_t *) "DataSequenceInOrder\0\0\0\0") + +/** + * @brief Login/Text Operational Session Text Key: Error recovery level. + * + * @verbatim + * Use: LO + * Senders: Initiator and target + * Scope: SW + * ErrorRecoveryLevel= + * Default is 0. + * @endverbatim + * Result function is Minimum.\n + * The initiator and target negotiate the recovery level supported. + * Recovery levels represent a combination of recovery capabilities. + * Each recovery level includes all the capabilities of the lower + * recovery levels and adds some new ones to them.\n + * In the description of recovery mechanisms, certain recovery classes + * are specified. + */ +#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. + * + * @verbatim + * Use: ALL + * Senders: Initiator and target + * Scope: specific key dependent + * X-reversed.vendor.dns_name.do_something= + * @endverbatim + * Keys with this format are used for private extension purposes. These + * keys always start with X- if unregistered with IANA (private). New + * public keys (if registered with IANA via an IETF Review RFC5226) no + * longer have an X# name prefix requirement; implementers may propose + * any intuitive unique name.\n + * For unregistered keys, to identify the vendor we suggest using the + * reversed DNS-name as a prefix to the key-proper.\n + * The part of key-name following X- MUST conform to the format for + * key-name.\n + * 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 ((const uint8_t *) "X-reversed.vendor\0\0\0\0\0\0") + +/** + * @brief Login/Text Operational Session Text Key: Task reporting. + * + * @verbatim + * Use: LO + * Senders: Initiator and target + * Scope: SW + * Irrelevant when: SessionType=Discovery + * TaskReporting= + * Default is RFC3720. + * @endverbatim + * This key is used to negotiate the task completion reporting semantics + * from the SCSI target. The following table describes the semantics + * that an iSCSI target MUST support for respective negotiated key + * values. Whenever this key is negotiated, at least the RFC3720 and + * ResponseFence values MUST be offered as options by the negotiation + * originator. + * Name | Description + * :-------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------ + * | RFC3720 | RFC 3720-compliant semantics. Response fencing is not guaranteed, and fast completion of multi-task aborting is not supported. + * | ResponseFence | Response Fence semantics MUST be supported in reporting task completions. + * | FastAbort | Updated fast multi-task abort semantics defined in MUST be supported. Support for the Response. Fence is implied - i.e., semantics MUST be supported as well. + * + * 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 ((const uint8_t *) "TaskReporting\0\0") + +/** + * @brief Login/Text Operational Session Text Key: X Node architecture. + * * @verbatim - * TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.8675309 - * TargetAddress=10.1.0.45:5300,1 - * TargetAddress=10.1.1.46:5300,1 - * TargetAddress=10.1.0.47:5300,2 - * TargetAddress=10.1.1.48:5300,2 - * TargetAddress=10.1.1.49:5300,3 + * Use: LO, Declarative + * Senders: Initiator and target + * Scope: SW + * X#NodeArchitecture= + * Default is None. + * Examples: + * X#NodeArchitecture=ExampleOS/v1234,ExampleInc_SW_Initiator/1.05a + * X#NodeArchitecture=ExampleInc_HW_Initiator/4010,Firmware/2.0.0.5 + * X#NodeArchitecture=ExampleInc_SW_Initiator/2.1,CPU_Arch/i686 * @endverbatim - * In this example, any of the target addresses can be used to reach the - * same target. A single-connection session can be established to any - * of these TCP addresses. A multiple-connection session could span - * addresses .45 and .46 or .47 and .48 but cannot span any other - * combination. A TargetAddress with its own tag (.49) cannot be - * combined with any other address within the same session.\n - * This SendTargets response does not indicate whether .49 supports - * multiple connections per session; it is communicated via the - * MaxConnections text key upon login to the target. + * This document does not define the structure or content of the list of + * values.\n + * The initiator or target declares the details of its iSCSI node + * architecture to the remote endpoint. These details may include, but + * are not limited to, iSCSI vendor software, firmware, or hardware + * versions; the OS version; or hardware architecture. This key may be + * declared on a Discovery session or a Normal session.\n + * The length of the key value (total length of the list-of-values) MUST + * NOT be greater than 255 bytes.\n + * X#NodeArchitecture MUST NOT be redeclared during the Login Phase.\n + * Functional behavior of the iSCSI node (this includes the iSCSI + * protocol logic - the SCSI, iSCSI, and TCP/IP protocols) MUST NOT + * depend on the presence, absence, or content of the X#NodeArchitecture + * key. The key MUST NOT be used by iSCSI nodes for interoperability or + * for exclusion of other nodes. To ensure proper use, key values + * SHOULD be set by the node itself, and there SHOULD NOT be provisions + * for the key values to contain user-defined text.\n + * Nodes implementing this key MUST choose one of the following + * implementation options:\n + * - only transmit the key, + * - only log the key values received from other nodes, or + * - both transmit and log the key values. + * + * Each node choosing to implement transmission of the key values MUST + * be prepared to handle the response of iSCSI nodes that do not + * understand the key.\n + * Nodes that implement transmission and/or logging of the key values + * may also implement administrative mechanisms that disable and/or + * change the logging and key transmission details.\n + * Thus, a valid behavior for this key may be that a node is completely + * 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 ((const uint8_t *) "X#NodeArchitecture\0\0\0\0\0") + +/** + * @brief Login/Text Operational Session Text Key: IFMarker (obseleted). + * + * This document obsoletes the following keys defined in RFC3720:\n + * IFMarker, OFMarker, OFMarkInt, and IFMarkInt. However, iSCSI + * mplementations compliant to this document may still receive these + * obsoleted keys - i.e., in a responder role - in a text negotiation.\n + * When an IFMarker or OFMarker key is received, a compliant iSCSI + * implementation SHOULD respond with the constant "Reject" value. The + * implementation MAY alternatively respond with a "No" value.\n + * However, the implementation MUST NOT respond with a "NotUnderstood" + * value for either of these keys.\n + * When an IFMarkInt or OFMarkInt key is received, a compliant iSCSI + * implementation MUST respond with the constant "Reject" value. The + * implementation MUST NOT respond with a "NotUnderstood" value for + * either of these keys. + */ +#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). + * + * This document obsoletes the following keys defined in RFC3720:\n + * IFMarker, OFMarker, OFMarkInt, and IFMarkInt. However, iSCSI + * mplementations compliant to this document may still receive these + * obsoleted keys - i.e., in a responder role - in a text negotiation.\n + * When an IFMarker or OFMarker key is received, a compliant iSCSI + * implementation SHOULD respond with the constant "Reject" value. The + * implementation MAY alternatively respond with a "No" value.\n + * However, the implementation MUST NOT respond with a "NotUnderstood" + * value for either of these keys.\n + * When an IFMarkInt or OFMarkInt key is received, a compliant iSCSI + * implementation MUST respond with the constant "Reject" value. The + * implementation MUST NOT respond with a "NotUnderstood" value for + * either of these keys. + */ +#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). + * + * This document obsoletes the following keys defined in RFC3720:\n + * IFMarker, OFMarker, OFMarkInt, and IFMarkInt. However, iSCSI + * mplementations compliant to this document may still receive these + * obsoleted keys - i.e., in a responder role - in a text negotiation.\n + * When an IFMarker or OFMarker key is received, a compliant iSCSI + * implementation SHOULD respond with the constant "Reject" value. The + * implementation MAY alternatively respond with a "No" value.\n + * However, the implementation MUST NOT respond with a "NotUnderstood" + * value for either of these keys.\n + * When an IFMarkInt or OFMarkInt key is received, a compliant iSCSI + * implementation MUST respond with the constant "Reject" value. The + * implementation MUST NOT respond with a "NotUnderstood" value for + * either of these keys. + */ +#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). + * + * This document obsoletes the following keys defined in RFC3720:\n + * IFMarker, OFMarker, OFMarkInt, and IFMarkInt. However, iSCSI + * mplementations compliant to this document may still receive these + * obsoleted keys - i.e., in a responder role - in a text negotiation.\n + * When an IFMarker or OFMarker key is received, a compliant iSCSI + * implementation SHOULD respond with the constant "Reject" value. The + * implementation MAY alternatively respond with a "No" value.\n + * However, the implementation MUST NOT respond with a "NotUnderstood" + * value for either of these keys.\n + * When an IFMarkInt or OFMarkInt key is received, a compliant iSCSI + * implementation MUST respond with the constant "Reject" value. The + * implementation MUST NOT respond with a "NotUnderstood" value for + * either of these keys. + */ +#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. +#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION 0x0 + +/// Login request Next Stage (NSG) flags: LoginOperationalNegotiation. +#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 + +/// Login request Next Stage (NSG) flags: Reserved for future usage, may NOT be used. +#define ISCSI_LOGIN_REQ_FLAGS_BEXT_STAGE_RESERVED 0x2 + +/// Login request Next Stage (NSG) flags: FullFeaturePhase. +#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_FULL_FEATURE_PHASE 0x3 + +/** + * @brief Login request flags: Next Stage (NSG): First bit of the two bits. + * + * The Login negotiation requests and responses are associated + * with a specific stage in the session (SecurityNegotiation,\n + * LoginOperationalNegotiation, FullFeaturePhase) and may indicate the + * 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 + +/** + * @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 + * LoginOperationalNegotiation, FullFeaturePhase) and may indicate the + * 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_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 (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) (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. +#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_SECURITY_NEGOTIATION 0x0 + +/// Login request Current Stage (CSG) flags: LoginOperationalNegotiation. +#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 + +/// Login request Current Stage (CSG) flags: Reserved for future usage, may NOT be used. +#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_RESERVED 0x2 + +/// Login request Current Stage (CSG) flags: FullFeaturePhase. +#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_FULL_FEATURE_PHASE 0x3 + +/** + * @brief Login request flags: Current Stage (CSG): First 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_FIRST_BIT 2 + +/** + * @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_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 (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) (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)) + + +/** + * @brief Login request flags: Continue. + * + * (C) When set to 1, this bit indicates that the text (set of key=value + * pairs) in this Login Request is not complete (it will be continued on + * subsequent Login Requests); otherwise, it indicates that this Login + * Request ends a set of key=value pairs. A Login Request with the + * C bit set to 1 MUST have the T bit set to 0. + */ +#define ISCSI_LOGIN_REQ_FLAGS_CONTINUE (1 << 6) + +/** + * @brief Login request flags: Transmit. + * + * (T) When set to 1, this bit indicates that the initiator is ready to + * transit to the next stage.\n + * If the T bit is set to 1 and the NSG is set to FullFeaturePhase, then + * this also indicates that the initiator is ready for the Login + * Final-Response. + */ +#define ISCSI_LOGIN_REQ_FLAGS_TRANSIT (1 << 7) + + +/** + * @brief iSCSI Login Request packet data. + * + * After establishing a TCP connection between an initiator and a + * target, the initiator MUST start a Login Phase to gain further access + * to the target's resources. + * + * The Login Phase consists of a sequence of Login Requests and Login + * Responses that carry the same Initiator Task Tag. + * + * Login Requests are always considered as immediate. + */ +typedef struct __attribute__((packed)) iscsi_login_req_packet { + /// Always 0x03 according to iSCSI specification. + uint8_t opcode; + + /// Login request flags. + int8_t flags; + + /** + * @brief Version-max indicates the maximum version number supported. + * + * All Login Requests within the Login Phase MUST carry the same + * Version-max. Currently, this is always 0.\n + * The target MUST use the value presented with the first Login Request. + */ + uint8_t version_max; + + /** + * @brief Version-min indicates the minimum version number supported. + * + * All Login Requests within the Login Phase MUST carry the same + * Version-min. The target MUST use the value presented with the first + * Login Request. Always 0 for now. + */ + uint8_t version_min; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// Initiator Session ID (ISID). + iscsi_isid isid; + + /** + * @brief Target Session Identifying Handle (TSIH). + * + * The TSIH must be set in the first Login Request. The reserved value + * 0 MUST be used on the first connection for a new session. Otherwise, + * the TSIH sent by the target at the conclusion of the successful login + * of the first connection for this session MUST be used. The TSIH + * identifies to the target the associated existing session for this new + * connection.\n + * All Login Requests within a Login Phase MUST carry the same TSIH. + * The target MUST check the value presented with the first Login + * Request. + */ + uint16_t tsih; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /** + * @brief Connection ID (CID). + * + * The CID provides a unique ID for this connection within the session.\n + * All Login Requests within the Login Phase MUST carry the same CID. + * The target MUST use the value presented with the first Login Request.\n + * A Login Request with a non-zero TSIH and a CID equal to that of an + * existing connection implies a logout of the connection followed by a + * login. + */ + uint16_t cid; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /** + * @brief CmdSN. + * + * The CmdSN is either the initial command sequence number of a session + * (for the first Login Request of a session - the "leading" login) or + * the command sequence number in the command stream if the login is for + * a new connection in an existing session.\n + * Examples: + * - Login on a leading connection: If the leading login carries the + * CmdSN 123, all other Login Requests in the same Login Phase carry + * the CmdSN 123, and the first non-immediate command in the Full + * Feature Phase also carries the CmdSN 123. + * - Login on other than a leading connection: If the current CmdSN at + * the time the first login on the connection is issued is 500, then + * that PDU carries CmdSN=500. Subsequent Login Requests that are + * needed to complete this Login Phase may carry a CmdSN higher than + * 500 if non-immediate requests that were issued on other connections + * in the same session advance the CmdSN. + * + * If the Login Request is a leading Login Request, the target MUST use + * the value presented in the CmdSN as the target value for the + * ExpCmdSN. + */ + uint32_t cmd_sn; + + /** + * @brief ExpStatSN. + * + * For the first Login Request on a connection, this is the ExpStatSN + * for the old connection, and this field is only valid if the Login + * Request restarts a connection.\n + * For subsequent Login Requests, it is used to acknowledge the Login + * Responses with their increasing StatSN values. + */ + uint32_t exp_stat_sn; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2[2]; + + /** + * @brief Data segment - Login Parameters in Text Request Format. + * + * The initiator MUST provide some basic parameters in order + * 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_scsi_ds_cmd_data ds_cmd_data; +} iscsi_login_req_packet; + + +/// Login response Next Stage (NSG) flags: SecurityNegotiation. +#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION 0x0 + +/// Login response Next Stage (NSG) flags: LoginOperationalNegotiation. +#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 + +/// Login response Next Stage (NSG) flags: Reserved for future usage, may NOT be used. +#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_RESERVED 0x2 + +/// Login response Next Stage (NSG) flags: FullFeaturePhase. +#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FULL_FEATURE_PHASE 0x3 + +/** + * @brief Login response flags: Next Stage (NSG): First bit of the two bits. + * + * The Login negotiation requests and responses are associated + * with a specific stage in the session (SecurityNegotiation, + * LoginOperationalNegotiation, FullFeaturePhase) and may indicate the + * 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 + +/** + * @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, + * LoginOperationalNegotiation, FullFeaturePhase) and may indicate the + * 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_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 (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) (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. +#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_SECURITY_NEGOTIATION 0x0 + +/// Login response Current Stage (CSG) flags: LoginOperationalNegotiation. +#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 + +/// Login response Current Stage (CSG) flags: Reserved for future usage, may NOT be used. +#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_RESERVED 0x2 + +/// Login response Current Stage (CSG) flags: FullFeaturePhase. +#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FULL_FEATURE_PHASE 0x3 + +/** + * @brief Login response flags: Current Stage (CSG): First 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_RESPONSE_FLAGS_CURRENT_STAGE_FIRST_BIT 2 + +/** + * @brief Login response flags: Current Stage (CSG): First 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_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 (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) (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)) + + +/** + * @brief Login response flags: Continue. + * + * (C) When set to 1, this bit indicates that the text (set of key=value + * pairs) in this Login Response is not complete (it will be continued + * on subsequent Login Responses); otherwise, it indicates that this + * Login Response ends a set of key=value pairs. A Login Response with + * the C bit set to 1 MUST have the T bit set to 0. + */ +#define ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE (1 << 6) + +/** + * @brief Login response flags: Transmit. + * + * (T) The T bit is set to 1 as an indicator of the end of the stage. If + * the T bit is set to 1 and the NSG is set to FullFeaturePhase, then + * this is also the Login Final-Response. A T bit of 0 indicates a + * "partial" response, which means "more negotiation needed".\n + * A Login Response with the T bit set to 1 MUST NOT contain key=value + * pairs that may require additional answers from the initiator within + * the same stage.\n + * If the Status-Class is 0, the T bit MUST NOT be set to 1 if the T bit + * in the request was set to 0. + */ +#define ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT (1 << 7) + + +/** + * @brief Login response status class: Success. + * + * Indicates that the iSCSI target successfully received, understood, + * and accepted the request. The numbering fields (StatSN, ExpCmdSN, + * MaxCmdSN) are only valid if Status-Class is 0. + */ +#define ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SUCCESS 0x00 + +/** + * @brief Login response status details: Success. + * + * Login is proceeding OK. If the response T bit is set to 1 in both the + * request and the matching response, and the NSG is set to + * FullFeaturePhase in both the request and the matching response, the + * Login Phase is finished, and the initiator may proceed to issue SCSI + * commands. + */ +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SUCCESS 0x00 + + +/** + * @brief Login response status class: Redirection. + * + * Indicates that the initiator must take further action + * to complete the request. This is usually due to the + * target moving to a different address. All of the redirection + * Status-Class responses MUST return one or more text key + * parameters of the type "TargetAddress", which indicates the + * target's new address. A redirection response MAY be issued by + * a target prior to or after completing a security negotiation if + * a security negotiation is required. A redirection SHOULD be + * accepted by an initiator, even without having the target + * complete a security negotiation if any security negotiation is + * required, and MUST be accepted by the initiator after the + * completion of the security negotiation if any security + * negotiation is required. + */ +#define ISCSI_LOGIN_RESPONSE_STATUS_CLASS_REDIRECT 0x01 + +/** + * @brief Login response status details: Temporarily redirected. + * + * The requested iSCSI Target Name (ITN) has temporarily moved + * to the address provided. + */ +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_REDIRECT_TEMP 0x01 + +/** + * @brief Login response status details: Permanently redirected. + * + * The requested ITN has permanently moved to the address provided. + */ +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_REDIRECT_PERM 0x02 + + +/** + * @brief Login response status class: Initiator Error (not a format error). + * + * Indicates that the initiator most likely caused the error.\n + * This MAY be due to a request for a resource for which the + * initiator does not have permission. The request should + * not be tried again. + */ +#define ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR 0x02 + +/// Login response status details: Miscellaneous iSCSI initiator errors. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC 0x00 + +/// Login response status details: The initiator could not be successfully authenticated or target authentication is not supported. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_ERR 0x01 + +/// Login response status details: The initiator is not allowed access to the given target. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_FAIL 0x02 + +/// Login response status details: The requested iSCSI Target Name (ITN) does not exist at this address. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_NOT_FOUND 0x03 + +/// Login response status details: The requested ITN has been removed, and no forwarding address is provided. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_TARGET_REMOVED 0x04 + +/// Login response status details: The requested iSCSI version range is not supported by the target. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_WRONG_VERSION 0x05 + +/// Login response status details: Too many connections on this Session ID (SSID). +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_TOO_MANY_CONNECTIONS 0x06 + +/// Login response status details: Missing parameters (e.g. iSCSI Initiator Name and/or Target Name). +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISSING_PARAMETER 0x07 + +/// Login response status details: Target does not support session spanning to this connection (address). +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_NO_SESSION_SPANNING 0x08 + +/// Login response status details: Target does not support this type of session or not from this initiator. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_SESSION_NO_SUPPORT 0x09 + +/// Login response status details: Attempt to add a connection to a non-existent session. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_SESSION_NO_EXIST 0x0A + +/// Login response status details: Invalid request type during login. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_INVALID_LOGIN_REQ_TYPE 0x0B + + +/** + * @brief Login response status class: Target Error. + * + * Indicates that the target sees no errors in the + * initiator's Login Request but is currently incapable of + * fulfilling the request. The initiator may retry the same Login + * Request later. + */ +#define ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SERVER_ERR 0x03 + +/// Login response status details: Target hardware or software error. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_TARGET_ERROR 0x00 + +/// Login response status details: The iSCSI service or target is not currently operational. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_SERVICE_UNAVAILABLE 0x01 + +/// The target has insufficient session, connection, or other resources. +#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_OUT_OF_RESOURCES 0x02 + + +/** + * @brief iSCSI Login Response packet data. + * + * The Login Response indicates the progress and/or end of the Login + * Phase. + */ +typedef struct __attribute__((packed)) iscsi_login_response_packet { + /// Always 0x23 according to iSCSI specification. + uint8_t opcode; + + /// Login response flags. + int8_t flags; + + /** + * @brief This is the highest version number supported by the target. + * + * All Login Responses within the Login Phase MUST carry the same + * Version-max. + */ + uint8_t version_max; + + /** + * @brief Version-active indicates the highest version supported by the target and initiator. + * + * If the target does not support a version within the + * range specified by the initiator, the target rejects the login and + * this field indicates the lowest version supported by the target. + * All Login Responses within the Login Phase MUST carry the same + * Version-active.\n + * The initiator MUST use the value presented as a response to the first + * Login Request. + */ + uint8_t version_active; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// Initiator Session ID (ISID). + iscsi_isid isid; + + /** + * @brief Target Session Identifying Handle (TSIH). + * + * The TSIH is the target-assigned session-identifying handle. Its + * internal format and content are not defined by this protocol, except + * for the value 0, which is reserved. With the exception of the Login + * Final-Response in a new session, this field should be set to the TSIH + * provided by the initiator in the Login Request. For a new session, + * the target MUST generate a non-zero TSIH and ONLY return it in the + * Login Final-Response. + */ + uint16_t tsih; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved; + + /** + * @brief StatSN. + * + * For the first Login Response (the response to the first Login + * Request), this is the starting status sequence number for the + * connection. The next response of any kind - including the next + * Login Response, if any, in the same Login Phase - will carry this + * number + 1. This field is only valid if the Status-Class is 0. + */ + uint32_t stat_sn; + + /// ExpCmdSN. + uint32_t exp_cmd_sn; + + /// MaxCmdSN. + uint32_t max_cmd_sn; + + /** + * @brief Status-class. + * + * Status-class (see above for details). If the Status-Class is + * not 0, the initiator and target MUST close the TCP connection + * If the target wishes to reject the Login Request for more than one + * reason, it should return the primary reason for the rejection. + */ + uint8_t status_class; + + /// Status-detail. + uint8_t status_detail; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved2; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved3; + + /** + * @brief Data segment - Login Parameters in Text Request Format. + * + * The target MUST provide some basic parameters in order to enable the + * initiator to determine if it is connected to the correct port and the + * initial text parameters for the security exchange.\n + * All the rules specified for Text Responses also hold for Login + * Responses. + */ + iscsi_scsi_ds_cmd_data ds_cmd_data; +} iscsi_login_response_packet; + + +/// Logout request reason code: Close the session. All commands associated with the session (if any) are terminated. +#define ISCSI_LOGOUT_REQ_REASON_CODE_CLOSE_SESSION 0x00 + +/// Logout request reason code: Close the connection. All commands associated with the connection (if any) are terminated. +#define ISCSI_LOGOUT_REQ_REASON_CODE_CLOSE_CONNECTION 0x01 + +/// Logout request reason code: Remove the connection for recovery. The connection is closed, and all commands associated with it, if any, are to be prepared for a new allegiance. +#define ISCSI_LOGOUT_REQ_REASON_CODE_REMOVE_CONNECTION_RECOVERY 0x02 + + +/** + * @brief Logout request implicit reason code: Session reinstatement. + * + * The entire logout discussion in this section is also applicable for + * an implicit Logout realized by way of a connection reinstatement or + * session reinstatement. When a Login Request performs an implicit + * Logout, the implicit Logout is performed as if having the reason + * codes specified below: + */ +#define ISCSI_LOGOUT_REQ_REASON_CODE_IMPLICIT_SESSION_REINSTATEMENT 0x00 + +/** + * @brief Logout request implicit reason code: Connection reinstatement when the operational ErrorRecoveryLevel < 2. + * + * The entire logout discussion in this section is also applicable for + * an implicit Logout realized by way of a connection reinstatement or + * session reinstatement. When a Login Request performs an implicit + * Logout, the implicit Logout is performed as if having the reason + * codes specified below: + */ +#define ISCSI_LOGOUT_REQ_REASON_CODE_IMPLICIT_CONNECTION_REINSTATEMENT 0x01 + +/** + * @brief Logout request implicit reason code: Connection reinstatement when the operational ErrorRecoveryLevel = 2. + * + * The entire logout discussion in this section is also applicable for + * an implicit Logout realized by way of a connection reinstatement or + * session reinstatement. When a Login Request performs an implicit + * Logout, the implicit Logout is performed as if having the reason + * codes specified below: + */ +#define ISCSI_LOGOUT_REQ_REASON_CODE_IMPLICIT_CONNECTION_REINSTATEMENT_2 0x02 + + +/** + * @brief iSCSI Logout Request packet data. + * + * The Logout Request is used to perform a controlled closing of a + * connection. + * + * An initiator MAY use a Logout Request to remove a connection from a + * session or to close an entire session. + * + * After sending the Logout Request PDU, an initiator MUST NOT send any + * new iSCSI requests on the closing connection. If the Logout Request + * is intended to close the session, new iSCSI requests MUST NOT be sent + * on any of the connections participating in the session. + * + * When receiving a Logout Request with the reason code "close the + * connection" or "close the session", the target MUST terminate all + * pending commands, whether acknowledged via the ExpCmdSN or not, on + * that connection or session, respectively. + * + * When receiving a Logout Request with the reason code "remove the + * connection for recovery", the target MUST discard all requests not + * yet acknowledged via the ExpCmdSN that were issued on the specified + * connection and suspend all data/status/R2T transfers on behalf of + * pending commands on the specified connection. + * + * The target then issues the Logout Response and half-closes the TCP + * connection (sends FIN). After receiving the Logout Response and + * attempting to receive the FIN (if still possible), the initiator MUST + * completely close the logging-out connection. For the terminated + * commands, no additional responses should be expected. + * + * A Logout for a CID may be performed on a different transport + * connection when the TCP connection for the CID has already been + * terminated. In such a case, only a logical "closing" of the iSCSI + * connection for the CID is implied with a Logout. + * + * All commands that were not terminated or not completed (with status) + * and acknowledged when the connection is closed completely can be + * reassigned to a new connection if the target supports connection + * recovery. + * + * If an initiator intends to start recovery for a failing connection, + * it MUST use the Logout Request to "clean up" the target end of a + * failing connection and enable recovery to start, or use the Login + * Request with a non-zero TSIH and the same CID on a new connection for + * the same effect. In sessions with a single connection, the + * connection can be closed and then a new connection reopened. A + * connection reinstatement login can be used for recovery. + * + * A successful completion of a Logout Request with the reason code + * "close the connection" or "remove the connection for recovery" + * results at the target in the discarding of unacknowledged commands + * received on the connection being logged out. These are commands that + * have arrived on the connection being logged out but that have not + * been delivered to SCSI because one or more commands with a smaller + * CmdSN have not been received by iSCSI. The resulting holes in the + * command sequence numbers will have to be handled by appropriate + * recovery, unless the session is also closed. + */ +typedef struct __attribute__((packed)) iscsi_logout_req_packet { + /// Always 6 according to iSCSI specification. + uint8_t opcode; + + /** + * @brief Reason code. + * + * A target implicitly terminates the active tasks due to the iSCSI + * protocol in the following cases: + * -# When a connection is implicitly or explicitly logged out with + * the reason code "close the connection" and there are active + * tasks allegiant to that connection. + * -# When a connection fails and eventually the connection state + * times out and there are active tasks allegiant to that + * connection + * -# When a successful recovery Logout is performed while there are + * active tasks allegiant to that connection and those tasks + * eventually time out after the Time2Wait and Time2Retain periods + * without allegiance reassignment + * -# When a connection is implicitly or explicitly logged out with + * the reason code "close the session" and there are active tasks + * in that session + * + * If the tasks terminated in any of the above cases are SCSI tasks, + * they must be internally terminated as if with CHECK CONDITION status. + * This status is only meaningful for appropriately handling the + * internal SCSI state and SCSI side effects with respect to ordering, + * because this status is never communicated back as a terminating + * status to the initiator. However, additional actions may have to be + * taken at the SCSI level, depending on the SCSI context as defined by + * the SCSI standards (e.g., queued commands and ACA; UA for the next + * command on the I_T nexus in cases a), b), and c) above). After the + * tasks are terminated, the target MUST report a Unit Attention condition + * on the next command processed on any connection for each affected + * I_T_L nexus with the status of CHECK CONDITION, the ASC/ASCQ value + * of 0x47 / 0x7F ("SOME COMMANDS CLEARED BY ISCSI PROTOCOL EVENT"), etc. + */ + int8_t reason_code; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength (MUST be 0 for this PDU). + uint8_t total_ahs_len; + + /// DataSegmentLength (MUST be 0 for this PDU). + uint8_t ds_len[3]; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /** + * @brief Connection ID (CID). + * + * This is the connection ID of the connection to be closed (including + * closing the TCP stream). This field is only valid if the reason code + * is not "close the session". + */ + uint16_t cid; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved3; + + /// CmdSN. + uint32_t cmd_sn; + + /// This is the last ExpStatSN value for the connection to be closed. + uint32_t exp_stat_sn; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved4[2]; + + /// Optional header digest. + iscsi_header_digest hdr_digest; +} iscsi_logout_req_packet; + + +/// Logout response - response code: Connection or session closed successfully. +#define ISCSI_LOGOUT_RESPONSE_CLOSED_SUCCESSFULLY 0x00 + +/// Logout response - response code: Connection ID (CID) not found. +#define ISCSI_LOGOUT_RESPONSE_CID_NOT_FOUND 0x01 + +/// Logout response - response code: Connection recovery is not supported (i.e., the Logout reason code was "remove the connection for recovery" and the target does not support it as indicated by the operational ErrorRecoveryLevel). +#define ISCSI_LOGOUT_RESPONSE_CONNECTION_RECOVERY_NOT_SUPPORTED 0x02 + +/// Logout response - response code: Cleanup failed for various reasons. +#define ISCSI_LOGOUT_RESPONSE_CLEANUP_FAILED 0x03 + +/** + * @brief iSCSI Logout Response packet data. + * + * The Logout Response is used by the target to indicate if the cleanup + * operation for the connection(s) has completed. + * + * After Logout, the TCP connection referred by the CID MUST be closed + * at both ends (or all connections must be closed if the logout reason + * was session close). + */ +typedef struct __attribute__((packed)) iscsi_logout_response_packet { + /// Always 0x26 according to iSCSI specification. + uint8_t opcode; + + /// Reserved for future usage (always MUST be 0x80 for now). + int8_t flags; + + /// Response. + uint8_t response; + + /// Reserved for future usage, always MUST be 0. + uint8_t reserved; + + /// TotalAHSLength (MUST be 0 for this PDU). + uint8_t total_ahs_len; + + /// DataSegmentLength (MUST be 0 for this PDU). + uint8_t ds_len[3]; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved3; + + /// StatSN. + uint32_t stat_sn; + + /// ExpCmdSN. + uint32_t exp_cmd_sn; + + /// MaxCmdSN. + uint32_t max_cmd_sn; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved4; + + /** + * @brief Time2Wait. + * + * If the Logout response code is 0 and the operational + * ErrorRecoveryLevel is 2, this is the minimum amount of time, in + * seconds, to wait before attempting task reassignment. If the Logout + * response code is 0 and the operational ErrorRecoveryLevel is less + * than 2, this field is to be ignored.\n + * This field is invalid if the Logout response code is 1.\n + * If the Logout response code is 2 or 3, this field specifies the + * minimum time to wait before attempting a new implicit or explicit + * logout.\n + * If Time2Wait is 0, the reassignment or a new Logout may be attempted + * immediately. + */ + uint16_t time_wait; + + /** + * @brief Time2Retain. + * + * If the Logout response code is 0 and the operational + * ErrorRecoveryLevel is 2, this is the maximum amount of time, in + * seconds, after the initial wait (Time2Wait) that the target waits for + * the allegiance reassignment for any active task, after which the task + * state is discarded. If the Logout response code is 0 and the + * operational ErrorRecoveryLevel is less than 2, this field is to be + * ignored.\n + * This field is invalid if the Logout response code is 1.\n + * If the Logout response code is 2 or 3, this field specifies the + * maximum amount of time, in seconds, after the initial wait + * (Time2Wait) that the target waits for a new implicit or explicit + * logout.\n + * If it is the last connection of a session, the whole session state is + * discarded after Time2Retain.\n + * If Time2Retain is 0, the target has already discarded the connection + * (and possibly the session) state along with the task states. No + * reassignment or Logout is required in this case. + */ + uint16_t time_retain; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved5; + + /// Optional header digest. + iscsi_header_digest hdr_digest; +} iscsi_logout_response_packet; + + +/// Selective Negative / Sequence Number Acknowledgment (SNACK) request: Data/R2T SNACK: requesting retransmission of one or more Data-In or R2T PDUs. +#define ISCSI_SNACK_REQ_TYPE_DATA_R2T_SNACK 0x00 + +/// Selective Negative / Sequence Number Acknowledgment (SNACK) request: +#define ISCSI_SNACK_REQ_TYPE_STATUS_SNACK 0x01 // Status SNACK: requesting retransmission of one or more + // numbered responses + +/** + * @brief Selective Negative / Sequence Number Acknowledgment (SNACK) request: DataACK: positively acknowledges Data-In PDUs. + * + * If an initiator operates at ErrorRecoveryLevel 1 or higher, it MUST + * issue a SNACK of type DataACK after receiving a Data-In PDU with the + * A bit set to 1. However, if the initiator has detected holes in the + * input sequence, it MUST postpone issuing the SNACK of type DataACK + * until the holes are filled. An initiator MAY ignore the A bit if it + * deems that the bit is being set aggressively by the target (i.e., + * before the MaxBurstLength limit is reached).\n + * The DataACK is used to free resources at the target and not to + * request or imply data retransmission.\n + * An initiator MUST NOT request retransmission for any data it had + * already acknowledged */ -#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_SEND_TARGETS "SendTargets" +#define ISCSI_SNACK_REQ_TYPE_DATA_ACK 0x02 /** - * @brief Login/Text Operational Session Text Key: Initial Ready To Transfer. + * @brief Selective Negative / Sequence Number Acknowledgment (SNACK) request: R-Data SNACK: requesting retransmission of Data-In PDUs with possible resegmentation and status tagging. * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * Irrelevant when: SessionType=Discovery - * InitialR2T= - * Examples: - * I->InitialR2T=No - * T->InitialR2T=No - * Default is Yes. - * @endverbatim - * Result function is OR.\n - * The InitialR2T key is used to turn off the default use of R2T for - * unidirectional operations and the output part of bidirectional - * commands, thus allowing an initiator to start sending data to a - * target as if it has received an initial R2T with Buffer - * Offset=Immediate Data Length and Desired Data Transfer - * Length=(min(FirstBurstLength, Expected Data Transfer Length) - - * Received Immediate Data Length).\n - * The default action is that R2T is required, unless both the initiator - * and the target send this key-pair attribute specifying InitialR2T=No. - * Only the first outgoing data burst (immediate data and/or separate - * PDUs) can be sent unsolicited (i.e., not requiring an explicit R2T). + * If the initiator MaxRecvDataSegmentLength changed between the + * original transmission and the time the initiator requests + * retransmission, the initiator MUST issue a R-Data SNACK.\n + * With R-Data SNACK, the initiator indicates that it discards all the + * unacknowledged data and expects the target to resend it. It also + * expects resegmentation. In this case, the retransmitted Data-In PDUs + * MAY be different from the ones originally sent in order to reflect + * changes in MaxRecvDataSegmentLength. Their DataSN starts with the + * BegRun of the last DataACK received by the target if any was received; + * otherwise, it starts with 0 and is increased by 1 for each resent + * Data-In PDU.\n + * A target that has received a R-Data SNACK MUST return a SCSI Response + * that contains a copy of the SNACK Tag field from the R-Data SNACK in + * the SCSI Response SNACK Tag field as its last or only Response. For + * example, if it has already sent a response containing another value + * in the SNACK Tag field or had the status included in the last Data-In + * PDU, it must send a new SCSI Response PDU. If a target sends more + * than one SCSI Response PDU due to this rule, all SCSI Response PDUs + * must carry the same StatSN. If an initiator attempts to recover a lost + * SCSI Response when more than one response has been sent, the + * target will send the SCSI Response with the latest content known to + * the target, including the last SNACK Tag for the command.\n + * For considerations in allegiance reassignment of a task to a + * connection with a different MaxRecvDataSegmentLength. */ -#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_INITIAL_R2T "InitialR2T" +#define ISCSI_SNACK_REQ_TYPE_R_DATA_SNACK 0x03 -/** - * @brief Login/Text Operational Session Text Key: Immediate data. - * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * Irrelevant when: SessionType=Discovery - * ImmediateData= - * Default is Yes. - * @endverbatim - * Result function is AND.\n - * The initiator and target negotiate support for immediate dataTo - * 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 - * If ImmediateData is set to Yes and InitialR2T is set to Yes - * (default), then only immediate data are accepted in the first burst. - * If ImmediateData is set to No and InitialR2T is set to Yes, then the - * initiator MUST NOT send unsolicited data and the target MUST reject - * unsolicited data with the corresponding response code.\n - * If ImmediateData is set to No and InitialR2T is set to No, then the - * initiator MUST NOT send unsolicited immediate data but MAY send one - * unsolicited burst of Data-OUT PDUs.\n - * If ImmediateData is set to Yes and InitialR2T is set to No, then the - * initiator MAY send unsolicited immediate data and/or one unsolicited - * burst of Data-OUT PDUs.\n - * The following table is a summary of unsolicited data options: - * InitialR2T | ImmediateData | Unsolicited Data-Out PDUs | ImmediateData - * :--------- | :------------ | :------------------------ | :------------ - * | No | No | Yes | No | - * | No | Yes | Yes | Yes | - * | Yes | No | No | No | - * | Yes | Yes | No | Yes | - */ -#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IMMEDIATE_DATA "ImmediateData" /** - * @brief Login/Text Operational Session Text Key: Maximum receive DataSegmentLength. + * @brief iSCSI SNACK Request packet data. * - * @verbatim - * Use: ALL, Declarative - * Senders: Initiator and target - * Scope: CO - * MaxRecvDataSegmentLength= - * Default is 8192 bytes. - * @endverbatim - * The initiator or target declares the maximum data segment length in - * bytes it can receive in an iSCSI PDU.\n - * The transmitter (initiator or target) is required to send PDUs with a - * data segment that does not exceed MaxRecvDataSegmentLength of the - * receiver.\n - * A target receiver is additionally limited by MaxBurstLength for - * solicited data and FirstBurstLength for unsolicited dataAn - * initiator MUST NOT send solicited PDUs exceeding MaxBurstLength nor - * unsolicited PDUs exceeding FirstBurstLength (or FirstBurstLength- - * Immediate Data Length if immediate data were sent). + * If the implementation supports ErrorRecoveryLevel greater than zero, + * it MUST support all SNACK types. + * + * The SNACK is used by the initiator to request the retransmission of + * numbered responses, data, or R2T PDUs from the target. The SNACK + * Request indicates the numbered responses or data "runs" whose + * retransmission is requested, where the run starts with the first + * StatSN, DataSN, or R2TSN whose retransmission is requested and + * indicates the number of Status, Data, or R2T PDUs requested, + * including the first. 0 has special meaning when used as a starting + * number and length: + * + * - When used in RunLength, it means all PDUs starting with the + * initial. + * + * - When used in both BegRun and RunLength, it means all + * unacknowledged PDUs. + * + * The numbered response(s) or R2T(s) requested by a SNACK MUST be + * delivered as exact replicas of the ones that the target transmitted + * originally, except for the fields ExpCmdSN, MaxCmdSN, and ExpDataSN, + * which MUST carry the current values. R2T(s)requested by SNACK MUST + * also carry the current value of the StatSN. + * + * The numbered Data-In PDUs requested by a Data SNACK MUST be delivered + * as exact replicas of the ones that the target transmitted originally, + * except for the fields ExpCmdSN and MaxCmdSN, which MUST carry the + * current values; and except for resegmentation. + * + * Any SNACK that requests a numbered response, data, or R2T that was + * not sent by the target or was already acknowledged by the initiator + * MUST be rejected with a reason code of "Protocol Error". */ -#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_RECV_DS_LEN "MaxRecvDataSegmentLength" +typedef struct __attribute__((packed)) iscsi_snack_req_packet { + /// Always 0x10 according to iSCSI specification. + uint8_t opcode; + + /** + * @brief Type. + * + * Data/R2T SNACK, Status SNACK, or R-Data SNACK for a command MUST + * precede status acknowledgment for the given command. + */ + int8_t type; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// LUN or Reserved. + uint64_t lun; + + /** + * @brief Initiator Task Tag (ITT). + * + * For a Status SNACK and DataACK, the Initiator Task Tag MUST be set to + * the reserved value 0xFFFFFFFF. In all other cases, the Initiator + * Task Tag field MUST be set to the Initiator Task Tag of the + * referenced command. + */ + uint32_t init_task_tag; + + /** + * @brief Target Transfer Tag (TTT). + * + * For a R-Data SNACK, this field MUST contain a value that is different + * from 0 or 0xFFFFFFFF and is unique for the task (identified by the + * Initiator Task Tag). This value MUST be copied by the iSCSI target + * in the last or only SCSI Response PDU it issues for the command.\n + * For DataACK, the Target Transfer Tag MUST contain a copy of the + * Target Transfer Tag and LUN provided with the SCSI Data-In PDU with + * the A bit set to 1.\n + * In all other cases, the Target Transfer Tag field MUST be set to the + * reserved value 0xFFFFFFFF. + */ + uint32_t target_xfer_snack_tag; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved2; + + /// ExpStatSN. + uint32_t exp_stat_sn; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved3; + + /** + * @brief BegRun. + * + * This field indicates the DataSN, R2TSN, or StatSN of the first PDU + * whose retransmission is requested (Data/R2T and Status SNACK), or the + * next expected DataSN (DataACK SNACK).\n + * A BegRun of 0, when used in conjunction with a RunLength of 0, means + * "resend all unacknowledged Data-In, R2T or Response PDUs". + * BegRun MUST be 0 for a R-Data SNACK. + */ + uint32_t beg_run; + + /** + * @brief RunLength. + * + * This field indicates the number of PDUs whose retransmission is + * requested.\n + * A RunLength of 0 signals that all Data-In, R2T, or Response PDUs + * carrying the numbers equal to or greater than BegRun have to be + * resent.\n + * The RunLength MUST also be 0 for a DataACK SNACK in addition to a + * R-Data SNACK. + */ + uint32_t run_len; + + /// Optional header digest. + iscsi_header_digest hdr_digest; +} iscsi_snack_req_packet; + + +/// iSCSI Reject packet data: Reserved, original PDU can't be resent. +#define ISCSI_REJECT_REASON_RESERVED 0x01 /** - * @brief Login/Text Operational Session Text Key: Maximum burst length. + * @brief iSCSI Reject packet data: Data (payload) digest error, original PDU can be resent. * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * Irrelevant when: SessionType=Discovery - * MaxBurstLength= - * Default is 262144 (256 KB). - * @endverbatim - * Result function is Minimum.\n - * The initiator and target negotiate the maximum SCSI data payload in - * bytes in a Data-In or a solicited Data-Out iSCSI sequence. A - * 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. + * For iSCSI, Data-Out PDU retransmission is only done if the + * target requests retransmission with a recovery R2T. However, + * if this is the data digest error on immediate data, the + * initiator may choose to retransmit the whole PDU, including + * the immediate data. */ -#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_BURST_LEN "MaxBurstLength" +#define ISCSI_REJECT_REASON_DATA_DIGEST_ERR 0x02 + +/// iSCSI Reject reason packet data: SNACK Reject (original PDU can be resent). +#define ISCSI_REJECT_REASON_SNACK_REJECT 0x03 + +/// iSCSI Reject reason packet data: Protocol Error (e.g., SNACK Request for a status that was already acknowledged). Original PDU can't be resent. +#define ISCSI_REJECT_REASON_PROTOCOL_ERR 0x04 + +/// iSCSI Reject reason packet data: Command not supported (original PDU can't be resent). +#define ISCSI_REJECT_REASON_COMMAND_NOT_SUPPORTED 0x05 + +/// iSCSI Reject reason packet data: Immediate command reject - too many immediate commands (original PDU can be resent). +#define ISCSI_REJECT_REASON_TOO_MANY_IMMEDIATE_COMMANDS 0x06 + +/// iSCSI Reject reason packet data: Task in progress (original PDU can't be resent). +#define ISCSI_REJECT_REASON_TASK_IN_PROGRESS 0x07 + +/// iSCSI Reject reason packet data: Invalid data ack (original PDU can't be resent). +#define ISCSI_REJECT_REASON_INVALID_DATA_ACK 0x08 /** - * @brief Login/Text Operational Session Text Key: First burst length. + * @brief iSCSI Reject reason packet data: Invalid PDU field, original PDU can't be resent. * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * Irrelevant when: SessionType=Discovery - * Irrelevant when: ( InitialR2T=Yes and ImmediateData=No ) - * FirstBurstLength= - * Default is 65536 (64 KB). - * @endverbatim - * Result function is Minimum.\n - * The initiator and target negotiate the maximum amount in bytes of - * unsolicited data an iSCSI initiator may send to the target during the - * execution of a single SCSI command. This covers the immediate data - * (if any) and the sequence of unsolicited Data-Out PDUs (if any) that - * follow the command.\n - * FirstBurstLength MUST NOT exceed MaxBurstLength. + * A target should use this reason code for all invalid values + * of PDU fields that are meant to describe a task, a response, + * or a data transfer. Some examples are invalid TTT/ITT, + * buffer offset, LUN qualifying a TTT, and an invalid sequence + * number in a SNACK. */ -#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_FIRST_BURST_LEN "FirstBurstLength" +#define ISCSI_REJECT_REASON_INVALID_PDU_FIELD 0x09 + +/// iSCSI Reject reason packet data: Long op reject - Can't generate Target Transfer Tag - out of resources. Original PDU can be resent later. +#define ISCSI_REJECT_REASON_OUT_OF_RESOURCES 0x0A /** - * @brief Login/Text Operational Session Text Key: Default time to wait. + * @brief iSCSI Reject reason packet data: Deprecated; MUST NOT be used. * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * DefaultTime2Wait= - * Default is 2. - * @endverbatim - * Result function is Maximum.\n - * The initiator and target negotiate the minimum time, in seconds, to - * wait before attempting an explicit/implicit logout or an active task - * reassignment after an unexpected connection termination or a - * connection reset.\n - * A value of 0 indicates that logout or active task reassignment can be - * attempted immediately. + * Reason code 0x0B is deprecated and MUST NOT be used by + * implementations. An implementation receiving reason code + * 0x0B MUST treat it as a negotiation failure that terminates + * the Login Phase and the TCP connection. */ -#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_WAIT "DefaultTime2Wait" +#define ISCSI_REJECT_REASON_DEPRECATED 0x0B + +/// iSCSI Reject reason packet data: Waiting for Logout, original PDU can't be resent. +#define ISCSI_REJECT_REASON_WAITING_FOR_LOGOUT 0x0C /** - * @brief Login/Text Operational Session Text Key: Default time to retain. + * @brief iSCSI Reject packet data. * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * DefaultTime2Retain= - * Default is 20. - * @endverbatim - * Result function is Minimum.\n - * The initiator and target negotiate the maximum time, in seconds, - * after an initial wait (Time2Wait), before which an active task - * reassignment is still possible after an unexpected connection - * termination or a connection reset.\n - * This value is also the session state timeout if the connection in - * question is the last LOGGED_IN connection in the session.\n - * A value of 0 indicates that connection/task state is immediately - * discarded by the target. + * This structure will be received or sent, if an iSCSI + * packet was rejected or has been rejected for some reason. */ -#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_RETAIN "DefaultTime2Retain" +typedef struct __attribute__((packed)) iscsi_reject_packet { + /// Always 0x3F according to iSCSI specification. + uint8_t opcode; + + /// Reserved for future usage (always MUST be 0x80 for now). + int8_t flags; + + /** + * @brief Reject reason. + * + * In all the cases in which a pre-instantiated SCSI task is terminated + * because of the reject, the target MUST issue a proper SCSI command + * response with CHECK CONDITION. In these cases in which a status for + * the SCSI task was already sent before the reject, no additional + * status is required. If the error is detected while data from the + * initiator is still expected (i.e., the command PDU did not contain + * all the data and the target has not received a Data-Out PDU with the + * Final bit set to 1 for the unsolicited data, if any, and all + * outstanding R2Ts, if any), the target MUST wait until it receives + * the last expected Data-Out PDUs with the F bit set to 1 before + * sending the Response PDU. + */ + uint8_t reason; + + /// Reserved for future usage, always MUST be 0. + uint8_t reserved; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2; + + /// Always 0xFFFFFFFF for now. + uint32_t tag; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved3; + + /** + * @brief StatSN. + * + * This field carries its usual value and is not related to the + * rejected command. The StatSN is advanced after a Reject. + */ + uint32_t stat_sn; + + /** + * @brief ExpCmdSN. + * + * This field carries its usual value and is not related to the + * rejected command. + */ + uint32_t exp_cmd_sn; + + /** + * @brief MaxCmdSN. + * + * This field carries its usual value and is not related to the + * rejected command. + */ + uint32_t max_cmd_sn; + + /** + * @brief DataSN / Ready To Transfer Sequence Number (R2TSN) or Reserved. + * + * This field is only valid if the rejected PDU is a Data/R2T SNACK and + * the Reject reason code is "Protocol Error". The DataSN/R2TSN is the + * next Data/R2T sequence number that the target would send for the + * task, if any. + */ + uint32_t data_r2t_sn; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved4; + + /// Optional header digest. + iscsi_header_digest hdr_digest; + + /** + * @brief Complete Header of Bad PDU. + * + * The target returns the header (not including the digest) of the + * PDU in error as the data of the response. + */ + iscsi_bhs_packet bad_pdu_hdr; + + /// Vendor-specific data (if any). + uint8_t vendor_data[0]; + + /// Optional data digest. + iscsi_data_digest data_digest; +} iscsi_reject_packet; /** - * @brief Login/Text Operational Session Text Key: Maximum outstanding Ready To Transfer. + * @brief iSCSI NOP-Out packet data. * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * MaxOutstandingR2T= - * Irrelevant when: SessionType=Discovery - * Default is 1. - * @endverbatim - * Result function is Minimum.\n - * The initiator and target negotiate the maximum number of outstanding - * R2Ts per task, excluding any implied initial R2T that might be part - * of that task. An R2T is considered outstanding until the last data - * PDU (with the F bit set to 1) is transferred or a sequence reception - * timeout is encountered for that data sequence. + * NOP-Out may be used by an initiator as a "ping request" to verify + * that a connection/session is still active and all its components are + * operational. The NOP-In response is the "ping echo". + * + * A NOP-Out is also sent by an initiator in response to a NOP-In. + * + * A NOP-Out may also be used to confirm a changed ExpStatSN if another + * PDU will not be available for a long time. + * + * Upon receipt of a NOP-In with the Target Transfer Tag set to a valid + * value (not the reserved value 0xffffffff), the initiator MUST respond + * with a NOP-Out. In this case, the NOP-Out Target Transfer Tag MUST + * contain a copy of the NOP-In Target Transfer Tag. The initiator + * + * SHOULD NOT send a NOP-Out in response to any other received NOP-In, + * in order to avoid lengthy sequences of NOP-In and NOP-Out PDUs sent + * in response to each other. */ -#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_OUTSTANDING_R2T "MaxOutstandingR2T" +typedef struct __attribute__((packed)) iscsi_nop_out_packet { + /// Always 0x00 according to iSCSI specification. + uint8_t opcode; + + /// Reserved for future usage (always MUST be 0x80 for now). + int8_t flags; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// LUN or Reserved. + uint64_t lun; + + /** + * @brief Initiator Task Tag (ITT). + * + * The NOP-Out MUST have the Initiator Task Tag set to a valid value + * only if a response in the form of a NOP-In is requested (i.e., the + * NOP-Out is used as a ping request). Otherwise, the Initiator Task + * Tag MUST be set to 0xFFFFFFFF.\n + * When a target receives the NOP-Out with a valid Initiator Task Tag, + * it MUST respond with a NOP-In Response.\n + * If the Initiator Task Tag contains 0xFFFFFFFF, the I bit MUST be set + * to 1, and the CmdSN is not advanced after this PDU is sent. + */ + uint32_t init_task_tag; + + /** + * @brief Target Transfer Tag (TTT). + * + * The Target Transfer Tag is a target-assigned identifier for the + * operation.\n + * The NOP-Out MUST only have the Target Transfer Tag set if it is + * issued in response to a NOP-In with a valid Target Transfer Tag. In + * this case, it copies the Target Transfer Tag from the NOP-In PDU.\n + * Otherwise, the Target Transfer Tag MUST be set to 0xFFFFFFFF.\n + * When the Target Transfer Tag is set to a value other than 0xFFFFFFFF, + * the LUN field MUST also be copied from the NOP-In. + */ + uint32_t target_xfer_tag; + + /// CmdSN. + uint32_t cmd_sn; -/** - * @brief Login/Text Operational Session Text Key: Data Protocol Data Unit (PDU) in order. - * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * Irrelevant when: SessionType=Discovery - * DataPDUInOrder= - * Default is Yes. - * @endverbatim - * Result function is OR.\n - * "No" is used by iSCSI to indicate that the data PDUs within sequences - * can be in any order. "Yes" is used to indicate that data PDUs within - * sequences have to be at continuously increasing addresses and - * overlays are forbidden. - */ -#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_PDU_IN_ORDER "DataPDUInOrder" + /// ExpStatSN. + uint32_t exp_stat_sn; -/** - * @brief Login/Text Operational Session Text Key: Data sequence in order. - * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * Irrelevant when: SessionType=Discovery - * DataSequenceInOrder= - * Default is Yes. - * @endverbatim - * Result function is OR.\n - * A data sequence is a sequence of Data-In or Data-Out PDUs that end - * with a Data-In or Data-Out PDU with the F bit set to 1. A Data-Out - * sequence is sent either unsolicited or in response to an R2T.\n - * Sequences cover an offset-range.\n - * If DataSequenceInOrder is set to No, data PDU sequences may be - * transferred in any order.\n - * If DataSequenceInOrder is set to Yes, data sequences MUST be - * transferred using continuously non-decreasing sequence offsets (R2T - * buffer offset for writes, or the smallest SCSI Data-In buffer offset - * within a read data sequence).\n - * If DataSequenceInOrder is set to Yes, a target may retry at most the - * last R2T, and an initiator may at most request retransmission for the - * last read data sequence. For this reason, if ErrorRecoveryLevel is - * 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" + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2[2]; -/** - * @brief Login/Text Operational Session Text Key: Error recovery level. - * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * ErrorRecoveryLevel= - * Default is 0. - * @endverbatim - * Result function is Minimum.\n - * The initiator and target negotiate the recovery level supported. - * Recovery levels represent a combination of recovery capabilities. - * Each recovery level includes all the capabilities of the lower - * recovery levels and adds some new ones to them.\n - * In the description of recovery mechanisms, certain recovery classes - * are specified. - */ -#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_ERR_RECOVERY_LEVEL "ErrorRecoveryLevel" + /// Optional header digest. + iscsi_header_digest hdr_digest; + + /** + * @brief DataSegment - Ping Data (optional). + * + * Ping data is reflected in the NOP-In Response. The length of the + * reflected data is limited to MaxRecvDataSegmentLength. The length of + * ping data is indicated by the DataSegmentLength. 0 is a valid value + * for the DataSegmentLength and indicates the absence of ping data. + */ + iscsi_scsi_ds_cmd_data ds_ping_data; + + /// Optional data digest. + iscsi_data_digest data_digest; +} iscsi_nop_out_packet; -/** - * @brief Login/Text Operational Session Text Key: X reversed vendor. - * - * @verbatim - * Use: ALL - * Senders: Initiator and target - * Scope: specific key dependent - * X-reversed.vendor.dns_name.do_something= - * @endverbatim - * Keys with this format are used for private extension purposes. These - * keys always start with X- if unregistered with IANA (private). New - * public keys (if registered with IANA via an IETF Review RFC5226) no - * longer have an X# name prefix requirement; implementers may propose - * any intuitive unique name.\n - * For unregistered keys, to identify the vendor we suggest using the - * reversed DNS-name as a prefix to the key-proper.\n - * The part of key-name following X- MUST conform to the format for - * key-name.\n - * 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" /** - * @brief Login/Text Operational Session Text Key: Task reporting. + * @brief iSCSI NOP-In packet data. * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * Irrelevant when: SessionType=Discovery - * TaskReporting= - * Default is RFC3720. - * @endverbatim - * This key is used to negotiate the task completion reporting semantics - * from the SCSI target. The following table describes the semantics - * that an iSCSI target MUST support for respective negotiated key - * values. Whenever this key is negotiated, at least the RFC3720 and - * ResponseFence values MUST be offered as options by the negotiation - * originator. - * Name | Description - * :-------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------ - * | RFC3720 | RFC 3720-compliant semantics. Response fencing is not guaranteed, and fast completion of multi-task aborting is not supported. - * | ResponseFence | Response Fence semantics MUST be supported in reporting task completions. - * | FastAbort | Updated fast multi-task abort semantics defined in MUST be supported. Support for the Response. Fence is implied - i.e., semantics MUST be supported as well. + * NOP-In is sent by a target as either a response to a NOP-Out, a + * "ping" to an initiator, or a means to carry a changed ExpCmdSN and/or + * MaxCmdSN if another PDU will not be available for a long time (as + * determined by the target). * - * 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" - -/** - * @brief Login/Text Operational Session Text Key: X Node architecture. + * When a target receives the NOP-Out with a valid Initiator Task Tag + * (not the reserved value 0xFFFFFFFF), it MUST respond with a NOP-In + * with the same Initiator Task Tag that was provided in the NOP-Out + * request. It MUST also duplicate up to the first + * MaxRecvDataSegmentLength bytes of the initiator-provided Ping Data. + * For such a response, the Target Transfer Tag MUST be 0xFFFFFFFF. * - * @verbatim - * Use: LO, Declarative - * Senders: Initiator and target - * Scope: SW - * X#NodeArchitecture= - * Default is None. - * Examples: - * X#NodeArchitecture=ExampleOS/v1234,ExampleInc_SW_Initiator/1.05a - * X#NodeArchitecture=ExampleInc_HW_Initiator/4010,Firmware/2.0.0.5 - * X#NodeArchitecture=ExampleInc_SW_Initiator/2.1,CPU_Arch/i686 - * @endverbatim - * This document does not define the structure or content of the list of - * values.\n - * The initiator or target declares the details of its iSCSI node - * architecture to the remote endpoint. These details may include, but - * are not limited to, iSCSI vendor software, firmware, or hardware - * versions; the OS version; or hardware architecture. This key may be - * declared on a Discovery session or a Normal session.\n - * The length of the key value (total length of the list-of-values) MUST - * NOT be greater than 255 bytes.\n - * X#NodeArchitecture MUST NOT be redeclared during the Login Phase.\n - * Functional behavior of the iSCSI node (this includes the iSCSI - * protocol logic - the SCSI, iSCSI, and TCP/IP protocols) MUST NOT - * depend on the presence, absence, or content of the X#NodeArchitecture - * key. The key MUST NOT be used by iSCSI nodes for interoperability or - * for exclusion of other nodes. To ensure proper use, key values - * SHOULD be set by the node itself, and there SHOULD NOT be provisions - * for the key values to contain user-defined text.\n - * Nodes implementing this key MUST choose one of the following - * implementation options:\n - * - only transmit the key, - * - only log the key values received from other nodes, or - * - both transmit and log the key values. + * The target SHOULD NOT send a NOP-In in response to any other received + * NOP-Out in order to avoid lengthy sequences of NOP-In and NOP-Out + * PDUs sent in response to each other. * - * Each node choosing to implement transmission of the key values MUST - * be prepared to handle the response of iSCSI nodes that do not - * understand the key.\n - * Nodes that implement transmission and/or logging of the key values - * may also implement administrative mechanisms that disable and/or - * change the logging and key transmission details.\n - * Thus, a valid behavior for this key may be that a node is completely - * silent (the node does not transmit any key value and simply discards - * any key values it receives without issuing a NotUnderstood response). + * Otherwise, when a target sends a NOP-In that is not a response to a + * NOP-Out received from the initiator, the Initiator Task Tag MUST be + * set to 0xFFFFFFFF, and the data segment MUST NOT contain any data + * (DataSegmentLength MUST be 0). */ -#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_X_NODE_ARCH "X#NodeArchitecture" +typedef struct __attribute__((packed)) iscsi_nop_in_packet { + /// Always 0x20 according to iSCSI specification. + uint8_t opcode; -/** - * @brief Login/Text Operational Session Text Key: IFMarker (obseleted). - * - * This document obsoletes the following keys defined in RFC3720:\n - * IFMarker, OFMarker, OFMarkInt, and IFMarkInt. However, iSCSI - * mplementations compliant to this document may still receive these - * obsoleted keys - i.e., in a responder role - in a text negotiation.\n - * When an IFMarker or OFMarker key is received, a compliant iSCSI - * implementation SHOULD respond with the constant "Reject" value. The - * implementation MAY alternatively respond with a "No" value.\n - * However, the implementation MUST NOT respond with a "NotUnderstood" - * value for either of these keys.\n - * When an IFMarkInt or OFMarkInt key is received, a compliant iSCSI - * implementation MUST respond with the constant "Reject" value. The - * implementation MUST NOT respond with a "NotUnderstood" value for - * either of these keys. - */ -#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IF_MARKER "IFMarker" + /// Reserved for future usage (always MUST be 0x80 for now). + int8_t flags; -/** - * @brief Login/Text Operational Session Text Key: OFMarker (obseleted). - * - * This document obsoletes the following keys defined in RFC3720:\n - * IFMarker, OFMarker, OFMarkInt, and IFMarkInt. However, iSCSI - * mplementations compliant to this document may still receive these - * obsoleted keys - i.e., in a responder role - in a text negotiation.\n - * When an IFMarker or OFMarker key is received, a compliant iSCSI - * implementation SHOULD respond with the constant "Reject" value. The - * implementation MAY alternatively respond with a "No" value.\n - * However, the implementation MUST NOT respond with a "NotUnderstood" - * value for either of these keys.\n - * When an IFMarkInt or OFMarkInt key is received, a compliant iSCSI - * implementation MUST respond with the constant "Reject" value. The - * implementation MUST NOT respond with a "NotUnderstood" value for - * either of these keys. - */ -#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_OF_MARKER "OFMarker" + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; -/** - * @brief Login/Text Operational Session Text Key: OFMarkInt (obseleted). - * - * This document obsoletes the following keys defined in RFC3720:\n - * IFMarker, OFMarker, OFMarkInt, and IFMarkInt. However, iSCSI - * mplementations compliant to this document may still receive these - * obsoleted keys - i.e., in a responder role - in a text negotiation.\n - * When an IFMarker or OFMarker key is received, a compliant iSCSI - * implementation SHOULD respond with the constant "Reject" value. The - * implementation MAY alternatively respond with a "No" value.\n - * However, the implementation MUST NOT respond with a "NotUnderstood" - * value for either of these keys.\n - * When an IFMarkInt or OFMarkInt key is received, a compliant iSCSI - * implementation MUST respond with the constant "Reject" value. The - * implementation MUST NOT respond with a "NotUnderstood" value for - * either of these keys. - */ -#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_OF_MARK_INT "OFMarkInt" + /// TotalAHSLength + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// A LUN MUST be set to a correct value when the Target Transfer Tag is valid (not the reserved value 0xFFFFFFFF). + uint64_t lun; + + /// Initiator Task Tag (ITT) or 0xFFFFFFFF. + uint32_t init_task_tag; -/** - * @brief Login/Text Operational Session Text Key: IFMarkInt (obseleted). - * - * This document obsoletes the following keys defined in RFC3720:\n - * IFMarker, OFMarker, OFMarkInt, and IFMarkInt. However, iSCSI - * mplementations compliant to this document may still receive these - * obsoleted keys - i.e., in a responder role - in a text negotiation.\n - * When an IFMarker or OFMarker key is received, a compliant iSCSI - * implementation SHOULD respond with the constant "Reject" value. The - * implementation MAY alternatively respond with a "No" value.\n - * However, the implementation MUST NOT respond with a "NotUnderstood" - * value for either of these keys.\n - * When an IFMarkInt or OFMarkInt key is received, a compliant iSCSI - * implementation MUST respond with the constant "Reject" value. The - * implementation MUST NOT respond with a "NotUnderstood" value for - * either of these keys. - */ -#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IF_MARK_INT "IFMarkInt" + /** + * @brief Target Transfer Tag (TTT). + * + * If the target is responding to a NOP-Out, this field is set to the + * reserved value 0xFFFFFFFF.\n + * If the target is sending a NOP-In as a ping (intending to receive a + * corresponding NOP-Out), this field is set to a valid value (not the + * reserved value 0xFFFFFFFF).\n + * If the target is initiating a NOP-In without wanting to receive a + * corresponding NOP-Out, this field MUST hold the reserved value + * 0xFFFFFFFF. + */ + uint32_t target_xfer_tag; + /** + * @brief StatSN. + * + * The StatSN field will always contain the next StatSN. However, when + * the Initiator Task Tag is set to 0xFFFFFFFF, the StatSN for the + * connection is not advanced after this PDU is sent. + */ + uint32_t stat_sn; -/// Login request Next Stage (NSG) flags: SecurityNegotiation. -#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION 0x0 + /// ExpCmdSN. + uint32_t exp_cmd_sn; -/// Login request Next Stage (NSG) flags: LoginOperationalNegotiation. -#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 + /// MaxCmdSN. + uint32_t max_cmd_sn; -/// Login request Next Stage (NSG) flags: Reserved for future usage, may NOT be used. -#define ISCSI_LOGIN_REQ_FLAGS_BEXT_STAGE_RESERVED 0x2 + /// Reserved for future usage, always MUST be 0. + uint32_t reserved2; -/// Login request Next Stage (NSG) flags: FullFeaturePhase. -#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_FULL_FEATURE_PHASE 0x3 + /// Reserved for future usage, always MUST be 0. + uint64_t reserved3; -/** - * @brief Login request flags: Next Stage (NSG): First bit of the two bits. - * - * The Login negotiation requests and responses are associated - * with a specific stage in the session (SecurityNegotiation,\n - * LoginOperationalNegotiation, FullFeaturePhase) and may indicate the - * 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 + /// Optional header digest. + iscsi_header_digest hdr_digest; -/** - * @brief Login request flags: Next Stage (NSG): Second bit of the two bits. - * - * The Login negotiation requests and responses are associated - * with a specific stage in the session (SecurityNegotiation,\n - * LoginOperationalNegotiation, FullFeaturePhase) and may indicate the - * 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) + /// DataSegment - Return Ping Data. + iscsi_scsi_ds_cmd_data ds_ping_data; -/// 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))) + /// Optional data digest. + iscsi_data_digest data_digest; +} iscsi_nop_in_packet; -/// 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) +/// iSCSI SCSI transport ID protocol identifier: iSCSI. +#define ISCSI_TRANSPORT_ID_PROTOCOL_ID_ISCSI 0x05 -/// Login request Current Stage (CSG) flags: SecurityNegotiation. -#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_SECURITY_NEGOTIATION 0x0 +/// iSCSI SCSI transport ID protocol identifier: First bit of the four bits. +#define ISCSI_TRANSPORT_ID_PROTOCOL_ID_FIRST_BIT 0 -/// Login request Current Stage (CSG) flags: LoginOperationalNegotiation. -#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 +/// 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) -/// Login request Current Stage (CSG) flags: Reserved for future usage, may NOT be used. -#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_RESERVED 0x2 +/// 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)) -/// Login request Current Stage (CSG) flags: FullFeaturePhase. -#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_FULL_FEATURE_PHASE 0x3 +/// 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)) -/** - * @brief Login request flags: Current Stage (CSG): First 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_FIRST_BIT 2 +/// 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)) -/** - * @brief Login request flags: Current Stage (CSG): Second 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) +/// iSCSI SCSI transport ID format. +#define ISCSI_TRANSPORT_ID_FORMAT 0x01 -/// 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))) +/// iSCSI SCSI transport ID format: First bit of the two bits. +#define ISCSI_TRANSPORT_ID_FORMAT_FIRST_BIT 6 -/// 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) +/// 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)) -/** - * @brief Login request flags: Continue. - * - * (C) When set to 1, this bit indicates that the text (set of key=value - * pairs) in this Login Request is not complete (it will be continued on - * subsequent Login Requests); otherwise, it indicates that this Login - * Request ends a set of key=value pairs. A Login Request with the - * C bit set to 1 MUST have the T bit set to 0. - */ -#define ISCSI_LOGIN_REQ_FLAGS_CONTINUE (1 << 6) +/// 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)) -/** - * @brief Login request flags: Transmit. - * - * (T) When set to 1, this bit indicates that the initiator is ready to - * transit to the next stage.\n - * If the T bit is set to 1 and the NSG is set to FullFeaturePhase, then - * this also indicates that the initiator is ready for the Login - * Final-Response. - */ -#define ISCSI_LOGIN_REQ_FLAGS_TRANSIT (1 << 7) +/// 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 Login Request packet data. - * - * After establishing a TCP connection between an initiator and a - * target, the initiator MUST start a Login Phase to gain further access - * to the target's resources. - * - * The Login Phase consists of a sequence of Login Requests and Login - * Responses that carry the same Initiator Task Tag. + * @brief iSCSI SCSI Transport ID structure. * - * Login Requests are always considered as immediate. + * This structure handles the iSCSI SCSI transport + * identifier data. */ -typedef struct __attribute__((packed)) iscsi_login_req_packet { - /// Always 0x03 according to iSCSI specification. - uint8_t opcode; - - /// Login request flags. - int8_t flags; +typedef struct __attribute__((packed)) iscsi_transport_id { + /// First 4 bits are protocol ID and last 2 bits are format. + uint8_t id; - /** - * @brief Version-max indicates the maximum version number supported. - * - * All Login Requests within the Login Phase MUST carry the same - * Version-max. Currently, this is always 0.\n - * The target MUST use the value presented with the first Login Request. - */ - uint8_t version_max; + /// Reserved for future usage (always MUST be 0). + uint8_t reserved; - /** - * @brief Version-min indicates the minimum version number supported. - * - * All Login Requests within the Login Phase MUST carry the same - * Version-min. The target MUST use the value presented with the first - * Login Request. Always 0 for now. - */ - uint8_t version_min; + /// Additional length of name. + uint16_t add_len; - /// TotalAHSLength. - uint8_t total_ahs_len; + /// Name. + uint8_t name[0]; +} iscsi_transport_id; - /// DataSegmentLength. - uint8_t ds_len[3]; - /// Initiator Session ID (ISID). - iscsi_isid isid; +/// Maximum length of a key according to iSCSI specifications. +#define ISCSI_TEXT_KEY_MAX_LEN 63U - /** - * @brief Target Session Identifying Handle (TSIH). - * - * The TSIH must be set in the first Login Request. The reserved value - * 0 MUST be used on the first connection for a new session. Otherwise, - * the TSIH sent by the target at the conclusion of the successful login - * of the first connection for this session MUST be used. The TSIH - * identifies to the target the associated existing session for this new - * connection.\n - * All Login Requests within a Login Phase MUST carry the same TSIH. - * The target MUST check the value presented with the first Login - * Request. - */ - uint16_t tsih; +/// Maximum length of value for a simple key type. +#define ISCSI_TEXT_VALUE_MAX_SIMPLE_LEN 255U - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; +/// Maximum length of value for a normal key. +#define ISCSI_TEXT_VALUE_MAX_LEN 8192U - /** - * @brief Connection ID (CID). - * - * The CID provides a unique ID for this connection within the session.\n - * All Login Requests within the Login Phase MUST carry the same CID. - * The target MUST use the value presented with the first Login Request.\n - * A Login Request with a non-zero TSIH and a CID equal to that of an - * existing connection implies a logout of the connection followed by a - * login. - */ - uint16_t cid; +/// Value data shift value for key value alignment enforcement. +#define ISCSI_TEXT_VALUE_ALIGN_SHIFT 4UL - /// Reserved for future usage, always MUST be 0. - uint16_t reserved; +/// 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)) - /** - * @brief CmdSN. - * - * The CmdSN is either the initial command sequence number of a session - * (for the first Login Request of a session - the "leading" login) or - * the command sequence number in the command stream if the login is for - * a new connection in an existing session.\n - * Examples: - * - Login on a leading connection: If the leading login carries the - * CmdSN 123, all other Login Requests in the same Login Phase carry - * the CmdSN 123, and the first non-immediate command in the Full - * Feature Phase also carries the CmdSN 123. - * - Login on other than a leading connection: If the current CmdSN at - * the time the first login on the connection is issued is 500, then - * that PDU carries CmdSN=500. Subsequent Login Requests that are - * needed to complete this Login Phase may carry a CmdSN higher than - * 500 if non-immediate requests that were issued on other connections - * in the same session advance the CmdSN. - * - * If the Login Request is a leading Login Request, the target MUST use - * the value presented in the CmdSN as the target value for the - * ExpCmdSN. - */ - uint32_t cmd_sn; - /** - * @brief ExpStatSN. - * - * For the first Login Request on a connection, this is the ExpStatSN - * for the old connection, and this field is only valid if the Login - * Request restarts a connection.\n - * For subsequent Login Requests, it is used to acknowledge the Login - * Responses with their increasing StatSN values. - */ - uint32_t exp_stat_sn; +/// iSCSI text key=value pair type: Invalid. +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_INVALID -1 - /// Reserved for future usage, always MUST be 0. - uint64_t reserved2[2]; +/// iSCSI text key=value pair type: Unspecified type. +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_UNSPECIFIED 0 - /** - * @brief Data segment - Login Parameters in Text Request Format. - * - * The initiator MUST provide some basic parameters in order - * 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_login_req_packet; +/// iSCSI text key=value pair type: List. +#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 2 -/// Login response Next Stage (NSG) flags: SecurityNegotiation. -#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION 0x0 +/// iSCSI text key=value pair type: Numerical maximum. +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MAX 3 -/// Login response Next Stage (NSG) flags: LoginOperationalNegotiation. -#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 +/// iSCSI text key=value pair type: Numerical declarative. +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_DECLARATIVE 4 -/// Login response Next Stage (NSG) flags: Reserved for future usage, may NOT be used. -#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_RESERVED 0x2 +/// iSCSI text key=value pair type: Declarative. +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE 5 -/// Login response Next Stage (NSG) flags: FullFeaturePhase. -#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FULL_FEATURE_PHASE 0x3 +/// iSCSI text key=value pair type: Boolean OR. +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_OR 6 -/** - * @brief Login response flags: Next Stage (NSG): First bit of the two bits. - * - * The Login negotiation requests and responses are associated - * with a specific stage in the session (SecurityNegotiation, - * LoginOperationalNegotiation, FullFeaturePhase) and may indicate the - * 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 +/// iSCSI text key=value pair type: Boolean AND. +#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_AND 7 -/** - * @brief Login response flags: Next Stage (NSG): First bit of the two bits. - * - * The Login negotiation requests and responses are associated - * with a specific stage in the session (SecurityNegotiation, - * LoginOperationalNegotiation, FullFeaturePhase) and may indicate the - * 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) -/// 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))) +/// iSCSI key value pair flags: Discovery ignored. +#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE (1 << 0) -/// 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) +/// iSCSI key value pair flags: Multi negotiation. +#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 << 2) +/// iSCSI key value pair flags: CHAP type. +#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_CHAP_TYPE (1 << 3) -/// Login response Current Stage (CSG) flags: SecurityNegotiation. -#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_SECURITY_NEGOTIATION 0x0 +/// iSCSI key value pair flags: Requires special handling. +#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_SPECIAL_HANDLING (1 << 4) -/// Login response Current Stage (CSG) flags: LoginOperationalNegotiation. -#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 +/// iSCSI key value pair flags: Use previous default value. +#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_USE_PREVIOUS_VALUE (1 << 5) -/// Login response Current Stage (CSG) flags: Reserved for future usage, may NOT be used. -#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_RESERVED 0x2 +/// iSCSI key value pair flags: Override with default value. +#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_OVERRIDE_DEFAULT (1 << 6) -/// Login response Current Stage (CSG) flags: FullFeaturePhase. -#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FULL_FEATURE_PHASE 0x3 +/// iSCSI key value pair flags: Uses maximum value depending on secondary key. +#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_USE_OTHER_MAX_VALUE (1 << 7) -/** - * @brief Login response flags: Current Stage (CSG): First 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_RESPONSE_FLAGS_CURRENT_STAGE_FIRST_BIT 2 /** - * @brief Login response flags: Current Stage (CSG): First bit of the two bits. + * @brief iSCSI connection and session lookup table entry, used for allowed key values and determining key type. * - * 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. + * This structure is shared by the iSCSI session + * and the iSCSI connection lookup table. */ -#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_SECOND_BIT ((ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FIRST_BIT) + 1) +typedef struct iscsi_key_value_pair_lut_entry { + /// Name of key. + const uint8_t *key; -/// 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))) + /// Default value of the key, always in string representation. + uint8_t *value; -/// 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) + /// NUL separated list of allowed string values. If key type is numeric: NUL separated minimum and maximum integer range. End is marked with another NUL. + uint8_t *list_range; + /// Type of key and value pair. + const int type; + + /// Flags indicating special key attributes. + const int flags; +} iscsi_key_value_pair_lut_entry; -/** - * @brief Login response flags: Continue. - * - * (C) When set to 1, this bit indicates that the text (set of key=value - * pairs) in this Login Response is not complete (it will be continued - * on subsequent Login Responses); otherwise, it indicates that this - * Login Response ends a set of key=value pairs. A Login Response with - * the C bit set to 1 MUST have the T bit set to 0. - */ -#define ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE (1 << 6) /** - * @brief Login response flags: Transmit. + * @brief iSCSI Text / Login extracted key=value pair. * - * (T) The T bit is set to 1 as an indicator of the end of the stage. If - * the T bit is set to 1 and the NSG is set to FullFeaturePhase, then - * this is also the Login Final-Response. A T bit of 0 indicates a - * "partial" response, which means "more negotiation needed".\n - * A Login Response with the T bit set to 1 MUST NOT contain key=value - * pairs that may require additional answers from the initiator within - * the same stage.\n - * If the Status-Class is 0, the T bit MUST NOT be set to 1 if the T bit - * in the request was set to 0. + * This structure is used for accessing key and value + * pairs which have been extracted from either the + * Text or Login packet data. */ -#define ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT (1 << 7) +typedef struct iscsi_key_value_pair { + /// Value of the key which is stored in the hash map. + uint8_t *value; + /// NUL separated list of allowed string values. If key type is numeric: NUL separated minimum and maximum integer range. End is marked with another NUL. + uint8_t *list_range; -/** - * @brief Login response status class: Success. - * - * Indicates that the iSCSI target successfully received, understood, - * and accepted the request. The numbering fields (StatSN, ExpCmdSN, - * MaxCmdSN) are only valid if Status-Class is 0. - */ -#define ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SUCCESS 0x00 + /// Type of key and value pair. + int type; -/** - * @brief Login response status details: Success. - * - * Login is proceeding OK. If the response T bit is set to 1 in both the - * request and the matching response, and the NSG is set to - * FullFeaturePhase in both the request and the matching response, the - * Login Phase is finished, and the initiator may proceed to issue SCSI - * commands. - */ -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SUCCESS 0x00 + /// Flags indicating special key attributes. + int flags; + /// State bit mask. + uint state_mask; +} iscsi_key_value_pair; -/** - * @brief Login response status class: Redirection. - * - * Indicates that the initiator must take further action - * to complete the request. This is usually due to the - * target moving to a different address. All of the redirection - * Status-Class responses MUST return one or more text key - * parameters of the type "TargetAddress", which indicates the - * target's new address. A redirection response MAY be issued by - * a target prior to or after completing a security negotiation if - * a security negotiation is required. A redirection SHOULD be - * accepted by an initiator, even without having the target - * complete a security negotiation if any security negotiation is - * required, and MUST be accepted by the initiator after the - * completion of the security negotiation if any security - * negotiation is required. - */ -#define ISCSI_LOGIN_RESPONSE_STATUS_CLASS_REDIRECT 0x01 +typedef struct iscsi_connection iscsi_connection; /** - * @brief Login response status details: Temporarily redirected. + * @brief iSCSI Text / Login key=value packet data construction helper. * - * The requested iSCSI Target Name (ITN) has temporarily moved - * to the address provided. + * This structure is used to store the key=value plus NUL terminator + * pairs for sending as DataSegment packet data to the client. */ -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_REDIRECT_TEMP 0x01 +typedef struct iscsi_key_value_pair_packet { + /// Associated iSCSI connection. + iscsi_connection *conn; -/** - * @brief Login response status details: Permanently redirected. - * - * The requested ITN has permanently moved to the address provided. - */ -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_REDIRECT_PERM 0x02 + /// Current text buffer containing multiple key=value + NUL terminator pairs. + uint8_t *buf; + /// Position of output buffer for next write. + uint32_t pos; -/** - * @brief Login response status class: Initiator Error (not a format error). - * - * Indicates that the initiator most likely caused the error.\n - * This MAY be due to a request for a resource for which the - * initiator does not have permission. The request should - * not be tried again. - */ -#define ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR 0x02 + /// Current length of buffer including final NUL terminator without iSCSI zero padding. + uint32_t len; -/// Login response status details: Miscellaneous iSCSI initiator errors. -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC 0x00 + /// Discovery mode. + int discovery; +} iscsi_key_value_pair_packet; -/// Login response status details: The initiator could not be successfully authenticated or target authentication is not supported. -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_ERR 0x01 +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 -/// Login response status details: The initiator is not allowed access to the given target. -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_FAIL 0x02 -/// Login response status details: The requested iSCSI Target Name (ITN) does not exist at this address. -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_NOT_FOUND 0x03 +/// iSCSI main global data: INI configuration filename. +#define ISCSI_GLOBALS_CONFIG_FILENAME "iscsi.conf" -/// Login response status details: The requested ITN has been removed, and no forwarding address is provided. -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_TARGET_REMOVED 0x04 -/// Login response status details: The requested iSCSI version range is not supported by the target. -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_WRONG_VERSION 0x05 +/// iSCSI main global data: iSCSI INI configuration iSCSI section identifier string. +#define ISCSI_GLOBALS_SECTION_ISCSI "iscsi" -/// Login response status details: Too many connections on this Session ID (SSID). -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_TOO_MANY_CONNECTIONS 0x06 +/// iSCSI main global data: iSCSI INI configuration iSCSI SCSI section identifier string. +#define ISCSI_GLOBALS_SECTION_SCSI "scsi" -/// Login response status details: Missing parameters (e.g. iSCSI Initiator Name and/or Target Name). -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISSING_PARAMETER 0x07 -/// Login response status details: Target does not support session spanning to this connection (address). -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_NO_SESSION_SPANNING 0x08 +/// 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" -/// Login response status details: Target does not support this type of session or not from this initiator. -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_SESSION_NO_SUPPORT 0x09 +/// 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" -/// Login response status details: Attempt to add a connection to a non-existent session. -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_SESSION_NO_EXIST 0x0A +/// 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" -/// Login response status details: Invalid request type during login. -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_INVALID_LOGIN_REQ_TYPE 0x0B +/// iSCSI main global data: iSCSI INI configuration iSCSI SCSI section device type key identifier string. +#define ISCSI_GLOBALS_SECTION_SCSI_KEY_DEVICE_TYPE "DeviceType" -/** - * @brief Login response status class: Target Error. - * - * Indicates that the target sees no errors in the - * initiator's Login Request but is currently incapable of - * fulfilling the request. The initiator may retry the same Login - * Request later. - */ -#define ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SERVER_ERR 0x03 +/// 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" -/// Login response status details: Target hardware or software error. -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_TARGET_ERROR 0x00 +/// 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" -/// Login response status details: The iSCSI service or target is not currently operational. -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_SERVICE_UNAVAILABLE 0x01 +/// iSCSI main global data: iSCSI INI configuration iSCSI SCSI section removable device key identifier string. +#define ISCSI_GLOBALS_SECTION_SCSI_KEY_REMOVABLE "Removable" -/// The target has insufficient session, connection, or other resources. -#define ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_OUT_OF_RESOURCES 0x02 +/// 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" -/** - * @brief iSCSI Login Response packet data. - * - * The Login Response indicates the progress and/or end of the Login - * Phase. - */ -typedef struct __attribute__((packed)) iscsi_login_response_packet { - /// Always 0x23 according to iSCSI specification. - uint8_t opcode; +/// 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" - /// Login response flags. - int8_t flags; +/// 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" - /** - * @brief This is the highest version number supported by the target. - * - * All Login Responses within the Login Phase MUST carry the same - * Version-max. - */ - uint8_t version_max; +/// 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 - /** - * @brief Version-active indicates the highest version supported by the target and initiator. - * - * If the target does not support a version within the - * range specified by the initiator, the target rejects the login and - * this field indicates the lowest version supported by the target. - * All Login Responses within the Login Phase MUST carry the same - * Version-active.\n - * The initiator MUST use the value presented as a response to the first - * Login Request. - */ - uint8_t version_active; +/// iSCSI main global data config type: Initial ready to transfer. +#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_INIT_R2T 15 - /// TotalAHSLength. - uint8_t total_ahs_len; +/// iSCSI main global data config type: Immediate data. +#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_IMMEDIATE_DATA 16 - /// DataSegmentLength. - uint8_t ds_len[3]; +/// iSCSI main global data config type: Data PDU in order. +#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_DATA_PDU_IN_ORDER 17 - /// Initiator Session ID (ISID). - iscsi_isid isid; +/// iSCSI main global data config type: Data sequence in order. +#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_DATA_SEQ_IN_ORDER 18 - /** - * @brief Target Session Identifying Handle (TSIH). - * - * The TSIH is the target-assigned session-identifying handle. Its - * internal format and content are not defined by this protocol, except - * for the value 0, which is reserved. With the exception of the Login - * Final-Response in a new session, this field should be set to the TSIH - * provided by the initiator in the Login Request. For a new session, - * the target MUST generate a non-zero TSIH and ONLY return it in the - * Login Final-Response. - */ - uint16_t tsih; +/// iSCSI main global data config type: SCSI emulation for I/O removable device. +#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_REMOVABLE 19 - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; +/// iSCSI main global data config type: SCSI emulation for I/O UNMAP supporting device. +#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_UNMAP 20 - /// Reserved for future usage, always MUST be 0. - uint32_t reserved; +/// 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 - /** - * @brief StatSN. - * - * For the first Login Response (the response to the first Login - * Request), this is the starting status sequence number for the - * connection. The next response of any kind - including the next - * Login Response, if any, in the same Login Phase - will carry this - * number + 1. This field is only valid if the Status-Class is 0. - */ - uint32_t stat_sn; +/// 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 - /// ExpCmdSN. - uint32_t exp_cmd_sn; +/// 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 - /// MaxCmdSN. - uint32_t max_cmd_sn; +/// 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 - /** - * @brief Status-class. - * - * Status-class (see above for details). If the Status-Class is - * not 0, the initiator and target MUST close the TCP connection - * If the target wishes to reject the Login Request for more than one - * reason, it should return the primary reason for the rejection. - */ - uint8_t status_class; - /// Status-detail. - uint8_t status_detail; +/// iSCSI main global data SCSI device configuration flags: Initial ready to transfer. +#define ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_INIT_R2T (1 << 0) - /// Reserved for future usage, always MUST be 0. - uint16_t reserved2; +/// iSCSI main global data SCSI device configuration flags: Immediate data. +#define ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_IMMEDIATE_DATA (1 << 1) - /// Reserved for future usage, always MUST be 0. - uint64_t reserved3; +/// 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) - /** - * @brief Data segment - Login Parameters in Text Request Format. - * - * The target MUST provide some basic parameters in order to enable the - * initiator to determine if it is connected to the correct port and the - * initial text parameters for the security exchange.\n - * All the rules specified for Text Responses also hold for Login - * Responses. - */ - iscsi_ds_cmd_data ds_cmd_data; -} iscsi_login_response_packet; +/// 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) -/// Logout request reason code: Close the session. All commands associated with the session (if any) are terminated. -#define ISCSI_LOGOUT_REQ_REASON_CODE_CLOSE_SESSION 0x00 +/// 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) -/// Logout request reason code: Close the connection. All commands associated with the connection (if any) are terminated. -#define ISCSI_LOGOUT_REQ_REASON_CODE_CLOSE_CONNECTION 0x01 +/// 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) -/// Logout request reason code: Remove the connection for recovery. The connection is closed, and all commands associated with it, if any, are to be prepared for a new allegiance. -#define ISCSI_LOGOUT_REQ_REASON_CODE_REMOVE_CONNECTION_RECOVERY 0x02 +/// 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) -/** - * @brief Logout request implicit reason code: Session reinstatement. - * - * The entire logout discussion in this section is also applicable for - * an implicit Logout realized by way of a connection reinstatement or - * session reinstatement. When a Login Request performs an implicit - * Logout, the implicit Logout is performed as if having the reason - * codes specified below: - */ -#define ISCSI_LOGOUT_REQ_REASON_CODE_IMPLICIT_SESSION_REINSTATEMENT 0x00 +/// 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 Logout request implicit reason code: Connection reinstatement when the operational ErrorRecoveryLevel < 2. - * - * The entire logout discussion in this section is also applicable for - * an implicit Logout realized by way of a connection reinstatement or - * session reinstatement. When a Login Request performs an implicit - * Logout, the implicit Logout is performed as if having the reason - * codes specified below: - */ -#define ISCSI_LOGOUT_REQ_REASON_CODE_IMPLICIT_CONNECTION_REINSTATEMENT 0x01 /** - * @brief Logout request implicit reason code: Connection reinstatement when the operational ErrorRecoveryLevel = 2. + * @brief iSCSI main global data SCSI device configuration. * - * The entire logout discussion in this section is also applicable for - * an implicit Logout realized by way of a connection reinstatement or - * session reinstatement. When a Login Request performs an implicit - * Logout, the implicit Logout is performed as if having the reason - * codes specified below: + * This structure is used for specific SCSI device + * configuration which are matched using wildcard + * patterns which are stored in the hash map key. */ -#define ISCSI_LOGOUT_REQ_REASON_CODE_IMPLICIT_CONNECTION_REINSTATEMENT_2 0x02 +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; -/** - * @brief iSCSI Logout Request packet data. - * - * The Logout Request is used to perform a controlled closing of a - * connection. - * - * An initiator MAY use a Logout Request to remove a connection from a - * session or to close an entire session. - * - * After sending the Logout Request PDU, an initiator MUST NOT send any - * new iSCSI requests on the closing connection. If the Logout Request - * is intended to close the session, new iSCSI requests MUST NOT be sent - * on any of the connections participating in the session. - * - * When receiving a Logout Request with the reason code "close the - * connection" or "close the session", the target MUST terminate all - * pending commands, whether acknowledged via the ExpCmdSN or not, on - * that connection or session, respectively. - * - * When receiving a Logout Request with the reason code "remove the - * connection for recovery", the target MUST discard all requests not - * yet acknowledged via the ExpCmdSN that were issued on the specified - * connection and suspend all data/status/R2T transfers on behalf of - * pending commands on the specified connection. - * - * The target then issues the Logout Response and half-closes the TCP - * connection (sends FIN). After receiving the Logout Response and - * attempting to receive the FIN (if still possible), the initiator MUST - * completely close the logging-out connection. For the terminated - * commands, no additional responses should be expected. - * - * A Logout for a CID may be performed on a different transport - * connection when the TCP connection for the CID has already been - * terminated. In such a case, only a logical "closing" of the iSCSI - * connection for the CID is implied with a Logout. - * - * All commands that were not terminated or not completed (with status) - * and acknowledged when the connection is closed completely can be - * reassigned to a new connection if the target supports connection - * recovery. - * - * If an initiator intends to start recovery for a failing connection, - * it MUST use the Logout Request to "clean up" the target end of a - * failing connection and enable recovery to start, or use the Login - * Request with a non-zero TSIH and the same CID on a new connection for - * the same effect. In sessions with a single connection, the - * connection can be closed and then a new connection reopened. A - * connection reinstatement login can be used for recovery. - * - * A successful completion of a Logout Request with the reason code - * "close the connection" or "remove the connection for recovery" - * results at the target in the discarding of unacknowledged commands - * received on the connection being logged out. These are commands that - * have arrived on the connection being logged out but that have not - * been delivered to SCSI because one or more commands with a smaller - * CmdSN have not been received by iSCSI. The resulting holes in the - * command sequence numbers will have to be handled by appropriate - * recovery, unless the session is also closed. - */ -typedef struct __attribute__((packed)) iscsi_logout_req_packet { - /// Always 6 according to iSCSI specification. - uint8_t opcode; + /// Data digest (CRC32), always MUST be 0 or 4 for now. + int data_digest; - /** - * @brief Reason code. - * - * A target implicitly terminates the active tasks due to the iSCSI - * protocol in the following cases: - * -# When a connection is implicitly or explicitly logged out with - * the reason code "close the connection" and there are active - * tasks allegiant to that connection. - * -# When a connection fails and eventually the connection state - * times out and there are active tasks allegiant to that - * connection - * -# When a successful recovery Logout is performed while there are - * active tasks allegiant to that connection and those tasks - * eventually time out after the Time2Wait and Time2Retain periods - * without allegiance reassignment - * -# When a connection is implicitly or explicitly logged out with - * the reason code "close the session" and there are active tasks - * in that session - * - * If the tasks terminated in any of the above cases are SCSI tasks, - * they must be internally terminated as if with CHECK CONDITION status. - * This status is only meaningful for appropriately handling the - * internal SCSI state and SCSI side effects with respect to ordering, - * because this status is never communicated back as a terminating - * status to the initiator. However, additional actions may have to be - * taken at the SCSI level, depending on the SCSI context as defined by - * the SCSI standards (e.g., queued commands and ACA; UA for the next - * command on the I_T nexus in cases a), b), and c) above). After the - * tasks are terminated, the target MUST report a Unit Attention condition - * on the next command processed on any connection for each affected - * I_T_L nexus with the status of CHECK CONDITION, the ASC/ASCQ value - * of 0x47 / 0x7F ("SOME COMMANDS CLEARED BY ISCSI PROTOCOL EVENT"), etc. - */ - int8_t reason_code; + /// SCSI emulation: Device type. + uint scsi_device_type; - /// Reserved for future usage, always MUST be 0. - uint16_t reserved; + /// Maximum receive DataSegment length in bytes. + uint32_t max_recv_ds_len; - /// TotalAHSLength (MUST be 0 for this PDU). - uint8_t total_ahs_len; + /// Maximum number of connections per session. + uint32_t max_session_conns; - /// DataSegmentLength (MUST be 0 for this PDU). - uint8_t ds_len[3]; + /// Ready to transfer maximum outstanding value. + uint32_t max_outstanding_r2t; - /// Reserved for future usage, always MUST be 0. - uint64_t reserved2; + /// Default time to wait. + uint32_t default_time_to_wait; - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; + /// Default time to retain. + uint32_t default_time_to_retain; - /** - * @brief Connection ID (CID). - * - * This is the connection ID of the connection to be closed (including - * closing the TCP stream). This field is only valid if the reason code - * is not "close the session". - */ - uint16_t cid; + /// First burst length. + uint32_t first_burst_len; - /// Reserved for future usage, always MUST be 0. - uint16_t reserved3; + /// Maximum burst length. + uint32_t max_burst_len; - /// CmdSN. - uint32_t cmd_sn; + /// Error recovery level. + uint32_t err_recovery_level; - /// This is the last ExpStatSN value for the connection to be closed. - uint32_t exp_stat_sn; + /// SCSI emulation: Physical block size. + uint32_t scsi_physical_block_size; - /// Reserved for future usage, always MUST be 0. - uint64_t reserved4[2]; + /// SCSI emulation: Physical block size shift count. + uint32_t scsi_physical_block_size_shift; - /// Optional header digest. - iscsi_header_digest hdr_digest; -} iscsi_logout_req_packet; + /// 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; -/// Logout response - response code: Connection or session closed successfully. -#define ISCSI_LOGOUT_RESPONSE_CLOSED_SUCCESSFULLY 0x00 -/// Logout response - response code: Connection ID (CID) not found. -#define ISCSI_LOGOUT_RESPONSE_CID_NOT_FOUND 0x01 +/** + * @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 << 0) + +/// iSCSI main global data flags: CHAP authentication is disabled. +#define ISCSI_GLOBALS_FLAGS_CHAP_DISABLE (1 << 1) + +/// iSCSI main global data flags: CHAP authentication is required. +#define ISCSI_GLOBALS_FLAGS_CHAP_REQUIRE (1 << 2) -/// Logout response - response code: Connection recovery is not supported (i.e., the Logout reason code was "remove the connection for recovery" and the target does not support it as indicated by the operational ErrorRecoveryLevel). -#define ISCSI_LOGOUT_RESPONSE_CONNECTION_RECOVERY_NOT_SUPPORTED 0x02 +/// iSCSI main global data flags: CHAP authentication is mutual. +#define ISCSI_GLOBALS_FLAGS_CHAP_MUTUAL (1 << 3) -/// Logout response - response code: Cleanup failed for various reasons. -#define ISCSI_LOGOUT_RESPONSE_CLEANUP_FAILED 0x03 +/// iSCSI main global data flags: Initial ready to transfer. +#define ISCSI_GLOBALS_FLAGS_INIT_R2T (1 << 4) -/** - * @brief iSCSI Logout Response packet data. - * - * The Logout Response is used by the target to indicate if the cleanup - * operation for the connection(s) has completed. - * - * After Logout, the TCP connection referred by the CID MUST be closed - * at both ends (or all connections must be closed if the logout reason - * was session close). - */ -typedef struct __attribute__((packed)) iscsi_logout_response_packet { - /// Always 0x26 according to iSCSI specification. - uint8_t opcode; +/// iSCSI main global data flags: Immediate data. +#define ISCSI_GLOBALS_FLAGS_IMMEDIATE_DATA (1 << 5) - /// Reserved for future usage (must be always 0x80 for now). - int8_t flags; +/// iSCSI main global data flags: Data PDU in order. +#define ISCSI_GLOBALS_FLAGS_DATA_PDU_IN_ORDER (1 << 6) - /// Response. - uint8_t response; +/// iSCSI main global data flags: Data sequence in order. +#define ISCSI_GLOBALS_FLAGS_DATA_SEQ_IN_ORDER (1 << 7) - /// Reserved for future usage, always MUST be 0. - uint8_t reserved; +/// iSCSI main global data flags: SCSI emulation for I/O removable device. +#define ISCSI_GLOBALS_FLAGS_SCSI_IO_REMOVABLE (1 << 8) - /// TotalAHSLength (MUST be 0 for this PDU). - uint8_t total_ahs_len; +/// iSCSI main global data flags: SCSI emulation for I/O UNMAP supporting device. +#define ISCSI_GLOBALS_FLAGS_SCSI_IO_UNMAP (1 << 9) - /// DataSegmentLength (MUST be 0 for this PDU). - uint8_t ds_len[3]; +/// iSCSI main global data flags: SCSI emulation for I/O non-rotating device. +#define ISCSI_GLOBALS_FLAGS_SCSI_IO_NO_ROTATION (1 << 10) - /// Reserved for future usage, always MUST be 0. - uint64_t reserved2; +/// 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) - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; +/// iSCSI main global data flags: SCSI emulation for I/O write protected device. +#define ISCSI_GLOBALS_FLAGS_SCSI_IO_WRITE_PROTECT (1 << 12) - /// Reserved for future usage, always MUST be 0. - uint32_t reserved3; +/// iSCSI main global data flags: SCSI emulation for I/O write cache device. +#define ISCSI_GLOBALS_FLAGS_SCSI_IO_WRITE_CACHE (1 << 13) - /// StatSN. - uint32_t stat_sn; - /// ExpCmdSN. - uint32_t exp_cmd_sn; +/// iSCSI main global data target name validation check level: None, allow everything. +#define ISCSI_GLOBALS_TARGET_NAME_CHECK_NONE 0 - /// MaxCmdSN. - uint32_t max_cmd_sn; +/// 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 - /// Reserved for future usage, always MUST be 0. - uint32_t reserved4; +/// 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 - /** - * @brief Time2Wait. - * - * If the Logout response code is 0 and the operational - * ErrorRecoveryLevel is 2, this is the minimum amount of time, in - * seconds, to wait before attempting task reassignment. If the Logout - * response code is 0 and the operational ErrorRecoveryLevel is less - * than 2, this field is to be ignored.\n - * This field is invalid if the Logout response code is 1.\n - * If the Logout response code is 2 or 3, this field specifies the - * minimum time to wait before attempting a new implicit or explicit - * logout.\n - * If Time2Wait is 0, the reassignment or a new Logout may be attempted - * immediately. - */ - uint16_t time_wait; - /** - * @brief Time2Retain. - * - * If the Logout response code is 0 and the operational - * ErrorRecoveryLevel is 2, this is the maximum amount of time, in - * seconds, after the initial wait (Time2Wait) that the target waits for - * the allegiance reassignment for any active task, after which the task - * state is discarded. If the Logout response code is 0 and the - * operational ErrorRecoveryLevel is less than 2, this field is to be - * ignored.\n - * This field is invalid if the Logout response code is 1.\n - * If the Logout response code is 2 or 3, this field specifies the - * maximum amount of time, in seconds, after the initial wait - * (Time2Wait) that the target waits for a new implicit or explicit - * logout.\n - * If it is the last connection of a session, the whole session state is - * discarded after Time2Retain.\n - * If Time2Retain is 0, the target has already discarded the connection - * (and possibly the session) state along with the task states. No - * reassignment or Logout is required in this case. - */ - uint16_t time_retain; +/// iSCSI main global data: Default maximum number of connections. +#define ISCSI_GLOBALS_DEFAULT_MAX_CONNECTIONS 1UL - /// Reserved for future usage, always MUST be 0. - uint32_t reserved5; +/// iSCSI main global data: Default maximum number of outstanding ready to transfers. +#define ISCSI_GLOBALS_DEFAULT_MAX_OUTSTANDING_R2T 1UL - /// Optional header digest. - iscsi_header_digest hdr_digest; -} iscsi_logout_response_packet; +/// 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 -/// Selective Negative / Sequence Number Acknowledgment (SNACK) request: Data/R2T SNACK: requesting retransmission of one or more Data-In or R2T PDUs. -#define ISCSI_SNACK_REQ_TYPE_DATA_R2T_SNACK 0x00 +/// iSCSI main global data: First burst length in bytes. +#define ISCSI_GLOBALS_DEFAULT_FIRST_BURST_LEN ISCSI_DEFAULT_RECV_DS_LEN -/// Selective Negative / Sequence Number Acknowledgment (SNACK) request: -#define ISCSI_SNACK_REQ_TYPE_STATUS_SNACK 0x01 // Status SNACK: requesting retransmission of one or more - // numbered responses +/// 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 -/** - * @brief Selective Negative / Sequence Number Acknowledgment (SNACK) request: DataACK: positively acknowledges Data-In PDUs. - * - * If an initiator operates at ErrorRecoveryLevel 1 or higher, it MUST - * issue a SNACK of type DataACK after receiving a Data-In PDU with the - * A bit set to 1. However, if the initiator has detected holes in the - * input sequence, it MUST postpone issuing the SNACK of type DataACK - * until the holes are filled. An initiator MAY ignore the A bit if it - * deems that the bit is being set aggressively by the target (i.e., - * before the MaxBurstLength limit is reached).\n - * The DataACK is used to free resources at the target and not to - * request or imply data retransmission.\n - * An initiator MUST NOT request retransmission for any data it had - * already acknowledged - */ -#define ISCSI_SNACK_REQ_TYPE_DATA_ACK 0x02 /** - * @brief Selective Negative / Sequence Number Acknowledgment (SNACK) request: R-Data SNACK: requesting retransmission of Data-In PDUs with possible resegmentation and status tagging. + * @brief This is the main global iSCSI structure which manages all global data. * - * If the initiator MaxRecvDataSegmentLength changed between the - * original transmission and the time the initiator requests - * retransmission, the initiator MUST issue a R-Data SNACK.\n - * With R-Data SNACK, the initiator indicates that it discards all the - * unacknowledged data and expects the target to resend it. It also - * expects resegmentation. In this case, the retransmitted Data-In PDUs - * MAY be different from the ones originally sent in order to reflect - * changes in MaxRecvDataSegmentLength. Their DataSN starts with the - * BegRun of the last DataACK received by the target if any was received; - * otherwise, it starts with 0 and is increased by 1 for each resent - * Data-In PDU.\n - * A target that has received a R-Data SNACK MUST return a SCSI Response - * that contains a copy of the SNACK Tag field from the R-Data SNACK in - * the SCSI Response SNACK Tag field as its last or only Response. For - * example, if it has already sent a response containing another value - * in the SNACK Tag field or had the status included in the last Data-In - * PDU, it must send a new SCSI Response PDU. If a target sends more - * than one SCSI Response PDU due to this rule, all SCSI Response PDUs - * must carry the same StatSN. If an initiator attempts to recover a lost - * SCSI Response when more than one response has been sent, the - * target will send the SCSI Response with the latest content known to - * the target, including the last SNACK Tag for the command.\n - * For considerations in allegiance reassignment of a task to a - * connection with a different MaxRecvDataSegmentLength. + * All iSCSI portal groups, target nodes, sessions and + * connections are stored here for global access. */ -#define ISCSI_SNACK_REQ_TYPE_R_DATA_SNACK 0x03 +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; -/** - * @brief iSCSI SNACK Request packet data. - * - * If the implementation supports ErrorRecoveryLevel greater than zero, - * it MUST support all SNACK types. - * - * The SNACK is used by the initiator to request the retransmission of - * numbered responses, data, or R2T PDUs from the target. The SNACK - * Request indicates the numbered responses or data "runs" whose - * retransmission is requested, where the run starts with the first - * StatSN, DataSN, or R2TSN whose retransmission is requested and - * indicates the number of Status, Data, or R2T PDUs requested, - * including the first. 0 has special meaning when used as a starting - * number and length: - * - * - When used in RunLength, it means all PDUs starting with the - * initial. - * - * - When used in both BegRun and RunLength, it means all - * unacknowledged PDUs. - * - * The numbered response(s) or R2T(s) requested by a SNACK MUST be - * delivered as exact replicas of the ones that the target transmitted - * originally, except for the fields ExpCmdSN, MaxCmdSN, and ExpDataSN, - * which MUST carry the current values. R2T(s)requested by SNACK MUST - * also carry the current value of the StatSN. - * - * The numbered Data-In PDUs requested by a Data SNACK MUST be delivered - * as exact replicas of the ones that the target transmitted originally, - * except for the fields ExpCmdSN and MaxCmdSN, which MUST carry the - * current values; and except for resegmentation. - * - * Any SNACK that requests a numbered response, data, or R2T that was - * not sent by the target or was already acknowledged by the initiator - * MUST be rejected with a reason code of "Protocol Error". - */ -typedef struct __attribute__((packed)) iscsi_snack_req_packet { - /// Always 0x10 according to iSCSI specification. - uint8_t opcode; + /// Hash map containing all registered iSCSI portal groups. + iscsi_hashmap *portal_groups; - /** - * @brief Type. - * - * Data/R2T SNACK, Status SNACK, or R-Data SNACK for a command MUST - * precede status acknowledgment for the given command. - */ - int8_t type; + /// 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; - /// Reserved for future usage, always MUST be 0. - uint16_t reserved; + /// iSCSI target nodes. + iscsi_hashmap *target_nodes; - /// TotalAHSLength. - uint8_t total_ahs_len; + /// 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; - /// DataSegmentLength. - uint8_t ds_len[3]; + /// Hash map containing all iSCSI sessions. + iscsi_hashmap *sessions; - /// LUN or Reserved. - uint64_t lun; + /// 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; - /** - * @brief Initiator Task Tag (ITT). - * - * For a Status SNACK and DataACK, the Initiator Task Tag MUST be set to - * the reserved value 0xFFFFFFFF. In all other cases, the Initiator - * Task Tag field MUST be set to the Initiator Task Tag of the - * referenced command. - */ - uint32_t init_task_tag; + /// Hash map containing session key and value pair types and allowed values or ranges. + iscsi_hashmap *session_key_value_pairs; - /** - * @brief Target Transfer Tag (TTT). - * - * For a R-Data SNACK, this field MUST contain a value that is different - * from 0 or 0xFFFFFFFF and is unique for the task (identified by the - * Initiator Task Tag). This value MUST be copied by the iSCSI target - * in the last or only SCSI Response PDU it issues for the command.\n - * For DataACK, the Target Transfer Tag MUST contain a copy of the - * Target Transfer Tag and LUN provided with the SCSI Data-In PDU with - * the A bit set to 1.\n - * In all other cases, the Target Transfer Tag field MUST be set to the - * reserved value 0xFFFFFFFF. - */ - uint32_t target_xfer_snack_tag; + /// Hash map containing connection key and value pair types and allowed values or ranges. + iscsi_hashmap *connection_key_value_pairs; - /// Reserved for future usage, always MUST be 0. - uint32_t reserved2; + /// Hash map containing iSCSI SCSI device specific configuration. + iscsi_hashmap *scsi_device_config; - /// ExpStatSN. - uint32_t exp_stat_sn; + /// Mutex for hash map containing iSCSI SCSI device specific configuration. + pthread_mutex_t scsi_device_config_mutex; - /// Reserved for future usage, always MUST be 0. - uint32_t reserved3; + /// Global flags. + int flags; - /** - * @brief BegRun. - * - * This field indicates the DataSN, R2TSN, or StatSN of the first PDU - * whose retransmission is requested (Data/R2T and Status SNACK), or the - * next expected DataSN (DataACK SNACK).\n - * A BegRun of 0, when used in conjunction with a RunLength of 0, means - * "resend all unacknowledged Data-In, R2T or Response PDUs". - * BegRun MUST be 0 for a R-Data SNACK. - */ - uint32_t beg_run; + /// Target name validation check level. + int target_name_check; - /** - * @brief RunLength. - * - * This field indicates the number of PDUs whose retransmission is - * requested.\n - * A RunLength of 0 signals that all Data-In, R2T, or Response PDUs - * carrying the numbers equal to or greater than BegRun have to be - * resent.\n - * The RunLength MUST also be 0 for a DataACK SNACK in addition to a - * R-Data SNACK. - */ - uint32_t run_len; + /// Maximum number of allowed sessions. + uint max_sessions; - /// Optional header digest. - iscsi_header_digest hdr_digest; -} iscsi_snack_req_packet; + /// 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; -/// iSCSI Reject packet data: Reserved, original PDU can't be resent. -#define ISCSI_REJECT_REASON_RESERVED 0x01 + /// SCSI emulation: Device type. + uint scsi_device_type; -/** - * @brief iSCSI Reject packet data: Data (payload) digest error, original PDU can be resent. - * - * For iSCSI, Data-Out PDU retransmission is only done if the - * target requests retransmission with a recovery R2T. However, - * if this is the data digest error on immediate data, the - * initiator may choose to retransmit the whole PDU, including - * the immediate data. - */ -#define ISCSI_REJECT_REASON_DATA_DIGEST_ERR 0x02 + /// Maximum receive DataSegment length in bytes. + uint32_t max_recv_ds_len; -/// iSCSI Reject reason packet data: SNACK Reject (original PDU can be resent). -#define ISCSI_REJECT_REASON_SNACK_REJECT 0x03 + /// Maximum number of connections per session. + uint32_t max_session_conns; -/// iSCSI Reject reason packet data: Protocol Error (e.g., SNACK Request for a status that was already acknowledged). Original PDU can't be resent. -#define ISCSI_REJECT_REASON_PROTOCOL_ERR 0x04 + /// Ready to transfer maximum outstanding value. + uint32_t max_outstanding_r2t; -/// iSCSI Reject reason packet data: Command not supported (original PDU can't be resent). -#define ISCSI_REJECT_REASON_COMMAND_NOT_SUPPORTED 0x05 + /// Default time to wait. + uint32_t default_time_to_wait; -/// iSCSI Reject reason packet data: Immediate command reject - too many immediate commands (original PDU can be resent). -#define ISCSI_REJECT_REASON_TOO_MANY_IMMEDIATE_COMMANDS 0x06 + /// Default time to retain. + uint32_t default_time_to_retain; -/// iSCSI Reject reason packet data: Task in progress (original PDU can't be resent). -#define ISCSI_REJECT_REASON_TASK_IN_PROGRESS 0x07 + /// First burst length. + uint32_t first_burst_len; -/// iSCSI Reject reason packet data: Invalid data ack (original PDU can't be resent). -#define ISCSI_REJECT_REASON_INVALID_DATA_ACK 0x08 + /// Maximum burst length. + uint32_t max_burst_len; -/** - * @brief iSCSI Reject reason packet data: Invalid PDU field, original PDU can't be resent. - * - * A target should use this reason code for all invalid values - * of PDU fields that are meant to describe a task, a response, - * or a data transfer. Some examples are invalid TTT/ITT, - * buffer offset, LUN qualifying a TTT, and an invalid sequence - * number in a SNACK. - */ -#define ISCSI_REJECT_REASON_INVALID_PDU_FIELD 0x09 + /// Error recovery level. + uint32_t err_recovery_level; -/// iSCSI Reject reason packet data: Long op reject - Can't generate Target Transfer Tag - out of resources. Original PDU can be resent later. -#define ISCSI_REJECT_REASON_OUT_OF_RESOURCES 0x0A + /// CHAP group id. + int32_t chap_group; -/** - * @brief iSCSI Reject reason packet data: Deprecated; MUST NOT be used. - * - * Reason code 0x0B is deprecated and MUST NOT be used by - * implementations. An implementation receiving reason code - * 0x0B MUST treat it as a negotiation failure that terminates - * the Login Phase and the TCP connection. - */ -#define ISCSI_REJECT_REASON_DEPRECATED 0x0B + /// SCSI emulation: Physical block size. + uint32_t scsi_physical_block_size; -/// iSCSI Reject reason packet data: Waiting for Logout, original PDU can't be resent. -#define ISCSI_REJECT_REASON_WAITING_FOR_LOGOUT 0x0C + /// SCSI emulation: Physical block size shift count. + uint32_t scsi_physical_block_size_shift; -/** - * @brief iSCSI Reject packet data. - * - * This structure will be received or sent, if an iSCSI - * packet was rejected or has been rejected for some reason. - */ -typedef struct __attribute__((packed)) iscsi_reject_packet { - /// Always 0x3F according to iSCSI specification. - uint8_t opcode; + /// SCSI emulation: Logical block size. + uint32_t scsi_logical_block_size; - /// Reserved for future usage (must be always 0x80 for now). - int8_t flags; + /// SCSI emulation: Logical block size shift count. + uint32_t scsi_logical_block_size_shift; +} iscsi_globals; - /** - * @brief Reject reason. - * - * In all the cases in which a pre-instantiated SCSI task is terminated - * because of the reject, the target MUST issue a proper SCSI command - * response with CHECK CONDITION. In these cases in which a status for - * the SCSI task was already sent before the reject, no additional - * status is required. If the error is detected while data from the - * initiator is still expected (i.e., the command PDU did not contain - * all the data and the target has not received a Data-Out PDU with the - * Final bit set to 1 for the unsolicited data, if any, and all - * outstanding R2Ts, if any), the target MUST wait until it receives - * the last expected Data-Out PDUs with the F bit set to 1 before - * sending the Response PDU. - */ - uint8_t reason; - /// Reserved for future usage, always MUST be 0. - uint8_t reserved; +/// Reference to iSCSI global vector. MUST be initialized with iscsi_create before any iSCSI functions are used. +extern iscsi_globals *iscsi_globvec; - /// TotalAHSLength. - uint8_t total_ahs_len; +/// 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; - /// DataSegmentLength. - uint8_t ds_len[3]; - /// Reserved for future usage, always MUST be 0. - uint64_t reserved2; +int iscsi_create(); // Allocates and initializes the iSCSI global vector structure +void iscsi_destroy(); // Deallocates all resources acquired by iscsi_create - /// Always 0xFFFFFFFF for now. - uint32_t tag; +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 - /// Reserved for future usage, always MUST be 0. - uint32_t reserved3; - /** - * @brief StatSN. - * - * This field carries its usual value and is not related to the - * rejected command. The StatSN is advanced after a Reject. - */ - uint32_t stat_sn; +/** + * @brief iSCSI portal group: Private portal group if set, public otherwise. + * + * When redirecting logins, there are two portal group types: public and + * private.\n + * Public portal groups return their portals during discovery session. + * A redirection private portal may also be specified for non-discovery + * logins.\n + * Private portal groups instead do not return their portals during + * the discovery session. + */ +#define ISCSI_PORTAL_GROUP_PRIVATE (1 << 0) - /** - * @brief ExpCmdSN. - * - * This field carries its usual value and is not related to the - * rejected command. - */ - uint32_t exp_cmd_sn; +/// iSCSI portal group: CHAP authentication is disabled. +#define ISCSI_PORTAL_GROUP_CHAP_DISABLE (1 << 1) - /** - * @brief MaxCmdSN. - * - * This field carries its usual value and is not related to the - * rejected command. - */ - uint32_t max_cmd_sn; +/// iSCSI portal group: CHAP authentication is required. +#define ISCSI_PORTAL_GROUP_CHAP_REQUIRE (1 << 2) - /** - * @brief DataSN / Ready To Transfer Sequence Number (R2TSN) or Reserved. - * - * This field is only valid if the rejected PDU is a Data/R2T SNACK and - * the Reject reason code is "Protocol Error". The DataSN/R2TSN is the - * next Data/R2T sequence number that the target would send for the - * task, if any. - */ - uint32_t data_r2tsn_sn; +/// iSCSI portal group: CHAP authentication is mutual. +#define ISCSI_PORTAL_GROUP_CHAP_MUTUAL (1 << 3) - /// Reserved for future usage, always MUST be 0. - uint32_t reserved4[2]; - /// Optional header digest. - iscsi_header_digest hdr_digest; +/** + * @brief iSCSI portal group. + * + * Portal groups are either public or private and also are used + * by CHAP authentication. + */ +typedef struct iscsi_portal_group { + /// Hash map containing all portals associated with this iSCSI group. + iscsi_hashmap *portals; - /** - * @brief Complete Header of Bad PDU. - * - * The target returns the header (not including the digest) of the - * PDU in error as the data of the response. - */ - iscsi_bhs_packet bad_pdu_hdr; + /// Tag value for this portal group. + uint64_t tag; - /// Vendor-specific data (if any). - uint8_t vendor_data[0]; + /// Reference count. + int ref_count; + + /// Portal group flags. + int flags; + + /// CHAP group id. + int32_t chap_group; +} iscsi_portal_group; - /// Optional data digest. - iscsi_data_digest data_digest; -} iscsi_reject_packet; /** - * @brief iSCSI NOP-Out packet data. - * - * NOP-Out may be used by an initiator as a "ping request" to verify - * that a connection/session is still active and all its components are - * operational. The NOP-In response is the "ping echo". - * - * A NOP-Out is also sent by an initiator in response to a NOP-In. - * - * A NOP-Out may also be used to confirm a changed ExpStatSN if another - * PDU will not be available for a long time. - * - * Upon receipt of a NOP-In with the Target Transfer Tag set to a valid - * value (not the reserved value 0xffffffff), the initiator MUST respond - * with a NOP-Out. In this case, the NOP-Out Target Transfer Tag MUST - * contain a copy of the NOP-In Target Transfer Tag. The initiator + * @brief iSCSI portal. * - * SHOULD NOT send a NOP-Out in response to any other received NOP-In, - * in order to avoid lengthy sequences of NOP-In and NOP-Out PDUs sent - * in response to each other. + * iSCSI portals manage the host / IP address and port, as well + * as the associated connections. */ -typedef struct __attribute__((packed)) iscsi_nop_out_packet { - /// Always 0x00 according to iSCSI specification. - uint8_t opcode; +typedef struct iscsi_portal { + /// Group this portal belongs to. + iscsi_portal_group *group; - /// Reserved for future usage (must be always 0x80 for now). - int8_t flags; + /// Hostname / IP address of the portal. + uint8_t *host; - /// Reserved for future usage, always MUST be 0. - uint16_t reserved; + /// Port of the portal. + uint8_t *port; - /// TotalAHSLength. - uint8_t total_ahs_len; + /// TCP/IP socket for the portal. + int sock; +} iscsi_portal; - /// DataSegmentLength. - uint8_t ds_len[3]; - /// LUN or Reserved. - uint64_t lun; +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 - /** - * @brief Initiator Task Tag (ITT). - * - * The NOP-Out MUST have the Initiator Task Tag set to a valid value - * only if a response in the form of a NOP-In is requested (i.e., the - * NOP-Out is used as a ping request). Otherwise, the Initiator Task - * Tag MUST be set to 0xFFFFFFFF.\n - * When a target receives the NOP-Out with a valid Initiator Task Tag, - * it MUST respond with a NOP-In Response.\n - * If the Initiator Task Tag contains 0xFFFFFFFF, the I bit MUST be set - * to 1, and the CmdSN is not advanced after this PDU is sent. - */ - uint32_t init_task_tag; +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); - /** - * @brief Target Transfer Tag (TTT). - * - * The Target Transfer Tag is a target-assigned identifier for the - * operation.\n - * The NOP-Out MUST only have the Target Transfer Tag set if it is - * issued in response to a NOP-In with a valid Target Transfer Tag. In - * this case, it copies the Target Transfer Tag from the NOP-In PDU.\n - * Otherwise, the Target Transfer Tag MUST be set to 0xFFFFFFFF.\n - * When the Target Transfer Tag is set to a value other than 0xFFFFFFFF, - * the LUN field MUST also be copied from the NOP-In. - */ - uint32_t target_xfer_tag; - /// CmdSN. - uint32_t cmd_sn; +/// 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 - /// ExpStatSN. - uint32_t exp_stat_sn; +/// iSCSI SCSI status code: Reservation conflict. +#define ISCSI_SCSI_STATUS_RESERVATION_CONFLICT 0x18 - /// Reserved for future usage, always MUST be 0. - uint64_t reserved2[2]; +/// iSCSI SCSI status code: Obselete. +#define ISCSI_SCSI_STATUS_OBSELETE 0x22 - /// Optional header digest. - iscsi_header_digest hdr_digest; +/// iSCSI SCSI status code: Task set full. +#define ISCSI_SCSI_STATUS_TASK_SET_FULL 0x28 - /** - * @brief DataSegment - Ping Data (optional). - * - * Ping data is reflected in the NOP-In Response. The length of the - * reflected data is limited to MaxRecvDataSegmentLength. The length of - * 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 status code: ACA active. +#define ISCSI_SCSI_STATUS_ACA_ACTIVE 0x30 - /// Optional data digest. - iscsi_data_digest data_digest; -} iscsi_nop_out_packet; +/// iSCSI SCSI status code: Task aborted. +#define ISCSI_SCSI_STATUS_TASK_ABORTED 0x40 -/** - * @brief iSCSI NOP-In packet data. - * - * NOP-In is sent by a target as either a response to a NOP-Out, a - * "ping" to an initiator, or a means to carry a changed ExpCmdSN and/or - * MaxCmdSN if another PDU will not be available for a long time (as - * determined by the target). - * - * When a target receives the NOP-Out with a valid Initiator Task Tag - * (not the reserved value 0xFFFFFFFF), it MUST respond with a NOP-In - * with the same Initiator Task Tag that was provided in the NOP-Out - * request. It MUST also duplicate up to the first - * MaxRecvDataSegmentLength bytes of the initiator-provided Ping Data. - * For such a response, the Target Transfer Tag MUST be 0xFFFFFFFF. - * - * The target SHOULD NOT send a NOP-In in response to any other received - * NOP-Out in order to avoid lengthy sequences of NOP-In and NOP-Out - * PDUs sent in response to each other. - * - * Otherwise, when a target sends a NOP-In that is not a response to a - * NOP-Out received from the initiator, the Initiator Task Tag MUST be - * set to 0xFFFFFFFF, and the data segment MUST NOT contain any data - * (DataSegmentLength MUST be 0). - */ -typedef struct __attribute__((packed)) iscsi_nop_in_packet { - /// Always 0x20 according to iSCSI specification. - uint8_t opcode; +/// iSCSI SCSI sense key: No sense. +#define ISCSI_SCSI_SENSE_KEY_NO_SENSE 0x00 - /// Reserved for future usage (must be always 0x80 for now). - int8_t flags; +/// iSCSI SCSI sense key: Recovered error. +#define ISCSI_SCSI_SENSE_KEY_RECOVERED_ERR 0x01 - /// Reserved for future usage, always MUST be 0. - uint16_t reserved; +/// iSCSI SCSI sense key: Not ready. +#define ISCSI_SCSI_SENSE_KEY_NOT_READY 0x02 - /// TotalAHSLength - uint8_t total_ahs_len; +/// iSCSI SCSI sense key: Medium error. +#define ISCSI_SCSI_SENSE_KEY_MEDIUM_ERR 0x03 - /// DataSegmentLength. - uint8_t ds_len[3]; +/// iSCSI SCSI sense key: Hardware error. +#define ISCSI_SCSI_SENSE_KEY_HARDWARE_ERR 0x04 - /// A LUN MUST be set to a correct value when the Target Transfer Tag is valid (not the reserved value 0xFFFFFFFF). - uint64_t lun; +/// iSCSI SCSI sense key: Illegal request. +#define ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ 0x05 - /// Initiator Task Tag (ITT) or 0xFFFFFFFF. - uint32_t init_task_tag; +/// iSCSI SCSI sense key: Unit attention. +#define ISCSI_SCSI_SENSE_KEY_UNIT_ATTENTION 0x06 - /** - * @brief Target Transfer Tag (TTT). - * - * If the target is responding to a NOP-Out, this field is set to the - * reserved value 0xFFFFFFFF.\n - * If the target is sending a NOP-In as a ping (intending to receive a - * corresponding NOP-Out), this field is set to a valid value (not the - * reserved value 0xFFFFFFFF).\n - * If the target is initiating a NOP-In without wanting to receive a - * corresponding NOP-Out, this field MUST hold the reserved value - * 0xFFFFFFFF. - */ - uint32_t target_xfer_tag; +/// iSCSI SCSI sense key: Data protect. +#define ISCSI_SCSI_SENSE_KEY_DATA_PROTECT 0x07 - /** - * @brief StatSN. - * - * The StatSN field will always contain the next StatSN. However, when - * the Initiator Task Tag is set to 0xFFFFFFFF, the StatSN for the - * connection is not advanced after this PDU is sent. - */ - uint32_t stat_sn; +/// iSCSI SCSI sense key: Blank check. +#define ISCSI_SCSI_SENSE_KEY_BLANK_CHECK 0x08 - /// ExpCmdSN. - uint32_t exp_cmd_sn; // ExpCmdSN +/// iSCSI SCSI sense key: Vendor specific. +#define ISCSI_SCSI_SENSE_KEY_VENDOR_SPECIFIC 0x09 - /// MaxCmdSN. - uint32_t max_cmd_sn; +/// iSCSI SCSI sense key: Copy aborted. +#define ISCSI_SCSI_SENSE_KEY_COPY_ABORTED 0x0A - /// Reserved for future usage, always MUST be 0. - uint32_t reserved2[3]; +/// iSCSI SCSI sense key: Aborted command. +#define ISCSI_SCSI_SENSE_KEY_ABORTED_COMMAND 0x0B - /// Optional header digest. - iscsi_header_digest hdr_digest; +/// iSCSI SCSI sense key: Volume overflow. +#define ISCSI_SCSI_SENSE_KEY_VOLUME_OVERFLOW 0x0D - /// DataSegment - Return Ping Data. - iscsi_ds_cmd_data ds_ping_data; +/// iSCSI SCSI sense key: Miscompare. +#define ISCSI_SCSI_SENSE_KEY_MISCOMPARE 0x0E - /// Optional data digest. - iscsi_data_digest data_digest; -} iscsi_nop_in_packet; +/// iSCSI SCSI Additional Sense Code (ASC): No additional sense. +#define ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE 0x00 -/// iSCSI SCSI transport ID protocol identifier. -#define ISCSI_TRANSPORT_ID_PROTOCOL_ID_ISCSI 0x05 +/// iSCSI SCSI Additional Sense Code (ASC): Peripheral device write fault. +#define ISCSI_SCSI_ASC_PERIPHERAL_DEVICE_WRITE_FAULT 0x03 -/// iSCSI SCSI transport ID format. -#define ISCSI_TRANSPORT_ID_FORMAT 0x01 +/// 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 -typedef struct __attribute__((packed)) iscsi_transport_id { - /// First 4 bits are protocol ID and last 2 bits are format. - uint8_t id; +/// iSCSI SCSI Additional Sense Code (ASC): Write error. +#define ISCSI_SCSI_ASC_WRITE_ERR 0x0C - /// Reserved for future usage (always MUST be 0). - uint8_t reserved; +/// iSCSI SCSI Additional Sense Code (ASC): Block guard check failed. +#define ISCSI_SCSI_ASC_LOGICAL_BLOCK_GUARD_CHECK_FAIL 0x10 - /// Additional length of name. - uint16_t add_len; +/// iSCSI SCSI Additional Sense Code (ASC): Block application tag checdk failed. +#define ISCSI_SCSI_ASC_LOGICAL_BLOCK_APP_TAG_CHECK_FAIL 0x10 - /// Name. - uint8_t name[0]; -} iscsi_transport_id; +/// 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 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 SCSI Additional Sense Code (ASC): Miscompare during verify operation. +#define ISCSI_SCSI_ASC_MISCOMPARE_DURING_VERIFY_OPERATION 0x1D -/// 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 SCSI Additional Sense Code (ASC): Invalid command operation code. +#define ISCSI_SCSI_ASC_INVALID_COMMAND_OPERATION_CODE 0x20 -/// 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 SCSI Additional Sense Code (ASC): Access denied. +#define ISCSI_SCSI_ASC_ACCESS_DENIED 0x20 -/// 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 SCSI Additional Sense Code (ASC): Logical block address out of range. +#define ISCSI_SCSI_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x21 -/// 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 SCSI Additional Sense Code (ASC): Invalid field in CDB. +#define ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB 0x24 -/// 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 SCSI Additional Sense Code (ASC): Logical unit not supported. +#define ISCSI_SCSI_ASC_LU_NOT_SUPPORTED 0x25 -/// 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 SCSI Additional Sense Code (ASC): Write protected. +#define ISCSI_SCSI_ASC_WRITE_PROTECTED 0x27 -/// 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 SCSI Additional Sense Code (ASC): Data has changed. +#define ISCSI_SCSI_ASC_CAPACITY_DATA_HAS_CHANGED 0x2A -/// 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 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_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 SCSI Additional Sense Code (ASC): Internal target failure. +#define ISCSI_SCSI_ASC_INTERNAL_TARGET_FAIL 0x44 -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 SCSI Additional Sense Code Qualifier (ASCQ): Cause not reportable. +#define ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE 0x00 -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 +/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Becoming ready. +#define ISCSI_SCSI_ASCQ_BECOMING_READY 0x01 -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 +/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Format command failed. +#define ISCSI_SCSI_ASCQ_FORMAT_COMMAND_FAIL 0x01 -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 +/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Block guard check failed. +#define ISCSI_SCSI_ASCQ_LOGICAL_BLOCK_GUARD_CHECK_FAIL 0x01 -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 +/// 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 -/// Maximum length of a key according to iSCSI specifications. -#define ISCSI_TEXT_KEY_MAX_LEN 63UL +/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Manual intervention required. +#define ISCSI_SCSI_ASCQ_MANUAL_INTERVENTION_REQUIRED 0x03 -/// Maximum length of value for a simple key type. -#define ISCSI_TEXT_VALUE_MAX_SIMPLE_LEN 255UL +/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Block reference tag check failed. +#define ISCSI_SCSI_ASCQ_LOGICAL_BLOCK_REF_TAG_CHECK_FAIL 0x03 -/// Maximum length of value for a normal key. -#define ISCSI_TEXT_VALUE_MAX_LEN 8192UL +/// 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 text key=value pair type: Invalid. -#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_INVALID -1L +/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Capacity data has changed. +#define ISCSI_SCSI_ASCQ_CAPACITY_DATA_HAS_CHANGED 0x09 -/// iSCSI text key=value pair type: Unspecified type. -#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_UNSPECIFIED 0L -/// iSCSI text key=value pair type: List. -#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST 1L +typedef struct iscsi_port iscsi_port; -/// iSCSI text key=value pair type: Numerical minimum. -#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN 2L -/// iSCSI text key=value pair type: Numerical maximum. -#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MAX 3L +/** + * @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; -/// iSCSI text key=value pair type: Numerical declarative. -#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_DECLARATIVE 4L + /// Target iSCSI port name. + uint8_t *target_name; -/// iSCSI text key=value pair type: Declarative. -#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE 5L + /// Initiator iSCSI port. + iscsi_port *init_port; -/// iSCSI text key=value pair type: Boolean OR. -#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_OR 6L + /// Initiator iSCSI port name. + uint8_t *init_name; -/// iSCSI text key=value pair type: Boolean AND. -#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_AND 7L + /// Transport ID. + iscsi_transport_id *transport_id; + /// Reservation key. + uint64_t r_key; -/// iSCSI key value pair flags: Discovery ignored. -#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE (1 << 0L) + /// Relative target port identifier. + uint16_t rel_target_port_id; -/// iSCSI key value pair flags: Multi negotiation. -#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_MULTI_NEGOTIATION (1 << 1L) + /// Transport ID length. + uint16_t transport_id_len; +} iscsi_scsi_pr_registrant; -/// iSCSI key value pair flags: Target declarative. -#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_TARGET_DECLARATIVE (1 << 2L) -/// iSCSI key value pair flags: CHAP type. -#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_CHAP_TYPE (1 << 3L) +/// iSCSI SCSI Persistent Reservation (PR) reservation type: Write exclusive. +#define ISCSI_SCSI_PR_RESERVATION_TYPE_WRITE_EXCLUSIVE 0x01 -/// iSCSI key value pair flags: Requires special handling. -#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_SPECIAL_HANDLING (1 << 4L) +/// iSCSI SCSI Persistent Reservation (PR) reservation type: Exclusive access. +#define ISCSI_SCSI_PR_RESERVATION_TYPE_EXCLUSIVE_ACCESS 0x03 -/// iSCSI key value pair flags: Use previous default value. -#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_USE_PREVIOUS_VALUE (1 << 5L) +/// iSCSI SCSI Persistent Reservation (PR) reservation type: Write exclusive - registrants only. +#define ISCSI_SCSI_PR_RESERVATION_TYPE_WRITE_EXCLUSIVE_REGS_ONLY 0x05 -/// iSCSI key value pair flags: Override with default value. -#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_OVERRIDE_DEFAULT (1 << 6L) +/// iSCSI SCSI Persistent Reservation (PR) reservation type: Exclusive access - registrants only. +#define ISCSI_SCSI_PR_RESERVATION_TYPE_EXCLUSIVE_ACCESS_REGS_ONLY 0x06 -/// 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) +/// 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 connection and session lookup table entry, used for allowed key values and determining key type. + * @brief iSCSI SCSI Persistent Reservation (PR) reservation with LU_SCOPE. * - * This structure is shared by the iSCSI session - * and the iSCSI connection lookup table. + * LU_SCOPE means that Persistent Reservation (PR) scope + * applies to the full logical unit. */ -typedef struct iscsi_key_value_pair_lut_entry { - /// Name of key. - uint8_t *key; - - /// Default value of the key, always in string representation. - uint8_t *value; +typedef struct iscsi_scsi_pr_reservation { + /// Registrant for this reservation. + iscsi_scsi_pr_registrant *holder; - /// NUL separated list of allowed string values. If key type is numeric: NUL separated minimum and maximum integer range. End is marked with another NUL. - uint8_t *list_range; + /// Current reservation key. + uint64_t cr_key; - /// Type of key and value pair. - const int type; + /// Reservation type. + int type; - /// Flags indicating special key attributes. - const int flags; -} iscsi_key_value_pair_lut_entry; + /// Reservation flags. + int32_t flags; +} iscsi_scsi_pr_reservation; /** - * @brief iSCSI Text / Login extracted key=value pair. + * @brief iSCSI SCSI Persistent Reservation (PR) registrant search by target and initiator port. * - * This structure is used for accessing key and value - * pairs which have been extracted from either the - * Text or Login packet data. + * This structure is used by iterating through + * all iSCSI LUN Persistent Reservation (PR) + * registrant's finding by target and initiator + * port. */ -typedef struct iscsi_key_value_pair { - /// Value of the key which is stored in the hash map. - uint8_t *value; +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; - /// NUL separated list of allowed string values. If key type is numeric: NUL separated minimum and maximum integer range. End is marked with another NUL. - uint8_t *list_range; + /// The target port to be searched for is stored here. + iscsi_port *target_port; - /// Type of key and value pair. - int type; + /// The initiator port to be searched for is stored here. + iscsi_port *init_port; +} iscsi_scsi_pr_registrant_get_reg; - /// Flags indicating special key attributes. - int flags; - /// State bit mask. - uint state_mask; -} iscsi_key_value_pair; +/// 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 iSCSI Text / Login key=value packet data construction helper. + * @brief Callback when iSCSI SCSI transfer task completed. * - * This structure is used to store the key=value plus NUL terminator - * pairs for sending as DataSegment packet data to the client. + * 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 struct iscsi_key_value_pair_packet { - /// Associated iSCSI connection. - struct iscsi_connection *conn; +typedef void (*iscsi_scsi_task_xfer_complete_callback)(iscsi_scsi_task *scsi_task); - /// Current text buffer containing multiple key=value + NUL terminator pairs. - uint8_t *buf; +/** + * @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); - /// Position of output buffer for next write. - uint pos; +/** + * @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); - /// Current length of buffer including final NUL terminator without iSCSI zero padding. - uint len; +/** + * @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); - /// 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 +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; -/// iSCSI main global data flags: Allow duplicate ISIDs. -#define ISCSI_GLOBALS_FLAGS_ISID_ALLOW_DUPLICATES (1 << 0L) + /// I/O task wait callback user data. + uint8_t *user_data; +} iscsi_scsi_emu_io_wait; -/// iSCSI main global data flags: CHAP authentication is disabled. -#define ISCSI_GLOBALS_FLAGS_CHAP_DISABLE (1 << 1L) -/// iSCSI main global data flags: CHAP authentication is required. -#define ISCSI_GLOBALS_FLAGS_CHAP_REQUIRE (1 << 2L) +/// iSCSI SCSI task flags: Read. +#define ISCSI_SCSI_TASK_FLAGS_XFER_READ (1 << 0) -/// iSCSI main global data flags: CHAP authentication is mutual. -#define ISCSI_GLOBALS_FLAGS_CHAP_MUTUAL (1 << 3L) +/// iSCSI SCSI task flags: Write. +#define ISCSI_SCSI_TASK_FLAGS_XFER_WRITE (1 << 1) /** - * @brief This is the main global iSCSI structure which manages all global data. + * @brief iSCSI SCSI Task. * - * All iSCSI portal groups, target nodes, sessions and - * connections are stored here for global access. + * This structure is used for the iSCSI SCSI + * layer task management. */ -typedef struct iscsi_globals { - /// Hash map containing all iSCSI devices. - iscsi_hashmap *devices; +typedef struct iscsi_scsi_task { + /// Doubly linked list node, MUST be first element. + iscsi_node node; - /// Hash map containing all registered iSCSI portal groups. - iscsi_hashmap *portal_groups; + /// SCSI LUN associated with this task. + iscsi_scsi_lun *lun; - /// iSCSI target nodes. - iscsi_hashmap *target_nodes; + /// Target iSCSI port. + iscsi_port *target_port; - /// Hash map containing all iSCSI sessions. - iscsi_hashmap *sessions; + /// Initiator iSCSI port. + iscsi_port *init_port; - /// Hash map containing session key and value pair types and allowed values or ranges. - iscsi_hashmap *session_key_value_pairs; + /// SCSI Command Descriptor Block (CDB). + iscsi_scsi_cdb *cdb; - /// Hash map containing connections not associated with an iSCSI sessions. - iscsi_hashmap *connections; + /// SCSI sense data. + iscsi_scsi_sense_data_packet *sense_data; - /// Hash map containing connection key and value pair types and allowed values or ranges. - iscsi_hashmap *connection_key_value_pairs; + /// Transfer complete callback function. + iscsi_scsi_task_xfer_complete_callback xfer_complete_callback; - /// Global flags. + /// 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; - /// Maximum number of allowed sessions. - uint max_sessions; + /// Reference counter. + uint32_t ref; - /// CHAP group id. - int32_t chap_group; -} iscsi_globals; + /// Transfer position in bytes. + uint32_t xfer_pos; + /// Transfer length in bytes. + uint32_t xfer_len; -/// iSCSI global vector. MUST be initialized with iscsi_create before any iSCSI functions are used. -iscsi_globals *iscsi_globvec = NULL; + /// Sense data length. + uint8_t sense_data_len; + /// iSCSI SCSI status code. + uint8_t status; -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 + /// Task management function. + uint8_t task_mgmt_func; -/** - * @brief iSCSI portal group: Private portal group if set, public otherwise. - * - * When redirecting logins, there are two portal group types: public and - * private.\n - * Public portal groups return their portals during discovery session. - * A redirection private portal may also be specified for non-discovery - * logins.\n - * Private portal groups instead do not return their portals during - * the discovery session. - */ -#define ISCSI_PORTAL_GROUP_PRIVATE (1 << 0L) + /// 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 portal group: CHAP authentication is disabled. -#define ISCSI_PORTAL_GROUP_CHAP_DISABLE (1 << 1L) +/// 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 portal group: CHAP authentication is required. -#define ISCSI_PORTAL_GROUP_CHAP_REQUIRE (1 << 2L) +/// iSCSI SCSI emulation maximum UNMAP block descriptor count in block descriptors. +#define ISCSI_SCSI_EMU_MAX_UNMAP_BLOCK_DESC_COUNT 256UL -/// iSCSI portal group: CHAP authentication is mutual. -#define ISCSI_PORTAL_GROUP_CHAP_MUTUAL (1 << 3L) +/// iSCSI SCSI emulation I/O type: Removable. +#define ISCSI_SCSI_EMU_IO_TYPE_REMOVABLE (1 << 0) -/** - * @brief iSCSI portal group. - * - * Portal groups are either public or private and also are used - * by CHAP authentication. - */ -typedef struct iscsi_portal_group { - /// Hash map containing all portals associated with this iSCSI group. - iscsi_hashmap *portals; +/// iSCSI SCSI emulation I/O type: Unmap. +#define ISCSI_SCSI_EMU_IO_TYPE_UNMAP (1 << 1) - /// Reference count. - int ref_count; +/// iSCSI SCSI emulation I/O type: Non-rotating medium (e.g., solid state). +#define ISCSI_SCSI_EMU_IO_TYPE_NO_ROTATION (1 << 2) - /// Tag value for this portal group. - int tag; +/// iSCSI SCSI emulation I/O type: Physical read only device. +#define ISCSI_SCSI_EMU_IO_TYPE_PHYSICAL_READ_ONLY (1 << 3) - /// Portal group flags. - int flags; +/// iSCSI SCSI emulation I/O type: Device is (temporarily) write protected. +#define ISCSI_SCSI_EMU_IO_TYPE_WRITE_PROTECT (1 << 4) - /// CHAP group id. - int32_t chap_group; -} iscsi_portal_group; +/// iSCSI SCSI emulation I/O type: Write cache available. +#define ISCSI_SCSI_EMU_IO_TYPE_WRITE_CACHE (1 << 5) -/** - * @brief iSCSI portal. - * - * iSCSI portals manage the host / IP address and port, as well - * as the associated connections. - */ -typedef struct iscsi_portal { - /// Group this portal belongs to. - iscsi_portal_group *group; +/// iSCSI SCSI emulation block flags: Write operation. +#define ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE (1 << 0) - /// Hostname / IP address of the portal. - uint8_t *host; +/// iSCSI SCSI emulation block flags: Verify operation. +#define ISCSI_SCSI_EMU_BLOCK_FLAGS_VERIFY (1 << 1) - /// Port of the portal. - uint8_t *port; - /// TCP/IP socket for the portal. - int sock; -} iscsi_portal; +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 -iscsi_portal_group *iscsi_portal_group_create(const int tag, const int flags); // Creates and initializes an iSCSI portal group -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_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_portal *iscsi_portal_create(const uint8_t *host, const uint8_t *port); // Allocates and initializes an iSCSI portal structure -void iscsi_portal_destroy(iscsi_portal *portal); +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 -/// ISCSI port flags: In use. -#define ISCSI_PORT_FLAGS_IN_USE (1 << 0L) +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: Default maximum number of outstanding ready to transfers. -#define ISCSI_SESSION_DEFAULT_MAX_OUTSTANDING_R2T 1UL - -/// iSCSI session: Default time to wait in seconds. -#define ISCSI_SESSION_DEFAULT_TIME_TO_WAIT 2UL - -/// 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 flags: Initial ready to transfer. +#define ISCSI_SESSION_FLAGS_INIT_R2T (1 << 0) -/// iSCSI session: Default initial ready to transfer state. -#define ISCSI_SESSION_DEFAULT_INIT_R2T true +/// iSCSI session flags: Immediate data. +#define ISCSI_SESSION_FLAGS_IMMEDIATE_DATA (1 << 1) -/// iSCSI session: Default immediate data state. -#define ISCSI_SESSION_DEFAULT_IMMEDIATE_DATA true +/// iSCSI session flags: Data PDU in order. +#define ISCSI_SESSION_FLAGS_DATA_PDU_IN_ORDER (1 << 2) -/// 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) + + +/** + * @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; - /// Position of DataSegment buffer for next read. - uint pos; + /// 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 ) ) { -- cgit v1.2.3-55-g7522 From 677c663907ecbe2c073abee1515d0a59e561609a Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Mon, 6 Oct 2025 15:00:20 +0200 Subject: [SERVER] iscsi: Implement relaying requests to uplink servers --- README.md | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/server/iscsi.c | 111 +++++++++++++++++++++++++++++++++++++---------- src/server/iscsi.h | 12 ++++-- 3 files changed, 220 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/README.md b/README.md index 5fad3ae..a77a64d 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ apt-get install git \ linux-headers-generic \ libfuse-dev \ libjansson-dev \ + libcurl4-openssl-dev \ rpm ``` @@ -82,6 +83,7 @@ apt-get install git \ linux-headers-generic \ libfuse-dev \ libjansson-dev \ + libcurl4-openssl-dev \ afl \ rpm ``` @@ -455,3 +457,124 @@ if (clientA) if (clientB) unlock(clientB.lock); ``` + + +## iSCSI server +DNBD3 partially supports the _Internet Small Computer Systems Interface (iSCSI)_ protocol based on [RFC 7143](https://www.rfc-editor.org/rfc/rfc7143) and the [Storage Performance Development Kit (SPDK)](https://spdk.io) implementation. + +The iSCSI server can be enabled in the configuration file _server.conf_ by: + +```ini +[dnbd3] +; Specifies whether the iSCSI server should be initialized, enabled and used upon start of DNBD3 server. +iSCSIServer=true +``` + + +The valid configuration options of _iscsi.conf_ (see below) can be used in _server.conf_ as well, but _iscsi.conf_ will override any present settings. It is recommended to use _server.conf_ for default settings and _iscsi.conf_ for specific settings. + + +### Configuration file _iscsi.conf_ +The _iscsi.conf_ file is the main configuration file for the iSCSI implementation of _dnbd3-server_. The configuration in the file is specified the INI file format as shown in the following: + +```ini +; Using this file is recommended for actual iSCSI and SCSI emulation settings. This file will be +; parsed after server.conf and all values here will override the ones in server.conf. +; Also it is strictly recommended to set SCSI device specific details here. +; All SCSI device specific details start with a section scsi-device- following a pattern which +; can either be the full image name or a matching pattern like debian*.iso, e.g. +; [scsi-device-debian*.iso] will match all images starting with debian and end with the .iso +; file extension. +; scsi-device- sections will be processed in order, always inherit from the [iscsi] and [scsi] +; sections and are matched case sensitive + +[iscsi] +; Target name check level (warning) specifying invalid check levels will default to full +; validation!). Default is Full +; Full Target name is checked for full standaard compliance (recommended) +; Relaxed All invalid characters according to standard will be allowed if not an IQN, NAA or EUI. +; None No checking at all (be careful) +TargetNameCheck=Full + +... + +[scsi] +; Valid devices types for emulated device (warning) specifying invalid types will default to direct +; access device!). Default is Direct +; Direct Direct access device (e.g. hard drive) +; Sequential Sequential access device (e.g. tape) +; WriteOnce Write once device (WORM) +; ReadOnlyDirect Read only direct access device +; OpticalMemory Optical memory device (e.g. CD-ROM, DVD, BluRay) +; MediaChanger Media changer device +DeviceType=Direct + +; Physical block size of emulated device, rounded up to nearest power of two). Default is 512 for +; supporting ancient systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed +; physical block sizes range from 256 to 32768 bytes (32 KiB). Default is 512 bytes +PhysicalBlockSize=512 + +; Logical block size of emulated device, rounded up to nearest power of two). Default is 4096 for +; supporting modern systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed +; logical block sizes range from 256 to 32768 bytes (32 KiB). Default is 4096 bytes (4 KiB) +LogicalBlockSize=4096 + +; Device is emulated as removable. Should be enabled for CD-ROM, DVD and BluRay and disabled for +; hard drives. Default is true +Removable=true + +... + +; SCSI device specific configuration for *.iso (case sensitive), i.e. all image files which +; end with .iso extension. Block sizes are set to 2 KiB for CD-ROM, DVD and BluRay type devices +; and the physical read only emulation flag is enabled +[scsi-device-*.iso] +; Physical block size of emulated device, rounded up to nearest power of two). Default is 512 for +; supporting ancient systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed +; physical block sizes range from 256 to 32768 bytes (32 KiB). Default is 512 bytes +PhysicalBlockSize=2048 + +; Logical block size of emulated device, rounded up to nearest power of two). Default is 4096 for +; supporting modern systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed +; logical block sizes range from 256 to 32768 bytes (32 KiB). Default is 4096 bytes (4 KiB) +LogicalBlockSize=2048 + +; Device is emulated as removable. Should be enabled for CD-ROM, DVD and BluRay and disabled for +; hard drives. Default is true +Removable=true + +; Device is emulated as physically read only. No possibility to enable writes in any case. Use +; for CD-ROM, DVD and BluRay devices. Default is false +PhysicalReadOnly=true + +; SCSI device specific configuration for *.ISO (case sensitive), i.e. all image files which +; end with .ISO extension. Block sizes are set to 2 KiB for CD-ROM, DVD and BluRay type devices +; and the physical read only emulation flag is enabled +[scsi-device-*.ISO] +; Physical block size of emulated device, rounded up to nearest power of two). Default is 512 for +; supporting ancient systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed +; physical block sizes range from 256 to 32768 bytes (32 KiB). Default is 512 bytes +PhysicalBlockSize=2048 + +; Logical block size of emulated device, rounded up to nearest power of two). Default is 4096 for +; supporting modern systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed +; logical block sizes range from 256 to 32768 bytes (32 KiB). Default is 4096 bytes (4 KiB) +LogicalBlockSize=2048 + +; Device is emulated as removable. Should be enabled for CD-ROM, DVD and BluRay and disabled for +; hard drives. Default is true +Removable=true + +; Device is emulated as physically read only. No possibility to enable writes in any case. Use +; for CD-ROM, DVD and BluRay devices. Default is false +PhysicalReadOnly=true + +``` + + +For a complete list of possible configuration options, look at the comments in the sample _iscsi.conf_ file, which describes the options in detail. + + +### _World Wide Name (WWN)_ to DNBD3 image mapping for standard compliance. +As the iSCSI target node name supports only a very restricted number of allowed characters, DNBD3 images also can be specified either using the _Network Address Authority (NAA) IEEE Extended_ name identifier, the _64-bit extended unique identifier (EUI-64)_ or _iSCSI Qualified Name (IQN)_ using the _wwn-0x_ prefix. The revision of the DNBD3 image is specified with a _:_ (colon) after the DNBD3 image name and also the _Logical Unit Number (LUN)_. + diff --git a/src/server/iscsi.c b/src/server/iscsi.c index b44f695..e9b0fc1 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -46,7 +46,9 @@ #include "ini.h" #include "iscsi.h" #include "locks.h" +#include "uplink.h" #include "threadpool.h" +#include "reference.h" /** * @file iscsi.c @@ -3850,13 +3852,13 @@ void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task, iscsi_scsi_task_xfer_com scsi_task->target_port = NULL; scsi_task->init_port = NULL; scsi_task->cdb = NULL; + scsi_task->sense_data = 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; @@ -5239,6 +5241,32 @@ static int iscsi_scsi_emu_queue_io_wait(iscsi_scsi_task *scsi_task, iscsi_scsi_e return iscsi_scsi_emu_io_queue( &scsi_task->io_wait ); } +/** + * @brief Called when data requested via an uplink server has arrived. + * + * This function is used to retrieve + * block data which is NOT locally + * available. + * + * @param[in] data Pointer to related scsi_task. May NOT + * be NULL, so be careful. + * @param[in] handle Uplink handle. + * @param[in] start Start of range in bytes. + * @param[in] length Length of range in bytes, as passed to + * uplink_request(). + * @param[in] buffer Data for requested range. + */ +static void iscsi_uplink_callback(void *data, uint64_t handle UNUSED, uint64_t start UNUSED, uint32_t length, const char *buffer) +{ + iscsi_scsi_task *scsi_task = (iscsi_scsi_task *) data; + + memcpy( scsi_task->buf, buffer, length ); + + pthread_mutex_lock( &scsi_task->uplink_mutex ); + pthread_cond_signal( &scsi_task->uplink_cond ); + pthread_mutex_unlock( &scsi_task->uplink_mutex ); +} + /** * @brief Converts offset and length specified by a block size to offset and length in bytes. * @@ -5279,9 +5307,6 @@ static uint64_t iscsi_scsi_emu_blocks_to_bytes(uint64_t *offset_bytes, const uin * @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. @@ -5297,16 +5322,62 @@ static uint64_t iscsi_scsi_emu_blocks_to_bytes(uint64_t *offset_bytes, const uin * @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) +int iscsi_scsi_emu_io_block_read(iscsi_scsi_task *scsi_task, 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); + const uint64_t num_bytes = iscsi_scsi_emu_blocks_to_bytes( &offset_bytes, offset_blocks, num_blocks, block_size ); + dnbd3_cache_map_t *cache = ref_get_cachemap( image ); + bool readFromFile; + bool success; + + if ( cache == NULL ) { + readFromFile = true; + } else { + // This is a proxyed image, check if we need to relay the request... + const uint64_t start = (offset_bytes & ~(uint64_t)(DNBD3_BLOCK_SIZE - 1)); + const uint64_t end = ((offset_bytes + num_bytes + DNBD3_BLOCK_SIZE - 1) & ~(uint64_t) (DNBD3_BLOCK_SIZE - 1)); + + readFromFile = image_isRangeCachedUnsafe( cache, start, end ); + ref_put( &cache->reference ); + + if ( !readFromFile ) { + // Not cached, request via uplink + + if ( pthread_mutex_init( &scsi_task->uplink_mutex, NULL ) != 0 ) { + logadd( LOG_ERROR, "iscsi_scsi_emu_io_block_read: Error while initializing DNBD3 uplink mutex for iSCSI SCSI task" ); + + return -ENOMEM; + } + + if ( pthread_cond_init( &scsi_task->uplink_cond, NULL ) != 0 ) { + logadd( LOG_ERROR, "iscsi_scsi_emu_io_block_read: Error while initializing DNBD3 uplink condition for iSCSI SCSI task" ); + + pthread_mutex_destroy( &scsi_task->uplink_mutex ); + + return -ENOMEM; + } + + pthread_mutex_lock( &scsi_task->uplink_mutex ); + success = uplink_request( image, (void *) scsi_task, iscsi_uplink_callback, 0ULL, offset_bytes, (uint32_t) num_bytes ); + + if ( success ) + pthread_cond_wait( &scsi_task->uplink_cond, &scsi_task->uplink_mutex ); + + pthread_mutex_unlock( &scsi_task->uplink_mutex ); + pthread_cond_destroy( &scsi_task->uplink_cond ); + pthread_mutex_destroy( &scsi_task->uplink_mutex ); + } + } + + if ( readFromFile ) { + const int64_t len = pread( image->readFd, scsi_task->buf, (size_t) num_bytes, offset_bytes ); + 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" ); + logadd( LOG_ERROR, "iscsi_scsi_emu_io_block_read: Out of memory while allocating execution queue for I/O read" ); return -ENOMEM; } @@ -5367,9 +5438,6 @@ uint8_t *iscsi_scsi_emu_block_read_complete_callback(dnbd3_image_t *image, uint8 * 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. @@ -5388,7 +5456,7 @@ uint8_t *iscsi_scsi_emu_block_read_complete_callback(dnbd3_image_t *image, uint8 * @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) +int iscsi_scsi_emu_io_block_cmp_write(iscsi_scsi_task *scsi_task, 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. @@ -5440,9 +5508,6 @@ uint8_t *iscsi_scsi_emu_block_write_complete_callback(dnbd3_image_t *image, uint * @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. @@ -5458,16 +5523,16 @@ uint8_t *iscsi_scsi_emu_block_write_complete_callback(dnbd3_image_t *image, uint * @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) +int iscsi_scsi_emu_io_block_write(iscsi_scsi_task *scsi_task, 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 int64_t len = pwrite( image->readFd, scsi_task->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" ); + logadd( LOG_ERROR, "iscsi_scsi_emu_io_block_read: Out of memory while allocating execution queue for I/O write" ); return -ENOMEM; } @@ -5575,7 +5640,7 @@ static int iscsi_scsi_emu_block_read_write(dnbd3_image_t *image, iscsi_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 ); + rc = iscsi_scsi_emu_io_block_read( scsi_task, 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 ); @@ -5589,9 +5654,9 @@ static int iscsi_scsi_emu_block_read_write(dnbd3_image_t *image, iscsi_scsi_task 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 ); + rc = iscsi_scsi_emu_io_block_cmp_write( scsi_task, 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 ); + rc = iscsi_scsi_emu_io_block_write( scsi_task, image, offset_blocks, num_blocks, block_size, iscsi_scsi_emu_block_write_complete_callback, (uint8_t *) scsi_task ); } if ( rc < 0 ) { @@ -5726,7 +5791,7 @@ static int iscsi_scsi_emu_block_write_same(dnbd3_image_t *image, iscsi_scsi_task * @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. + * successfully and reading is not possible. */ static bool iscsi_scsi_emu_image_init(iscsi_scsi_task *scsi_task, const bool access) { diff --git a/src/server/iscsi.h b/src/server/iscsi.h index 193b292..ded2d1d 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -10823,6 +10823,12 @@ typedef struct iscsi_scsi_task { /// I/O task wait. iscsi_scsi_emu_io_wait io_wait; + /// Uplink read mutex for synchronization. + pthread_mutex_t uplink_mutex; + + /// Conditional to signal uplink read complete. + pthread_cond_t uplink_cond; + /// Output buffer. uint8_t *buf; @@ -10935,11 +10941,11 @@ int iscsi_scsi_pr_in(iscsi_scsi_task *scsi_task, iscsi_scsi_pr_reserve_in_parame 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 +int iscsi_scsi_emu_io_block_read(iscsi_scsi_task *scsi_task, 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 +int iscsi_scsi_emu_io_block_cmp_write(iscsi_scsi_task *scsi_task, 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_block_write(iscsi_scsi_task *scsi_task, 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 -- cgit v1.2.3-55-g7522 From 531ba156de326210e4807b701183eaf2f506cf2a Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Fri, 10 Oct 2025 18:13:16 +0200 Subject: [SERVER] iscsi refactor: First working version Work towards simplifying the iscsi implementation has begun. Goals are: - Simpler and easier to understand resource/lifecycle management of allocations - Single-threaded architecture, making locking unnecessary - Moving as many allocations as possible to the stack - Making the call-stack more shallow for easier tracking of code flow --- inc/dnbd3/config/server.h | 2 + src/server/image.c | 2 +- src/server/iscsi.c | 15289 ++++++++------------------------------------ src/server/iscsi.h | 9831 ++++++---------------------- src/server/net.c | 67 +- src/server/sendfile.c | 60 + src/server/sendfile.h | 18 + src/server/server.c | 10 - src/server/uplink.c | 4 +- src/server/uplink.h | 2 +- 10 files changed, 4977 insertions(+), 20308 deletions(-) create mode 100644 src/server/sendfile.c create mode 100644 src/server/sendfile.h (limited to 'src') diff --git a/inc/dnbd3/config/server.h b/inc/dnbd3/config/server.h index b6eee2c..9330915 100644 --- a/inc/dnbd3/config/server.h +++ b/inc/dnbd3/config/server.h @@ -17,6 +17,8 @@ #define UPLINK_MAX_CLIENTS_PER_REQUEST 32 // Maximum number of clients that can attach to one uplink request #define SERVER_UPLINK_QUEUELEN_THRES 900 // Threshold where we start dropping incoming clients #define SERVER_MAX_PENDING_ALT_CHECKS 500 // Length of queue for pending alt checks requested by uplinks +#define SERVER_TCP_BUFFER_MIN_SIZE_PAYLOAD 1048576 // Tweak socket buffer for direction with payload (image data) to be at least this size (1MiB) +#define SERVER_TCP_BUFFER_MIN_SIZE_REQUESTS 8192 // Tweak socket buffer for direction without payload (requests) to be at least this large (8KiB) // Wait a maximum of 5 minutes before saving cache map (if data was received at all) #define CACHE_MAP_MAX_SAVE_DELAY 300 diff --git a/src/server/image.c b/src/server/image.c index a2ff247..5d1f4a1 100644 --- a/src/server/image.c +++ b/src/server/image.c @@ -1361,7 +1361,7 @@ server_fail: ; image_release( image ); } // If everything worked out, this call should now actually return the image - image = image_get( name, acceptedRemoteRid, false ); + image = image_get( name, revision == 0 ? acceptedRemoteRid : revision, false ); if ( image != NULL && uplinkSock != -1 ) { // If so, init the uplink and pass it the socket if ( !uplink_init( image, uplinkSock, &uplinkServer, remoteProtocolVersion ) ) { diff --git a/src/server/iscsi.c b/src/server/iscsi.c index e9b0fc1..2b39955 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -20,7 +20,6 @@ #include #include -#include #include #include #include @@ -36,20 +35,22 @@ #include #include #include -#include #include -#include "fileutil.h" #include "globals.h" #include "helper.h" #include "image.h" -#include "ini.h" #include "iscsi.h" -#include "locks.h" #include "uplink.h" -#include "threadpool.h" #include "reference.h" +#include + +#define ISCSI_DEFAULT_LUN 1 +#define ISCSI_DEFAULT_PROTOCOL_ID 1 +#define ISCSI_DEFAULT_DEVICE_ID 1 +#define ISCSI_DEFAULT_QUEUE_DEPTH 16 + /** * @file iscsi.c * @author Sebastian Vater @@ -63,102 +64,56 @@ * @see https://www.rfc-editor.org/rfc/rfc7143 */ +//#define malloc(x) (rand() % 100 == 0 ? NULL : malloc(x)) -/** - * @brief Allocates and appends a buffer and sprintf's it. - * - * Merges multiple strings using printf style formatting - * and allocates memory for holding the result. - * - * @param[in] buf Buffer to merge into. - * @param[in] format printf style format string. - * @param[in] args Values to fill in the format with. - * @return New buffer which holds the final result. - */ -uint8_t *iscsi_vsprintf_append_realloc(char *buf, const char *format, va_list args) -{ - va_list args_copy; - uint orig_size = 0U; - if ( buf != NULL ) - orig_size = (uint) strlen( (char *) buf ); +static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task); - va_copy( args_copy, args ); - uint new_size = vsnprintf( NULL, 0, format, args_copy ); - va_end( args_copy ); +static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task); - new_size += (uint) (orig_size + 1U); - uint8_t *new_buf = realloc( buf, new_size ); +static void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task); // Allocates and initializes a SCSI task - if ( new_buf == NULL ) { - logadd( LOG_ERROR, "iscsi_vsprintf_append_realloc: Out of memory while allocating string buffer" ); +static void iscsi_scsi_task_xfer_complete(iscsi_scsi_task *scsi_task, iscsi_pdu *pdu); // Callback function when an iSCSI SCSI task completed the data transfer - return NULL; - } +static void iscsi_scsi_task_lun_process_none(iscsi_scsi_task *scsi_task); // Processes a iSCSI SCSI task with no LUN identifier - vsnprintf( (char *) (new_buf + orig_size), (new_size - orig_size), format, args ); - return new_buf; -} +static 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 +static int iscsi_scsi_lun_get_from_iscsi(const uint64_t lun); // Converts an iSCSI LUN from packet data to internal SCSI LUN identifier -/** - * @brief Allocates and appends a buffer and sprintf's it. - * - * Merges strings using printf style formatting and allocates - * memory for holding the result. - * - * @param[in] buf Buffer to merge into. - * @param[in] format printf style format string. - * @param[in] ... Values to fill in the format string with. - * @return New buffer which holds the final result. - */ -uint8_t *iscsi_sprintf_append_realloc(char *buf, const char *format, ...) -{ - va_list args; +static void iscsi_scsi_lun_task_run( iscsi_scsi_task *scsi_task, iscsi_pdu *pdu); // Runs an iSCSI SCSI task for a specified iSCSI SCSI LUN - va_start( args, format ); - uint8_t *ret_buf = iscsi_vsprintf_append_realloc( buf, format, args ); - va_end( args ); +static int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, dnbd3_image_t *image, const uint64_t offset_blocks, const uint64_t num_blocks); // Reads a number of blocks from a block offset of a DNBD3 image to a specified buffer - return ret_buf; -} -/** - * @brief Allocates a buffer and sprintf's it. - * - * Merges strings using printf style formatting and allocates - * memory for holding the result. - * - * @param[in] format printf style format string. - * @param[in] args Values to fill in the format with. - * @return New buffer which holds the final result. - */ -uint8_t *iscsi_vsprintf_alloc(const char *format, va_list args) -{ - return iscsi_vsprintf_append_realloc( NULL, format, args ); -} +static 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 -/** - * @brief Allocates a buffer and sprintf's it. - * - * Allocates a buffer large enough to hold printf style - * string concatenation and fills it using vspnrintf. - * - * @param[in] format Format string to allocate and fill. - * @param[in] ... Values to fill in the format string with. - * @return New buffer containing the formatted string. - */ -uint8_t *iscsi_sprintf_alloc(const char *format, ... ) -{ - va_list args; - va_start( args, format ); - uint8_t *buf = iscsi_vsprintf_alloc( format, args ); - va_end( args ); +static iscsi_task *iscsi_task_create(iscsi_connection *conn); // Allocates and initializes an iSCSI task structure +static void iscsi_task_destroy(iscsi_task *task); // Deallocates resources acquired by iscsi_task_create + +static void iscsi_task_response(iscsi_connection *conn, iscsi_task *task, iscsi_pdu *pdu); // Creates, initializes and sends an iSCSI task reponse PDU. + +static uint64_t iscsi_target_node_wwn_get(const uint8_t *name); // Calculates the WWN using 64-bit IEEE Extended NAA for a name + +static iscsi_session *iscsi_session_create(iscsi_connection *conn, const int type); // Creates and initializes an iSCSI session +static void iscsi_session_destroy(iscsi_session *session); // Deallocates all resources acquired by iscsi_session_create + + +static iscsi_connection *iscsi_connection_create(dnbd3_client_t *client); // Creates data structure for an iSCSI connection from iSCSI portal and TCP/IP socket +static void iscsi_connection_destroy(iscsi_connection *conn); // Deallocates all resources acquired by iscsi_connection_create + +static 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 + +static void iscsi_connection_login_response_reject(iscsi_pdu *login_response_pdu, const iscsi_pdu *pdu); // Initializes a rejecting login response packet +static iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint32_t ds_len); +static void iscsi_connection_pdu_destroy(iscsi_pdu *pdu); // Destroys an iSCSI PDU structure used by connections + +static iscsi_bhs_packet *iscsi_connection_pdu_resize(iscsi_pdu *pdu, const uint ahs_len, const uint32_t ds_len); // Appends packet data to an iSCSI PDU structure used by connections + +static bool iscsi_connection_pdu_write(iscsi_connection *conn, iscsi_pdu *pdu); - return buf; -} /** * @brief Copies a string with additional padding character to fill in a specified size. @@ -175,7 +130,7 @@ uint8_t *iscsi_sprintf_alloc(const char *format, ... ) * @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) +static void iscsi_strcpy_pad(char *dst, const char *src, const size_t size, const int pad) { const size_t len = strlen( src ); @@ -187,12007 +142,2997 @@ void iscsi_strcpy_pad(char *dst, const char *src, const size_t size, const int p } } -/** - * @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. - * - * @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. - * @return A pointer to the hash map structure or NULL in case of an error. - */ -iscsi_hashmap *iscsi_hashmap_create(const uint capacity) +static void iscsi_copy_kvp_int(const char *name, int *dest, const char *src) { - iscsi_hashmap *map = (iscsi_hashmap *) malloc( sizeof(iscsi_hashmap) ); - - if ( map == NULL ) { - logadd( LOG_ERROR, "iscsi_hashmap_create: Out of memory while allocating iSCSI hash map" ); + long long res = 0; + const char *end = NULL; - return map; + if ( *dest != -1 ) { + logadd( LOG_DEBUG1, "Received duplicate entry for key '%s', ignoring (new: %s, old: %d)", name, src, *dest ); + return; } - if ( capacity > 0U ) { - uint new_capacity = (uint) iscsi_align_pow2_ceil( (uint32_t) capacity ); - - 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 - } else { - map->capacity = ISCSI_HASHMAP_DEFAULT_CAPACITY; + if ( *src == '\0' ) { + logadd( LOG_DEBUG1, "Empty value for numeric option '%s', ignoring", name ); + return; } + res = strtoll( src, (char **)&end, 10 ); // WTF why is the second arg not const char ** - map->buckets = (iscsi_hashmap_bucket *) calloc( map->capacity, sizeof(struct iscsi_hashmap_bucket) ); - - if ( map->buckets == NULL ) { - free( map ); - - logadd( LOG_ERROR, "iscsi_hashmap_create: Out of memory while allocating iSCSI hash map buckets" ); - - return NULL; + if ( end == NULL ) { + logadd( LOG_DEBUG1, "base 10 not valid! O.o" ); + return; } + if ( *end != '\0' ) { + logadd( LOG_DEBUG1, "Invalid non-numeric character in value for '%s': '%c' (0x%02x), ignoring option", + name, (int)*end, (int)*end ); + return; + } + if ( res < 0 ) { + res = 0; + } else if ( res > INT_MAX ) { + res = INT_MAX; + } + *dest = (int)res; +} - 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; +static void iscsi_copy_kvp_str(const char *name, const char **dest, const char *src) +{ + if ( *dest != NULL ) { + logadd( LOG_DEBUG1, "Received duplicate entry for key '%s', ignoring (new: %s, old: %s)", name, src, *dest ); + return; + } + *dest = src; } /** - * @brief Deallocates the hash map objects and buckets, not elements. Use iscsi_hashmap_iterate to deallocate the elements themselves. + * @brief Extracts a single text key / value pairs out of an iSCSI packet into a hash map. * - * 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. + * Parses and extracts a specific key and value pair out of an iSCSI packet + * data stream amd puts the extracted data into a hash map to be used by + * the iSCSI implementation. * - * @param[in] map Pointer to hash map and its buckets to deallocate. - * If this is NULL, nothing is done. + * @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. + * @param[in] len Length of the remaining packet data. + * @return Number of bytes used by the extracted 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. */ -void iscsi_hashmap_destroy(iscsi_hashmap *map) +static int iscsi_parse_text_key_value_pair(iscsi_negotiation_kvp *key_value_pairs, const char *packet_data, const uint32_t len) { - if ( map != NULL ) { - if ( map->buckets != NULL ) { - free( map->buckets ); + int key_val_len = (int) strnlen( packet_data, len ); + const char *key_end = memchr( packet_data, '=', key_val_len ); - map->buckets = NULL; - } + if ( key_val_len == (int)len ) { + logadd( LOG_DEBUG1, "iscsi_parse_text_key_value_pair: Final key/value pair not null-terminated, not spec compliant, aborting" ); + return -1; + } + // Account for the trailing nullchar (for return value), which we also consumed + key_val_len++; - free( map ); + if ( key_end == NULL ) { + logadd( LOG_DEBUG1, "iscsi_parse_text_key_value_pair: Key/value separator '=' not found, ignoring" ); + return key_val_len; } -} -/** - * @brief Creates a key suitable for hash map usage (ensures 8-byte boundary and zero padding). - * - * 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] 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). - */ -uint8_t *iscsi_hashmap_key_create(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 ); + const uint key_len = (uint) (key_end - packet_data); + const uint val_len = (uint) (key_val_len - key_len - 1); - if ( key == NULL ) { - logadd( LOG_ERROR, "iscsi_hashmap_key_create: Out of memory while allocating iSCSI hash map key" ); + if ( key_len == 0U ) { + logadd( LOG_DEBUG1, "iscsi_parse_text_key_value_pair: Empty key, not allowed according to iSCSI specs, ignoring" ); + return key_val_len; + } - return key; + if ( key_len > ISCSI_TEXT_KEY_MAX_LEN ) { + logadd( LOG_DEBUG1, "iscsi_parse_text_key_value_pair: Key is too long (max %d bytes), ignoring", ISCSI_TEXT_KEY_MAX_LEN ); + return key_val_len; } - memcpy( key, data, len ); - memset( (key + len), 0, (key_size - len) ); // Zero pad additional bytes in case length is not a multiple of 8 + if ( val_len > ISCSI_TEXT_VALUE_MAX_LEN ) { + logadd( LOG_DEBUG1, "iscsi_parse_text_key_value_pair: Value for '%.*s' is too long (max %d bytes), ignoring", + (int)key_len, packet_data, ISCSI_TEXT_VALUE_MAX_LEN ); + return key_val_len; + } - return key; -} +#define COPY_KVP(type, key) \ + else if ( strncmp( packet_data, #key, key_len ) == 0 ) iscsi_copy_kvp_ ## type ( #key, &key_value_pairs->key, key_end + 1 ) -/** - * @brief Creates an unique key identifier suitable for hash map usage (ensures 8-byte boundary and zero padding). - * - * 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 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. - */ -void iscsi_hashmap_key_create_id(iscsi_hashmap *map, uint64_t *key) -{ - *key = ++map->last_insert_id; + if ( 0 ) {} + COPY_KVP( int, MaxRecvDataSegmentLength ); + COPY_KVP( int, MaxBurstLength ); + COPY_KVP( int, FirstBurstLength ); + COPY_KVP( int, MaxConnections ); + COPY_KVP( int, ErrorRecoveryLevel ); + COPY_KVP( str, SessionType ); + COPY_KVP( str, AuthMethod ); + COPY_KVP( str, SendTargets ); + COPY_KVP( str, HeaderDigest ); + COPY_KVP( str, DataDigest ); + COPY_KVP( str, InitiatorName ); + COPY_KVP( str, TargetName ); + else { + logadd( LOG_DEBUG1, "iscsi_parse_text_key_value_pair: Unknown option: '%.*s'", (int)key_len, packet_data ); + } + +#undef COPY_KVP + + return (int)key_val_len; } /** - * @brief Deallocates all resources acquired by iscsi_hashmap_create_key. + * @brief Extracts all text key / value pairs out of an iSCSI packet into a hash map. * - * Deallocates a key allocated with the function - * iscsi_hashmap_key_create. + * Parses and extracts all key and value pairs out of iSCSI packet + * data amd puts the extracted data into a hash map to be used by + * the iSCSI implementation. * - * @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 ); -} - -/** - * @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. + * @param[in] pairs struct to write all key-value-pair options from packet to + * 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. + * @param[in] len Length of the remaining packet data. + * @retval -1 An error occured during parsing key. + * @retval 0 Key and value pair was parsed successfully and was added to + * hash map. */ -int iscsi_hashmap_key_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) +static int iscsi_parse_login_key_value_pairs(iscsi_negotiation_kvp *pairs, const uint8_t *packet_data, uint len) { - iscsi_hashmap_key_destroy( key ); + memset( pairs, -1 , sizeof(*pairs) ); + pairs->SessionType = NULL; + pairs->AuthMethod = NULL; + pairs->SendTargets = NULL; + pairs->HeaderDigest = NULL; + pairs->DataDigest = NULL; + pairs->InitiatorName = NULL; + pairs->TargetName = NULL; - return 0; -} + if ( len == 0U ) + return 0; // iSCSI specs don't allow zero length -/** - * @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 ); + int offset = 0; - return 0; -} + while ( ((uint) offset < len) && (packet_data[offset] != '\0') ) { + const int rc = iscsi_parse_text_key_value_pair( pairs, (const char *)(packet_data + offset), (len - offset) ); -/** - * @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 ); + if ( rc <= 0 ) + return -1; - iscsi_hashmap_key_destroy( key ); + offset += rc; + } return 0; } /** - * @brief Compares two hash keys with equal length match. + * @brief Allocates and initializes an iSCSI task structure. * - * 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. + * 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] 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 + * @param[in] conn Pointer to iSCSI connection to associate + * the task with. May NOT be NULL, so take * caution. - * @param[in] num_blocks Number of blocks in QWORDs (8 bytes) - * to be compared. - */ -static inline bool iscsi_hashmap_key_eq(const uint64_t *buf, const uint64_t *key, size_t num_blocks) -{ - do { - if ( *buf++ != *key++ ) - return false; - } while ( --num_blocks > 0UL ); - - return true; -} - -/** - * @brief Finds a bucket by key of a specified hash map by key, key size and hash code. - * - * Finds a bucket by key of a specified hash map by - * key, key size and hash code. This function may - * only be called if the bucket is guaranteed to - * be found, otherwise this function hangs, so be - * careful. - * - * @param[in] map Pointer to hash map where the key to be - * searched for is located, may NOT be NULL, so be careful. - * @param[in] key Pointer to key. NULL is invalid, so be - * careful. - * @param[in] key_size Number of bytes for the key. - * @param[in] hash Hash of the key to be searched for. - * @return Pointer to found bucket. + * @return Pointer to iSCSI task structure or NULL + * in case of an error (memory exhaustion). */ -static iscsi_hashmap_bucket *iscsi_hashmap_find_entry(iscsi_hashmap *map, const uint8_t *key, size_t key_size, uint32_t hash) +static iscsi_task *iscsi_task_create(iscsi_connection *conn) { - 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]; + iscsi_task *task = malloc( sizeof(struct iscsi_task) ); - 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; + if ( task == NULL ) { + logadd( LOG_ERROR, "iscsi_task_create: Out of memory while allocating iSCSI task" ); - index = ((index + 1UL) & (map->capacity - 1U)); + return NULL; } -} -/** - * @brief Calculates the hash code of data with a specified length. - * - * Calculates the hash code of data with a specified - * length. - * - * @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. - */ -static inline uint32_t iscsi_hashmap_key_hash_data(const uint8_t *data, const size_t len) -{ - 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; + task->conn = conn; + 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; - do { - hash ^= *hash_data++; - hash *= ISCSI_HASHMAP_HASH_MUL; - } while ( --num_blocks > 0UL ); + iscsi_scsi_task_create( &task->scsi_task ); + task->scsi_task.connection = conn; - return (uint32_t) (hash ^ hash >> 32ULL); + return task; } /** - * @brief Puts an old bucket into a resized hash map. + * @brief Deallocates resources acquired by iscsi_task_create. * - * Puts an old bucket into a resized hash map. + * This function also frees the embedded SCSI task. * - * @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] task Pointer to iSCSI task to deallocate. If + * set to NULL, this function does nothing. */ -static iscsi_hashmap_bucket *iscsi_hashmap_resize_entry(iscsi_hashmap *map, const iscsi_hashmap_bucket *old_entry) +static void iscsi_task_destroy(iscsi_task *task) { - 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; - } + if ( task == NULL ) + return; - index = ((index + 1) & (map->capacity - 1U)); - } + free( task->scsi_task.buf ); + free( task ); } /** - * @brief Resizes a hash map by doubling its bucket capacity. + * @brief Sends a single iSCSI SCSI Data In packet to the client. * - * Resizes a hash map by doubling its bucket capacity The - * old bucket list is freed after the - * resize operation has been finished. + * This function reads the data from the + * associated DNBD3 image as well and sends + * it to the initiator. * - * @param[in] map Pointer to hash map to resize. This may NOT be + * @pararm[in] conn Pointer to iSCSI connection for which the + * packet should be sent for. May NOT be * NULL, so be careful. - * @retval -1 An error occured during resize. - * @retval 0 Hash map has been resized successfully. + * @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 int iscsi_hashmap_resize(iscsi_hashmap *map) +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, bool immediate) { - 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}; - - map->capacity <<= ISCSI_HASHMAP_RESIZE_SHIFT; + iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, len ); - 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; + if ( response_pdu == NULL ) { + logadd( LOG_ERROR, "iscsi_scsi_data_in_send: Out of memory while allocating iSCSI SCSI Data In response PDU" ); - return -1; + return data_sn; } - 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; + response_pdu->task = task; - current = iscsi_hashmap_resize_entry( map, current ); + iscsi_scsi_data_in_response_packet *scsi_data_in_pkt = (iscsi_scsi_data_in_response_packet *) response_pdu->bhs_pkt; - iscsi_list_enqueue( &map->list, ¤t->node ); - } + 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; - free( old_buckets ); + 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)); - return 0; -} + if ( !immediate ) { + conn->session->max_cmd_sn++; + } -/** - * @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 - * buckets are resized automatically if required.\n - * This function neither does make a copy of the key, - * 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.\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. - * @param[in] value Value of the key to add, NULL is - * allowed. - * @retval -1 Adding key / value pair would have required - * hash map resizing which failed (probably due to - * memory exhaustion). - * @retval 0 Key / value pair was added successfully. - */ -int iscsi_hashmap_put(iscsi_hashmap *map, uint8_t *key, const size_t key_size, uint8_t *value) -{ - if ( ((map->count + 1U) > map->cap_load) && (iscsi_hashmap_resize( map ) < 0) ) - return -1; + iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->res_cnt, res_cnt ); + } else { + scsi_data_in_pkt->res_cnt = 0UL; + } - 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 ); + 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; + } - if ( entry->key == NULL ) { - iscsi_list_enqueue( &map->list, &entry->node ); + 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 ); - map->count++; + const uint32_t offset = (task->scsi_task.pos + pos); + iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->buf_offset, offset ); - entry->key = key; - entry->key_size = key_size; - entry->hash = hash; - } + memcpy( response_pdu->ds_cmd_data, (task->scsi_task.buf + pos), len ); - entry->value = value; + iscsi_connection_pdu_write( conn, response_pdu ); - return 0; + return (data_sn + 1UL); } /** - * @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 - * does exist, value will be set in `*out_in`, - * meaning the value of the pair will be in - * '*out_in` regardless of whether or not it it - * existed in the first place.\n - * The buckets are resized automatically if required. - * This function neither does make a copy of the key, - * 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. - * - * @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. - * @param[in,out] out_in_value Value of the key to add, - * NULL is allowed. - * @retval -1 Adding key / value pair would have required - * hash map resizing which failed (probably due to - * memory exhaustion). - * @retval 0 Key / value pair was added successfully. - * @retval 1 Key already existed. - */ -int iscsi_hashmap_get_put(iscsi_hashmap *map, uint8_t *key, const size_t key_size, uint8_t **out_in_value) -{ - if ( ((map->count + 1U) > map->cap_load) && (iscsi_hashmap_resize( map ) < 0) ) - return -1; - - 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_enqueue( &map->list, &entry->node ); - - entry->key = key; - entry->key_size = key_size; - entry->hash = hash; - entry->value = *out_in_value; + * @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. + * @param immediate + * @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, bool immediate) +{ + if ( task->scsi_task.status != ISCSI_SCSI_STATUS_GOOD ) + return 0; - map->count++; + const uint32_t pos = task->scsi_task.xfer_pos; + uint32_t xfer_len = task->scsi_task.len; + const uint32_t seg_len = conn->session->opts.MaxRecvDataSegmentLength; + uint32_t res_cnt = 0UL; + int8_t flags = 0; - return 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; } - *out_in_value = entry->value; - - return 1; -} + uint32_t data_sn = task->data_sn; + uint32_t max_burst_offset = 0UL; + const uint32_t max_burst_len = conn->session->opts.MaxBurstLength; + const uint32_t data_in_seq_count = ((xfer_len - 1UL) / max_burst_len) + 1UL; + int8_t status = 0; -/** - * @brief Assigns key / value pair to hash map without making copies with callback function in case the key already exists. - * - * Adds a key / value pair to a specified hash map - * bucket list. If the key already exists, it will - * be overwritten and a callback function will be - * invoked in order to allow, e.g. deallocation of - * resources, if necessary. The buckets are resized - * automatically if required. This function neither - * does make a copy of the key, nor of the value.\n - * 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. - * - * @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. - * @param[in] value Value of the key to add, NULL is - * allowed. - * @param[in] callback Callback function which allows, - * for example, a dallocation of resources for the - * overwritten key and value pair. The function is - * invoked just before overwriting the old values. - * 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. - * @return -1 in case adding key / value pair would - * have required hash map resizing which failed - * (probably due to memory exhaustion), 0 if the - * Key / value pair was added successfully and - * the callback function also returned 0, otherwise - * the return value got by the callbuck function. - */ -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 + 1U) > map->cap_load) && (iscsi_hashmap_resize( map ) < 0) ) - return -1; + for ( uint32_t i = 0UL; i < data_in_seq_count; i++ ) { + uint32_t seq_end = (max_burst_offset + max_burst_len); - 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 ( seq_end > xfer_len ) + seq_end = xfer_len; - if ( entry->key == NULL ) { - iscsi_list_enqueue( &map->list, &entry->node ); + for ( uint32_t offset = max_burst_offset; offset < seq_end; offset += seg_len ) { + uint32_t len = (seq_end - offset); - entry->key = key; - entry->key_size = key_size; - entry->hash = hash; - entry->value = value; + if ( len > seg_len ) + len = seg_len; - map->count++; + flags &= (int8_t) ~(ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS | ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_FINAL); - return 0; - } + if ( (offset + len) == seq_end ) { + flags |= (int8_t) ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_FINAL; - int err = callback( entry->key, key_size, entry->value, user_data ); + if ( (task->scsi_task.sense_data_len == 0U) && ((offset + len) == xfer_len) && (task->des_data_xfer_pos == task->scsi_task.xfer_len) ) { + flags |= (int8_t) ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS; + status |= flags; + } + } - entry->key = key; - entry->value = value; + data_sn = iscsi_scsi_data_in_send( conn, task, offset, len, res_cnt, data_sn, flags, immediate ); + } - return err; -} + max_burst_offset += max_burst_len; + } -/** - * @brief Checks whether a specified key exists. - * - * 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 - * 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. - * @retval true The key exists. - * @retval false The key does not exist. - */ -bool iscsi_hashmap_contains(iscsi_hashmap *map, const uint8_t *key, const size_t 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 ); + task->data_sn = data_sn; - return (entry->key != NULL); + return (status & ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS); } /** - * @brief Retrieves the value of a specified key. + * @brief Creates, initializes and sends an iSCSI task reponse PDU. * - * 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. + * This function also receives any remaining + * incoming data in case the task is reading. * - * @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. - * @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[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, + * @param[in] conn Pointer to iSCSI connection to handle the + * task resnponse for and may NOT be NULL, * so be careful. - * @retval 0 The key has been found and its value stored - * in the 'out_value' parameter. - * @retval -1 The key has not been found and NULL has been - * stored in the 'out_value' parameter. + * @param[in] task Pointer to iSSCI task to create the + * response PDU from. NULL is NOT allowed + * here, take caution. */ -int iscsi_hashmap_get(iscsi_hashmap *map, const uint8_t *key, const size_t key_size, uint8_t **out_value) +static void iscsi_task_response(iscsi_connection *conn, iscsi_task *task, iscsi_pdu *pdu) { - 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 ); + iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) pdu->bhs_pkt; + const uint32_t xfer_len = task->scsi_task.xfer_len; - *out_value = entry->value; + if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_READ) != 0 ) { + const int rc = iscsi_task_xfer_scsi_data_in( conn, task, (pdu->bhs_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) != 0 ); - return ((entry->key != NULL) ? 0 : -1); -} + if ( (rc > 0) || (task->des_data_xfer_pos != task->scsi_task.xfer_len) ) + return; + } -/** - * @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. 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. - * @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. - */ -void iscsi_hashmap_remove(iscsi_hashmap *map, const uint8_t *key, const size_t 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 ); + 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, ds_len ); + + 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 ( entry->key != NULL ) { - iscsi_list_remove( &entry->node ); + if ( task->scsi_task.sense_data_len != 0U ) { + iscsi_scsi_ds_cmd_data *ds_cmd_data_pkt = response_pdu->ds_cmd_data; - map->count--; + 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 ); - entry->key = NULL; - entry->value = NULL; + 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. } -} -/** - * @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 - * 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. - * @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] 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. - * @param[in,out] user_data Pointer to user specific data - * 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_key_hash_data( key, key_size ); - iscsi_hashmap_bucket *entry = iscsi_hashmap_find_entry( map, key, key_size, hash ); + response_pdu->task = task; + + scsi_response_pkt->opcode = ISCSI_OPCODE_SERVER_SCSI_RESPONSE; + scsi_response_pkt->flags = -0x80; + scsi_response_pkt->response = ISCSI_SCSI_RESPONSE_CODE_OK; - if ( entry->key != NULL ) { - iscsi_list_remove( &entry->node ); + const uint32_t pos = task->scsi_task.xfer_pos; - map->count--; + if ( (xfer_len != 0UL) && (task->scsi_task.status == ISCSI_SCSI_STATUS_GOOD) ) { + if ( pos < xfer_len ) { + const uint32_t res_cnt = (xfer_len - pos); - callback( entry->key, entry->key_size, entry->value, user_data ); + 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); - entry->key = NULL; - entry->value = NULL; + 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 ); } /** - * @brief Retrieves the number of elements of the hash map. + * @brief Initializes a SCSI task. * - * 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. - * @return Number of elements currently in use by the - * hash map. + * @param[in] scsi_task Pointer to SCSI task. This + * may NOT be NULL, so be careful. */ -uint iscsi_hashmap_size(const iscsi_hashmap *map) +static void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task) { - return map->count; + scsi_task->cdb = 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->xfer_pos = 0UL; + scsi_task->xfer_len = 0UL; + scsi_task->sense_data_len = 0U; + scsi_task->status = ISCSI_SCSI_STATUS_GOOD; } /** - * @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, 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. + * @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] 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. 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. + * @param[in] scsi_task Pointer to iSCSI SCSI task which finished + * the data transfer and may NOT be NULL, + * so be careful. + * @param pdu */ -int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, uint8_t *user_data) +static void iscsi_scsi_task_xfer_complete(iscsi_scsi_task *scsi_task, iscsi_pdu *pdu) { - iscsi_hashmap_bucket *current; - iscsi_hashmap_bucket *tmp; - int err = 0; - - iscsi_list_foreach_safe_node ( &map->list, current, tmp ) { - if ( current->key == NULL ) - continue; - - err = callback( current->key, current->key_size, current->value, user_data ); + iscsi_task *task = container_of( scsi_task, iscsi_task, scsi_task ); + iscsi_connection *conn = task->conn; - if ( err < 0 ) - break; - } + task->des_data_xfer_pos += task->scsi_task.len; - return err; + iscsi_task_response( conn, task, pdu ); + iscsi_task_destroy( task ); } - -/// iSCSI global vector. MUST be initialized with iscsi_create before any iSCSI functions are used. -iscsi_globals *iscsi_globvec = NULL; - -/// 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; - - -/// 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 } -}; - -/// 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 Initializes a global key and value pair with type and list / range informations for fast access. + * @brief Allocates, if necessary and initializes SCSI sense data for check condition status code. * - * This function is used to initialize the iSCSI - * global key and value pair list containing - * the key types and allowed values. + * This function is invoked whenever additional + * SCSI sense data for check condition status + * code is required for sending to the + * initiator. * - * @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. + * @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). */ -static int iscsi_global_key_value_pair_init(iscsi_hashmap *key_value_pairs, const iscsi_key_value_pair_lut_entry *lut) +static 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) { - 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) ); + 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 ( key_value_pair == NULL ) { - logadd( LOG_ERROR, "iscsi_global_key_value_pair_init: Out of memory allocating key value pair" ); + 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 -1; + return; } - const uint key_len = (uint) (strlen( (char *) lut[i].key ) + 1U); + scsi_task->sense_data = (iscsi_scsi_sense_data_packet *) sense_data; + } - 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; + 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)); - const int rc = iscsi_hashmap_put( key_value_pairs, (uint8_t *) lut[i].key, key_len, (uint8_t *) key_value_pair ); + 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 = 0UL; + sense_data->sense_key_spec_flags = 0U; + sense_data->sense_key_spec = 0U; // Zero does not require endianess conversion - if ( rc < 0 ) { - free( key_value_pair ); + scsi_task->sense_data_len = sizeof(struct iscsi_scsi_sense_data_check_cond_packet); +} - return rc; - } - } +/** + * @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 ); - return 0; + scsi_task->status = status; } /** - * @brief Allocates and initializes the iSCSI global vector structure. + * @brief Processes a iSCSI SCSI task with no LUN identifier. * - * 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. + * 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. * - * @return 0 if the iSCSI global vector has been initialized - * successfully and is ready to use, a negative - * error code otherwise (memory exhausted). + * @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. */ -int iscsi_create() +static void iscsi_scsi_task_lun_process_none(iscsi_scsi_task *scsi_task) { - pthread_rwlock_wrlock( &iscsi_globvec_rwlock ); - - if ( iscsi_globvec == NULL ) { - iscsi_globals *globvec = (iscsi_globals *) malloc( sizeof(struct iscsi_globals) ); + iscsi_scsi_std_inquiry_data_packet std_inquiry_data_pkt; + iscsi_scsi_cdb_inquiry *cdb = (iscsi_scsi_cdb_inquiry *) scsi_task->cdb; - if ( globvec == NULL ) { - logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector" ); + scsi_task->len = scsi_task->xfer_len; - pthread_rwlock_unlock( &iscsi_globvec_rwlock ); + if ( cdb->cdb.opcode == ISCSI_SCSI_OPCODE_INQUIRY ) { + uint len = sizeof(struct iscsi_scsi_std_inquiry_data_packet); - return -1; - } + memset( &std_inquiry_data_pkt, 0, len ); - globvec->devices = iscsi_hashmap_create( _maxImages ); + 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)); - if ( globvec->devices == NULL ) { - logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector devices hash map" ); + const uint alloc_len = iscsi_get_be16(cdb->alloc_len); - free( globvec ); - pthread_rwlock_unlock( &iscsi_globvec_rwlock ); + if ( len > alloc_len ) + len = alloc_len; - return -1; - } + memcpy( scsi_task->buf, &std_inquiry_data_pkt, len ); - 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" ); + 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 ); - 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 -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; - } - - pthread_rwlock_unlock( &iscsi_globvec_rwlock ); - - return 0; -} - -/** - * @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() -{ - pthread_rwlock_wrlock( &iscsi_globvec_rwlock ); - - iscsi_globals *globvec = iscsi_globvec; - - if ( globvec != NULL ) { - iscsi_globvec = NULL; - - 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 ); - } - - pthread_rwlock_unlock( &iscsi_globvec_rwlock ); -} - -/** - * @brief Parses an INI configuration value and returns an integer representation of string with special boolean and suffixes handling. - * - * 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] value Pointer to string for parsing. May - * NOT be NULL, so be careful. - * @return Parsed integer value or -2147483648 - * in case of an error. - */ -static int32_t iscsi_config_parse_int(const uint8_t *value) -{ - if ( *value == '\0' ) - return -2147483648L; - - 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; - - uint8_t *val_end; - int64_t rc = (int64_t) strtoll( (char *) value, (char **) &val_end, 10 ); - - if ( value == val_end ) - return -2147483648L; - - while ( (*val_end == '\t') || (*val_end == ' ') ) { - val_end++; - } - - switch ( *val_end ) { - case '\0' : { - break; - } - case 'm' : { - rc *= 60LL; - val_end++; - - break; - } - case 'h' : { - rc *= 3600LL; - val_end++; - - break; - } - case 'd' : { - rc *= 86400LL; - val_end++; - - break; - } - default : { - const uint8_t c = (*val_end++ | ('a' - 'A')); - const bool ten = ((*val_end | ('a' - 'A')) == 'b'); - - switch ( c ) { - case 'k' : { - rc = (ten ? (rc * 1000LL) : (rc << 10LL)); - - break; - } - case 'm' : { - rc = (ten ? (rc * 1000000LL) : (rc << 20LL)); - - break; - } - case 'g' : { - rc = (ten ? (rc * 1000000000LL) : (rc << 30LL)); - - break; - } - case 't' : { - rc = (ten ? (rc * 1000000000000LL) : (rc << 40LL)); - - break; - } - case 'p' : { - rc = (ten ? (rc * 1000000000000000LL) : (rc << 50LL)); - - break; - } - case 'e' : { - rc = (ten ? (rc * 1000000000000000000LL) : (rc << 60LL)); - - break; - } - default : { - return -2147483648L; - - break; - } - } - - if ( ten ) - val_end++; - - break; - } - } - - if ( *val_end != '\0' ) - return -2147483648L; - - if ( rc < -2147483647LL ) - rc = -2147483647LL; - else if ( rc > 2147483647LL ) - rc = 2147483647LL; - - return (int32_t) rc; -} - -/** - * @brief Callback function from DNBD3 INI parser invoked for handling a specific key=value pair in a specified INI section. - * - * 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] 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 ( (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 ( key_len == 0U ) - return 0; - - uint8_t *hash_key = iscsi_hashmap_key_create( pattern, key_len ); - - 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" ); - - return 0; - } - - 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 ); - - if ( scsi_device_config == NULL ) { - scsi_device_config = (iscsi_scsi_device_config *) malloc( sizeof(struct iscsi_scsi_device_config) ); - - if ( scsi_device_config == NULL ) { - logadd( LOG_ERROR, "iscsi_config_load_from_ini: Out of memory allocating memory for iSCSI SCSI device configuration" ); - - iscsi_hashmap_key_destroy( hash_key ); - - return 0; - } - - scsi_device_config->flags = 0; - - if ( (globvec->flags & ISCSI_GLOBALS_FLAGS_INIT_R2T) != 0 ) - scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_INIT_R2T; - - if ( (globvec->flags & ISCSI_GLOBALS_FLAGS_IMMEDIATE_DATA) != 0 ) - scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_IMMEDIATE_DATA; - - 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; - - 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; - - if ( (globvec->flags & ISCSI_GLOBALS_FLAGS_SCSI_IO_REMOVABLE) != 0 ) - scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_REMOVABLE; - - if ( (globvec->flags & ISCSI_GLOBALS_FLAGS_SCSI_IO_UNMAP) != 0 ) - scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_UNMAP; - - 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 ); - - 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 Loads iSCSI server configuration from INI file. - * - * This function parses the INI configuration file - * and assigns it to the config section of the iSCSI - * global vector. - * - * @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. - */ -int iscsi_config_load(iscsi_globals *globvec) -{ - char *name = (char *) iscsi_sprintf_alloc( "%s/%s", _configDir, CONFIG_FILENAME ); - - if ( name == NULL ) - return -1; - - if ( !file_isReadable( name ) ) { - free( name ); - - return 0; - } - - 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 ); - - name = (char *) iscsi_sprintf_alloc( "%s/%s", _configDir, ISCSI_GLOBALS_CONFIG_FILENAME ); - - if ( name == NULL ) { - pthread_mutex_unlock( &globvec->scsi_device_config_mutex ); - - 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 Finds an iSCSI SCSI device configuration by name using pattern matching. - * - * Callback function for each element while iterating - * through the iSCSI SCSI device configuration 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 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. - */ -int iscsi_config_get_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) -{ - 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) ); - - if ( rc == FNM_NOMATCH ) - return 0; - - if ( rc != 0 ) - return -2; - - scsi_device_config_find->scsi_device_config = (iscsi_scsi_device_config *) value; - - return -1; -} - -/** - * @brief Retrieves a configuration value either from the iSCSI global vector or for a specified SCSI device name. - * - * This function uses wildcard matching - * only if the SCSI device name does NOT - * have a direct match. - * - * @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. - */ -int32_t iscsi_config_get(uint8_t *name, const int type) -{ - if ( name != NULL ) { - const uint key_len = (uint) (strlen( (char *) name ) + 1U); - uint8_t *hash_key = iscsi_hashmap_key_create( name, key_len ); - - if ( hash_key == NULL ) { - logadd( LOG_ERROR, "iscsi_config_get: Out of memory allocating memory for iSCSI SCSI device configuration key" ); - - return -1L; - } - - pthread_mutex_lock( &iscsi_globvec->scsi_device_config_mutex ); - - 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 ); - - if ( rc < 0 ) { - iscsi_scsi_device_config_find scsi_device_config_find = {NULL, name}; - - rc = iscsi_hashmap_iterate(iscsi_globvec->scsi_device_config, iscsi_config_get_callback, (uint8_t *) &scsi_device_config_find ); - - scsi_device_config = scsi_device_config_find.scsi_device_config; - - 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 ( new_scsi_device_config == NULL ) { - logadd( LOG_ERROR, "iscsi_config_get: Out of memory allocating memory for new iSCSI SCSI device configuration" ); - - pthread_mutex_unlock( &iscsi_globvec->scsi_device_config_mutex ); - iscsi_hashmap_key_destroy( hash_key ); - - return -1L; - } - - 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 ); - - 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; - } - } - - pthread_mutex_unlock( &iscsi_globvec->scsi_device_config_mutex ); - - if ( hash_key != NULL ) - iscsi_hashmap_key_destroy( hash_key ); - - if ( scsi_device_config != NULL ) { - switch ( type ) { - case ISCSI_GLOBALS_CONFIG_TYPE_HEADER_DIGEST : { - return scsi_device_config->header_digest; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_DATA_DIGEST : { - return scsi_device_config->data_digest; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_MAX_RECV_DS_LEN : { - return scsi_device_config->max_recv_ds_len; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_MAX_SESSION_CONNS : { - return scsi_device_config->max_session_conns; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_MAX_OUTSTANDING_R2T : { - return scsi_device_config->max_outstanding_r2t; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_DEFAULT_TIME_TO_WAIT : { - return scsi_device_config->default_time_to_wait; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_DEFAULT_TIME_TO_RETAIN : { - return scsi_device_config->default_time_to_retain; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_FIRST_BURST_LEN : { - return scsi_device_config->first_burst_len; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_MAX_BURST_LEN : { - return scsi_device_config->max_burst_len; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_ERR_RECOVERY_LEVEL : { - return scsi_device_config->err_recovery_level; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE : { - return scsi_device_config->scsi_physical_block_size; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_DEVICE_TYPE : { - return scsi_device_config->scsi_device_type; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE_SHIFT : { - return scsi_device_config->scsi_physical_block_size_shift; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE : { - return scsi_device_config->scsi_logical_block_size; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE_SHIFT : { - return scsi_device_config->scsi_logical_block_size_shift; - - 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); - - 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); - - 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_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); - - 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); - - 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_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); - - 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); - - 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; - } - default : { - return -1L; - - break; - } - } - } - } - - switch ( type ) { - case ISCSI_GLOBALS_CONFIG_TYPE_HEADER_DIGEST : { - return iscsi_globvec->header_digest; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_DATA_DIGEST : { - return iscsi_globvec->data_digest; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_MAX_RECV_DS_LEN : { - return iscsi_globvec->max_recv_ds_len; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_MAX_SESSION_CONNS : { - return iscsi_globvec->max_session_conns; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_MAX_OUTSTANDING_R2T : { - return iscsi_globvec->max_outstanding_r2t; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_DEFAULT_TIME_TO_WAIT : { - return iscsi_globvec->default_time_to_wait; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_DEFAULT_TIME_TO_RETAIN : { - return iscsi_globvec->default_time_to_retain; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_FIRST_BURST_LEN : { - return iscsi_globvec->first_burst_len; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_MAX_BURST_LEN : { - return iscsi_globvec->max_burst_len; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_ERR_RECOVERY_LEVEL : { - return iscsi_globvec->err_recovery_level; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_DEVICE_TYPE : { - return iscsi_globvec->scsi_device_type; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE : { - return iscsi_globvec->scsi_physical_block_size; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE_SHIFT : { - return iscsi_globvec->scsi_physical_block_size_shift; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE : { - return iscsi_globvec->scsi_logical_block_size; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE_SHIFT : { - return iscsi_globvec->scsi_logical_block_size_shift; - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_INIT_R2T : { - return (((iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_INIT_R2T) != 0) ? 1L : 0L); - - 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_GLOBALS_CONFIG_TYPE_FLAGS_DATA_SEQ_IN_ORDER : { - return (((iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_DATA_SEQ_IN_ORDER) != 0) ? 1L : 0L); - - break; - } - case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_REMOVABLE : { - return (((iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_SCSI_IO_REMOVABLE) != 0) ? 1L : 0L); - - 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_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_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_PHYSICAL_READ_ONLY : { - return (((iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_SCSI_IO_PHYSICAL_READ_ONLY) != 0) ? 1L : 0L); - - 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); - - 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 -1L; - - break; - } - } - - return -1L; -} - -/** - * @brief Extracts a single text key / value pairs out of an iSCSI packet into a hash map. - * - * Parses and extracts a specific key and value pair out of an iSCSI packet - * data stream amd puts the extracted data into a hash map to be used by - * the iSCSI implementation. - * - * @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. - * @param[in] len Length of the remaining packet data. - * @return Number of bytes used by the extracted 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. - */ -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 ); - - if ( key_end == NULL ) { - logadd( LOG_ERROR, "iscsi_parse_text_key_value_pair: Key / value separator '=' not found" ); - - return -1; - } - - const uint key_len = (uint) (key_end - packet_data); - - 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 -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 -1; - } - - 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 -1; - - hash_key[key_len] = '\0'; - - 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 -1; - } - - 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 -1; - } - - 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 -1; - } - - memcpy( hash_val, (key_end + 1), val_len ); - - const int rc = iscsi_hashmap_put( key_value_pairs, hash_key, hash_key_len, hash_val ); - - if ( rc < 0 ) - return -1; - - return (int) (hash_key_len + val_len); -} - -/** - * @brief Extracts all text key / value pairs out of an iSCSI packet into a hash map. - * - * Parses and extracts all key and value pairs out of iSCSI packet - * data amd puts the extracted data into a hash map to be used by - * the iSCSI implementation. - * - * @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. - * @param[in] len Length of the remaining packet data. - * @param[in] c_bit Non-zero value of C bit was set in previously. - * @param[in] partial_pairs Array of partial pair pointers in - * case C bit was set (multiple iSCSI packets for text data). - * @retval -1 An error occured during parsing key. - * @retval 0 Key and value pair was parsed successfully and was added to - * 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) -{ - 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; - - for (key_val_pair_len = 0; (key_val_pair_len < len) && packet_data[key_val_pair_len] != '\0'; key_val_pair_len++) { - } - - uint8_t *tmp_partial_buf = iscsi_sprintf_alloc( "%s%s", *partial_pairs, (const char *) packet_data ); - - if ( tmp_partial_buf == NULL ) - return -1; - - 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 -1; - - free( *partial_pairs ); - *partial_pairs = NULL; - - packet_data += (key_val_pair_len + 1); - len -= (key_val_pair_len + 1); - } - - if ( c_bit ) { // Strip partial parameters in case C bit was enabled previousley - if ( partial_pairs == NULL ) { - logadd( LOG_ERROR, "iscsi_parse_key_value_pairs: C bit set but missing partial parameter" ); - - return -1; - } - - uint 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 != 0U ) - key_val_pair_len++; // NUL char found, don't copy to target buffer' - - *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 -1; - } - - memcpy( *partial_pairs, &packet_data[key_val_pair_len], (len - key_val_pair_len) ); - - if ( key_val_pair_len != 0U ) - len = (key_val_pair_len - 1U); - else - return 0; - } - - int offset = 0; - - while ( ((uint) offset < len) && (packet_data[offset] != '\0') ) { - const int rc = iscsi_parse_text_key_value_pair( key_value_pairs, (packet_data + offset), (len - offset) ); - - if ( rc < 0 ) - return -1; - - offset += rc; - } - - return 0; -} - -/** - * @brief Extracts a string from a key and value pair. - * - * This function calculates the length of the key - * for the hash map function and returns the value - * as string. - * - * @param[in] key_value_pairs The hash map containing the key and value pairs to be extracted. - * @param[in] key The key to retrieve the string value from. - * @param[out] out_value The string value of the key is stored here. - * @retval -1 An error occured during value retrieval. - * 'out value' is unchanged. - * @retval 0 The value of the key has been successfully - * stored in the 'out_value'. - */ -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 ) + 1U); - - return iscsi_hashmap_get( key_value_pairs, key, key_len, out_value ); -} - -/** - * @brief Allocates and adds a string value to a key / value hash map pair. - * - * This function allocates memory for a string key - * and its string value. - * - * @param[in] key_value_pairs Pointer to the hash map which should - * contain the added string key and value pair. - * NULL is NOT allowed here, so be careful. - * @param[in] key String containing the key name as string. May - * NOT be NULL, so take caution. - * @param[in] value String containing the value to be stored. - * @return 0 on successful operation, or a negative value on - * error (memory exhaustion). - */ -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 ) + 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 -1; - } - - 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 -1; - } - - memcpy( hash_val, value, val_len ); - - return iscsi_hashmap_put( key_value_pairs, hash_key, key_len, hash_val ); -} - -/** - * @brief Allocates and updates a string value of a key / value hash map pair. - * - * This function allocates memory for a string key - * and its string value.\n - * If the key does not exist, it will be added as - * a new one. - * - * @param[in] key_value_pairs Pointer to the hash map which should - * contain the updated string key and value pair. - * NULL is NOT allowed here, so be careful. - * @param[in] key String containing the key name as string. May - * NOT be NULL, so take caution. - * @param[in] value String containing the value to be stored. - * @return 0 on successful operation, or a negative value on - * error (memory exhaustion). - */ -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 ) + 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 -1; - } - - 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 -1; - } - - memcpy( hash_val, value, val_len ); - - return iscsi_hashmap_put_free( key_value_pairs, hash_key, key_len, hash_val, iscsi_hashmap_key_destroy_value_callback, NULL ); -} - -/** - * @brief Extracts an integer value from a key and value pair. - * - * This function converts a string representation of a - * key and value pair to an integer value. - * - * @param[in] key_value_pairs The hash map containing the key and value pairs to be extracted. - * @param[in] key The key to retrieve the integer value from. - * @param[out] out_value The integer value of the key is stored here - * or 0 in case of an error during string to integer conversion. - * @retval -1 An error occured during value retrieval. - * 'out value' is unchanged. - * @retval 0 The value of the key has been successfully - * stored in the 'out_value'. - */ -static int iscsi_get_int_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, int32_t *out_value) -{ - uint8_t *str_val; - int rc = iscsi_get_key_value_pair( key_value_pairs, key, &str_val ); - - if ( rc == 0 ) - *out_value = (int32_t) atol( (char *) str_val ); - - return rc; -} - -/** - * @brief Allocates and adds an integer value to a key / value hash map pair. - * - * This function allocates memory for a string key - * and its integer representation as string value. - * - * @param[in] key_value_pairs Pointer to the hash map which should - * contain the added integer key and value pair. - * NULL is NOT allowed here, so be careful. - * @param[in] key String containing the key name as string. May - * NOT be NULL, so take caution. - * @param[in] value Integer containing the value to be stored. - * @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 int32_t 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 -1; - } - - return iscsi_add_key_value_pair( key_value_pairs, key, hash_val ); -} - -/** - * @brief Allocates and updates an integer value of a key / value hash map pair. - * - * This function allocates memory for a string key - * and its integer representation as string value.\n - * If the key does not exist, it will be added as - * a new one. - * - * @param[in] key_value_pairs Pointer to the hash map which should - * contain the updated integer key and value pair. - * NULL is NOT allowed here, so be careful. - * @param[in] key String containing the key name as string. May - * NOT be NULL, so take caution. - * @param[in] value Integer containing the value to be stored. - * @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 int32_t 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 -1; - } - - const int rc = iscsi_update_key_value_pair( key_value_pairs, key, hash_val ); - - free( hash_val ); - - return rc; -} - -/** - * @brief Extracts a boolean value from a key and value pair. - * - * This function converts a string representation of a - * key and value pair to a boolean value. - * - * @param[in] key_value_pairs The hash map containing the key and value pairs to be extracted. - * @param[in] key The key to retrieve the boolean value from. - * @param[out] out_value The boolean value of the key is stored here. - * 'Yes' represents true and any other string results in false. - * @retval -1 An error occured during value retrieval. - * 'out value' is unchanged. - * @retval 0 The value of the key has been successfully - * stored in the 'out_value'. - */ -static int iscsi_get_bool_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, int32_t *out_value) -{ - uint8_t *value; - int rc = iscsi_get_key_value_pair( key_value_pairs, key, &value ); - - if ( rc == 0 ) - *out_value = (strcasecmp( (char *) value, "Yes" ) == 0); - - return rc; -} - -/** - * @brief Allocates and adds an boolean value to a key / value hash map pair. - * - * This function allocates memory for a string key - * and its integer value.\n - * The string representation for true is: Yes\n - * The string representation for false is: No - * - * @param[in] key_value_pairs Pointer to the hash map which should - * contain the added boolean key and value pair. - * NULL is NOT allowed here, so be careful. - * @param[in] key String containing the key name as string. May - * NOT be NULL, so take caution. - * @param[in] value Boolean containing the value to be stored - * as string. - * @return 0 on successful operation, or a negative value on - * error (memory exhaustion). - */ -static int iscsi_add_bool_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int value) -{ - const uint8_t *hash_val = (uint8_t *) ((value != 0) ? "Yes" : "No"); - - return iscsi_add_key_value_pair( key_value_pairs, key, hash_val ); -} - -/** - * @brief Allocates and updates an boolean value of a key / value hash map pair. - * - * This function allocates memory for a string key - * and its integer value.\n - * The string representation for true is: Yes\n - * The string representation for false is: No\n - * If the key does not exist, it will be added as - * a new one. - * - * @param[in] key_value_pairs Pointer to the hash map which should - * contain the updated boolean key and value pair. - * NULL is NOT allowed here, so be careful. - * @param[in] key String containing the key name as string. May - * NOT be NULL, so take caution. - * @param[in] value Boolean containing the value to be stored - * as string. - * @return 0 on successful operation, or a negative value on - * error (memory exhaustion). - */ -static int iscsi_update_bool_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int value) -{ - const uint8_t *hash_val = (uint8_t *) ((value != 0) ? "Yes" : "No"); - - return iscsi_update_key_value_pair( key_value_pairs, key, hash_val ); -} - -/** - * @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 - * initialization. - * @param[in] tag Tag to associate with the portal group. - * @param[in] flags Flags to set for the portal group. - * @return Pointer to allocated and initialized portal group - * or NULL in case of memory - */ -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) ); - - if ( portal_group == NULL ) { - logadd( LOG_ERROR, "iscsi_portal_group_create: Out of memory allocating iSCSI portal group structure" ); - - return NULL; - } - - 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" ); - - free( portal_group ); - - return NULL; - } - - portal_group->ref_count = 0; - portal_group->tag = tag; - portal_group->flags = flags; - portal_group->chap_group = 0L; - - return portal_group; -} - -/** - * @brief iSCSI portal group destructor callback for hash map. - * - * Callback function for deallocation of an iSCSI - * 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. - * @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_group_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) -{ - iscsi_portal_group_destroy( (iscsi_portal_group *) value ); - - return 0; -} - -/** - * @brief Deallocates resources acquired by iscsi_portal_group_create. - * - * 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. - */ -void iscsi_portal_group_destroy(iscsi_portal_group *portal_group) -{ - if ( portal_group != NULL ) { - if ( portal_group->portals != NULL ) { - iscsi_hashmap_iterate( portal_group->portals, iscsi_portal_destroy_callback, NULL ); - iscsi_hashmap_destroy( portal_group->portals ); - - portal_group->portals = NULL; - } - - free( portal_group ); - } -} - -/** - * @brief Adds an iSCSI portal to the iSCSI portal group hash map. - * - * This function allocates host:port of iSCSI portal for use - * as key and sets the portal group in the portal. - * - * @param[in] portal_group iSCSI portal group to add portal to. May NOT be NULL, - * so take caution. - * @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 - * @retval 0 The portal has been added successfully to the - * portal group. - */ -int iscsi_portal_group_add_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_add_portal: Out of memory allocating temporarily key buffer for iSCSI portal" ); - - return -1; - } - - 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 -1; - } - - int rc = iscsi_hashmap_put( portal_group->portals, key, key_len, (uint8_t *) portal ); - - if ( rc < 0 ) { - logadd( LOG_ERROR, "iscsi_portal_group_add_portal: Adding portal to hash map containing iSCSI portal group failed" ); - - iscsi_hashmap_key_destroy( key ); - - return rc; - } - - portal->group = portal_group; - - 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 ); -} - -/** - * @brief Allocates and initializes an iSCSI portal structure. - * - * This function makes a copy of the passed host / IP address - * and port, but does NOT initialize the socket. - * - * @param[in] host Host / IP address of the portal. - * @param[in] port Port of the portal. - * @return Pointer to iSCSI portal structure or NULL - * in case of an error (memory exhausted). - */ -iscsi_portal *iscsi_portal_create(const uint8_t *host, const uint8_t *port) -{ - iscsi_portal *portal = (iscsi_portal *) malloc( sizeof(struct iscsi_portal) ); - - if ( portal == NULL ) { - logadd( LOG_ERROR, "iscsi_portal_create: Out of memory allocating iSCSI portal structure" ); - - return NULL; - } - - portal->group = NULL; - - const uint host_len = (uint) (strlen( (char *) host ) + 1U); - - portal->host = (uint8_t *) malloc( host_len ); - - if ( portal->host == NULL ) { - logadd( LOG_ERROR, "iscsi_portal_create: Out of memory allocating iSCSI portal host name" ); - - return NULL; - } - - memcpy( portal->host, host, host_len ); - - const uint port_len = (uint) (strlen( (char *) port ) + 1U); - - portal->port = (uint8_t *) malloc( port_len ); - - if ( portal->port == NULL ) { - logadd( LOG_ERROR, "iscsi_portal_create: Out of memory allocating iSCSI portal port" ); - - return NULL; - } - - memcpy( portal->port, port, port_len ); - - 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 - * and port but does NOT remove it from the portal group hash map. - * - * @param[in] portal Pointer to iSCSI portal to be deallocated, - * which may be NULL in which case the function does nothing. - */ -void iscsi_portal_destroy(iscsi_portal *portal) -{ - if ( portal != NULL ) { - if ( portal->port != NULL ) { - free( portal->port ); - - portal->port = NULL; - } - - if ( portal->host != NULL ) { - free( portal->host ); - - portal->host = NULL; - } - - free( 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->sense_data = 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->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 Called when data requested via an uplink server has arrived. - * - * This function is used to retrieve - * block data which is NOT locally - * available. - * - * @param[in] data Pointer to related scsi_task. May NOT - * be NULL, so be careful. - * @param[in] handle Uplink handle. - * @param[in] start Start of range in bytes. - * @param[in] length Length of range in bytes, as passed to - * uplink_request(). - * @param[in] buffer Data for requested range. - */ -static void iscsi_uplink_callback(void *data, uint64_t handle UNUSED, uint64_t start UNUSED, uint32_t length, const char *buffer) -{ - iscsi_scsi_task *scsi_task = (iscsi_scsi_task *) data; - - memcpy( scsi_task->buf, buffer, length ); - - pthread_mutex_lock( &scsi_task->uplink_mutex ); - pthread_cond_signal( &scsi_task->uplink_cond ); - pthread_mutex_unlock( &scsi_task->uplink_mutex ); -} - -/** - * @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] 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, 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 ); - dnbd3_cache_map_t *cache = ref_get_cachemap( image ); - bool readFromFile; - bool success; - - if ( cache == NULL ) { - readFromFile = true; - } else { - // This is a proxyed image, check if we need to relay the request... - const uint64_t start = (offset_bytes & ~(uint64_t)(DNBD3_BLOCK_SIZE - 1)); - const uint64_t end = ((offset_bytes + num_bytes + DNBD3_BLOCK_SIZE - 1) & ~(uint64_t) (DNBD3_BLOCK_SIZE - 1)); - - readFromFile = image_isRangeCachedUnsafe( cache, start, end ); - ref_put( &cache->reference ); - - if ( !readFromFile ) { - // Not cached, request via uplink - - if ( pthread_mutex_init( &scsi_task->uplink_mutex, NULL ) != 0 ) { - logadd( LOG_ERROR, "iscsi_scsi_emu_io_block_read: Error while initializing DNBD3 uplink mutex for iSCSI SCSI task" ); - - return -ENOMEM; - } - - if ( pthread_cond_init( &scsi_task->uplink_cond, NULL ) != 0 ) { - logadd( LOG_ERROR, "iscsi_scsi_emu_io_block_read: Error while initializing DNBD3 uplink condition for iSCSI SCSI task" ); - - pthread_mutex_destroy( &scsi_task->uplink_mutex ); - - return -ENOMEM; - } - - pthread_mutex_lock( &scsi_task->uplink_mutex ); - success = uplink_request( image, (void *) scsi_task, iscsi_uplink_callback, 0ULL, offset_bytes, (uint32_t) num_bytes ); - - if ( success ) - pthread_cond_wait( &scsi_task->uplink_cond, &scsi_task->uplink_mutex ); - - pthread_mutex_unlock( &scsi_task->uplink_mutex ); - pthread_cond_destroy( &scsi_task->uplink_cond ); - pthread_mutex_destroy( &scsi_task->uplink_mutex ); - } - } - - if ( readFromFile ) { - const int64_t len = pread( image->readFd, scsi_task->buf, (size_t) num_bytes, offset_bytes ); - 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 I/O read" ); - - 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] 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 *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] 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, 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, scsi_task->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 I/O write" ); - - 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, 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, 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, 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 reading 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 - * NOT set a transport ID. Everything else is - * initialized, however. - * - * @param[in] name Pointer to port name. This - * may NOT be NULL, so be careful. - * @param[in] id Identifier for this port. - * @param[in] index Index number for this port. - * @return Pointer to initialized iSCSI port or NULL - * in case of memory exhaustion. - */ -iscsi_port *iscsi_port_create(const uint8_t *name, const uint64_t id, const uint16_t index) -{ - iscsi_port *port = (iscsi_port *) malloc( sizeof(struct iscsi_port) ); - - if ( port == NULL ) { - logadd( LOG_ERROR, "iscsi_port_create: Out of memory allocating iSCSI port" ); - - return NULL; - } - - const uint name_len = (uint) (strlen( (char *) name ) + 1UL); - - port->name = (uint8_t *) malloc( name_len ); - - if ( port->name == NULL ) { - logadd( LOG_ERROR, "iscsi_port_create: Out of memory allocating iSCSI port name" ); - - free( port ); - - return NULL; - } - - memcpy( port->name, name, name_len ); - - port->transport_id = NULL; - port->id = id; - port->index = index; - port->flags = ISCSI_PORT_FLAGS_IN_USE; - port->transport_id_len = 0U; - - return port; -} - -/** - * @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, - * if they exist. - * - * @param[in] port iSCSI port to deallocate. This may - * be NULL in which case nothing happens. - */ -void iscsi_port_destroy(iscsi_port *port) -{ - if ( port != NULL ) { - if ( port->name != NULL ) { - free( port->name ); - - port->name = NULL; - } - - if ( port->transport_id != NULL ) { - free( port->transport_id ); - - port->transport_id = NULL; - } - - free( port ); - } -} - -/** - * @brief Retrieves the name of an iSCSI port. - * - * This function is just a getter. - * - * @param[in] port Pointer to iSCSI port to retrieve - * the name from and may NOT be NULL, so be - * careful. - * @return Pointer to string containing the name - * of the iSCSI port. - */ -uint8_t *iscsi_port_get_name(const iscsi_port *port) -{ - return port->name; -} - -/** - * @brief Sets the SCSI transport ID of the iSCSI port. - * - * This function constructs the SCSI packet data - * for the SCSI transport id by assigning a name - * and the Initiator Session ID (ISID).\n - * Currently, always transport ID format 0x1 will - * be created. - * - * @param[in] Pointer to iSCSI port to assign the - * SCSI transport ID to. May NOT be NULL, so be - * careful. - * @param[in] Pointer to iSCSI name to assign - * along with the ISID as name. - * @param[in] Initiator Session ID (ISID). - * @return 0 if transport ID could be created - * successfully, a negative error code - * otherwise. - */ -int iscsi_port_transport_id_set(iscsi_port *port, const uint8_t *name, const uint64_t isid) -{ - uint8_t *tmp_buf = iscsi_sprintf_alloc( "%s,i,0x%12.12" PRIx64, name, isid ); - - if ( tmp_buf == NULL ) { - logadd( LOG_ERROR, "iscsi_port_transport_id_set: Out of memory allocating SCSI transport ID name for iSCSI port" ); - - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } - - const uint name_len = (uint) (strlen( (char *) tmp_buf ) + 1U); - const uint len = ISCSI_ALIGN(name_len, ISCSI_ALIGN_SIZE); - - 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 ); - - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER; - } - - port->transport_id = (iscsi_transport_id *) malloc( sizeof(struct iscsi_transport_id) + len ); - - if ( port->transport_id == NULL ) { - logadd( LOG_ERROR, "iscsi_port_transport_id_set: Out of memory allocating SCSI transport ID for iSCSI port" ); - - free( tmp_buf ); - - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } - - 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, (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 - * identifier in order to get the port. - * - * @param[in] device Pointer to iSCSI device to be searched. May - * NOT be NULL, so take caution. - * @param[in] id Portal group ID to be searched for. - * @return Pointer to iSCSI port belonging to the iSCSI - * portal group ID or NULL if either the portal - * group ID does not exist or the port is NOT in use. - */ -iscsi_port *iscsi_device_find_port_by_portal_group_tag(const iscsi_device *device, const uint64_t id) -{ - iscsi_port *port; - - if ( iscsi_hashmap_get( device->ports, (uint8_t *) &id, sizeof(id), (uint8_t **) &port ) < 0 ) - return NULL; - - if ( (port == NULL) || ((port->flags & ISCSI_PORT_FLAGS_IN_USE) == 0) ) - return NULL; - - 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; -} - -/** - * @brief Finds an iSCSI target node by case insensitive name search. - * - * Callback function for each element while iterating - * through the 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 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 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; - - if ( strcasecmp( (char *) target->name, (char *) target_find->name ) != 0 ) - return 0; - - target_find->target = target; - - return -1; -} - -/** - * @brief Searches an iSCSI target node by name using case insensitive search. - * - * This function searches for an iSCSI target node - * by iterating through the iSCSI global target node - * hash map. - * - * @param[in] target_name Pointer to string containing the name - * of the iSCSI target node to be searched for. - * @return Pointer to found iSCSI target node or NULL - * in case no iSCSI target node has a matching name. - */ -iscsi_target_node *iscsi_target_node_find(uint8_t *target_name) -{ - if ( target_name == NULL ) - return NULL; - - 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 ); - - 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; -} - -/** - * @brief Retrieves target node redirection address. - * - * This function checks whether the target node needs - * a redirect and is used for informing the client - * about a necessary redirection. - * - * @param[in] conn Pointer to iSCSI connection, may NOT - * be NULL, so be careful. - * @param[in] target Pointer to iSCSI target node where - * NULL is NOT allowed, so take caution. - * @return Pointer to redirect target address or NULL - * if no redirection. - */ -uint8_t *iscsi_target_node_get_redirect(iscsi_connection *conn, iscsi_target_node *target) -{ - // TODO: Implement function - - return NULL; -} - -/** - * @brief Checks if target node is accessible. - * - * This function checks whether access is possible - * to a specific iSCSI IQN and IP address. - * - * @param[in] conn Pointer to iSCSI connection which - * may NOT be NULL, so be careful. - * @param[in] target Pointer to iSCSI target node. NULL - * is not allowed here, to take caution. - * @param[in] iqn Pointer to iSCSI IQN string. This - * is not allowed to be NULL, so be careful. - * @param[in] adr Pointer to IP address, NULL is not - * allowed, so take care. - * @return 0 if access is possible, a negative error - * code otherwise. - */ -int iscsi_target_node_access(iscsi_connection *conn, iscsi_target_node *target, const uint8_t *iqn, const uint8_t *adr) -{ - // TODO: Implement access check function - - return 0; -} - -/** - * @brief Creates and initializes an iSCSI session. - * - * This function creates and initializes all relevant - * data structures of an ISCSI session.\n - * Default key and value pairs are created and - * assigned before they are negotiated at the - * login phase. - * - * @param[in] conn Pointer to iSCSI connection to associate with the session. - * @param[in] target Pointer to iSCSI target node to assign with the session. - * @param[in] type Session type to initialize the session with. - * @return Pointer to initialized iSCSI session or NULL in case an error - * occured (usually due to memory exhaustion). - */ -iscsi_session *iscsi_session_create(iscsi_connection *conn, iscsi_target_node *target, const int type) -{ - iscsi_session *session = (iscsi_session *) malloc( sizeof(struct iscsi_session) ); - - if ( session == NULL ) { - logadd( LOG_ERROR, "iscsi_session_create: Out of memory allocating iSCSI session" ); - - return NULL; - } - - session->tag = conn->pg_tag; - session->flags = 0; - - if ( (iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_INIT_R2T) != 0 ) - session->flags |= ISCSI_SESSION_FLAGS_INIT_R2T; - - if ( (iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_IMMEDIATE_DATA) != 0 ) - session->flags |= ISCSI_SESSION_FLAGS_IMMEDIATE_DATA; - - if ( (iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_DATA_PDU_IN_ORDER) != 0 ) - session->flags |= ISCSI_SESSION_FLAGS_DATA_PDU_IN_ORDER; - - if ( (iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_DATA_SEQ_IN_ORDER) != 0 ) - session->flags |= ISCSI_SESSION_FLAGS_DATA_SEQ_IN_ORDER; - - 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_list_create( &session->conn_list ); - iscsi_list_enqueue( &session->conn_list, &conn->node ); - - 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" ); - - 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, 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 ); - free( session ); - - return NULL; - } - - return session; -} - -/** - * @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; + scsi_task->xfer_pos = 0UL; + } } /** - * @brief Deallocates all resources acquired by iscsi_session_create. + * @brief Converts an internal representation of a LUN identifier to an iSCSI LUN required for packet data. * - * This function also frees the associated key and value pairs, - * the attached connections as well as frees the initiator - * port. + * This function needs to be called prior + * storing the internal SCSI identifier + * representation in the iSCSI packet. * - * @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 = 0ULL; - session->target = NULL; - session->type = ISCSI_SESSION_TYPE_INVALID; - - if ( session->key_value_pairs != NULL ) { - iscsi_hashmap_iterate( session->key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL ); - iscsi_hashmap_destroy( session->key_value_pairs ); - - session->key_value_pairs = NULL; - } - - iscsi_connection *conn; - iscsi_connection *tmp; - - iscsi_list_foreach_safe_node ( &session->conn_list, conn, tmp ) { - iscsi_list_remove( &conn->node ); - iscsi_connection_destroy( conn ); - } - - if ( session->init_port != NULL ) { - iscsi_port_destroy( session->init_port ); - - session->init_port = NULL; - } - - free( session ); // TODO: Check if potential reusage of session makes sense. - } -} - -/** - * @brief Initializes a key and value pair hash table with default values. - * - * This function is used by iSCSI connections and - * sessions with default values for required keys.\n - * The iSCSI global key and value pair allowed - * values and ranges for fast - * access - * - * @param[in] key_value_pairs Pointer to key and value pair hash map - * which should store all the default values for - * its keys and 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. + * @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. */ -static int iscsi_init_key_value_pairs(iscsi_hashmap *key_value_pairs, const iscsi_key_value_pair_lut_entry *lut) +static uint64_t iscsi_scsi_lun_get_from_scsi(const int lun_id) { - 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; - } + uint64_t iscsi_scsi_lun; - return 0; -} + 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; -/** - * @brief Initializes a key and value pair hash table with default values for an iSCSI session. - * - * This function only initializes the default key - * and value pairs used by iSCSI sessions. - * - * @param[in] key_value_pairs Pointer to key and value pair hash map - * which should store all the default values for - * its keys and may NOT be NULL, so take caution. - * @return 0 on success, a negative error code otherwise. - */ -int iscsi_session_init_key_value_pairs(iscsi_hashmap *key_value_pairs) -{ - return iscsi_init_key_value_pairs( key_value_pairs, &iscsi_session_key_value_pair_lut[0] ); + return iscsi_scsi_lun; } /** - * @brief Creates data structure for an iSCSI connection from iSCSI portal and TCP/IP socket. + * @brief Converts an iSCSI LUN from packet data to internal SCSI LUN identifier. * - * Creates a data structure for incoming iSCSI connection - * requests from iSCSI packet data. + * This function needs to be called prior + * storing the iSCSI packet data + * representation in the structures + * requiring an internal SCSI identifier. * - * @param[in] portal Pointer to iSCSI portal to associate the - * connection with. - * @param[in] sock TCP/IP socket to associate the connection with. - * @return Pointer to initialized iSCSI connection structure or NULL in - * case of an error (invalid iSCSI packet data or memory exhaustion). + * @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. */ -iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock) +static int iscsi_scsi_lun_get_from_iscsi(const uint64_t lun) { - iscsi_connection *conn = (iscsi_connection *) malloc( sizeof(struct iscsi_connection) ); - - if ( conn == NULL ) { - logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI connection" ); - - return NULL; - } - - conn->session = NULL; - 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 login text key / value pair hash map" ); - - free( conn ); - - return NULL; - } - - const int rc = iscsi_connection_init_key_value_pairs( conn->key_value_pairs ); - - if ( rc < 0 ) { - 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->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; - conn->init_adr = NULL; - conn->target = NULL; - conn->target_port = NULL; - conn->target_name_short = NULL; - conn->portal_host = NULL; - conn->portal_port = NULL; - conn->pdu_processing = NULL; - - iscsi_list_create( &conn->scsi_data_in_queued_tasks ); - - conn->login_response_pdu = NULL; - - 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 ); + int lun_id = (int) (lun >> 62ULL) & 0x03; - return NULL; - } + 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 conn; + return lun_id; } /** - * @brief Deallocates all pending iSCSI tasks and PDUs associated with an iSCSI connection. + * @brief Runs an iSCSI SCSI task for a specified iSCSI SCSI LUN. * - * This function only removes tasks which are - * not enqueued. + * 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] conn Pointer to iSCSI connection of which to - * deallocate all the tasks and PDUs. 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. + * @param pdu */ -static int iscsi_connection_tasks_destroy(iscsi_connection *conn) +static void iscsi_scsi_lun_task_run( iscsi_scsi_task *scsi_task, iscsi_pdu *pdu) { - iscsi_pdu *pdu; - iscsi_pdu *tmp_pdu; + int rc; - iscsi_list_foreach_safe_node ( &conn->pdus_snack, pdu, tmp_pdu ) { - iscsi_list_remove( &pdu->node ); - iscsi_connection_pdu_destroy( pdu ); - } + scsi_task->status = ISCSI_SCSI_STATUS_GOOD; - iscsi_task *task; - iscsi_task *tmp_task; + rc = iscsi_scsi_emu_block_process( scsi_task ); - iscsi_list_foreach_safe_node ( &conn->scsi_data_in_queued_tasks, task, tmp_task ) { - if ( (task->flags & ISCSI_TASK_FLAGS_QUEUED) != 0 ) - continue; + if ( rc == ISCSI_SCSI_TASK_RUN_UNKNOWN ) { + rc = iscsi_scsi_emu_primary_process( scsi_task ); - iscsi_list_remove( &task->node ); - iscsi_task_destroy( 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 ); + // TODO: Free task + rc = ISCSI_SCSI_TASK_RUN_COMPLETE; + } } - iscsi_list_foreach_safe_node ( &conn->pdus_write, pdu, tmp_pdu ) { - iscsi_list_remove( &pdu->node ); - iscsi_connection_pdu_destroy( pdu ); + if ( rc == ISCSI_SCSI_TASK_RUN_COMPLETE ) { + iscsi_scsi_task_xfer_complete( scsi_task, pdu ); } - - return ((conn->task_cnt != 0) ? -1 : 0); -} - -/** - * @brief iSCSI connection destructor callback for hash map. - * - * Callback function for deallocation of an iSCSI - * connection stored in the hash map managing all - * iSCSI connections. - * - * @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_connection_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) -{ - iscsi_connection_destroy( (iscsi_connection *) value ); - iscsi_hashmap_key_destroy( key ); - - return 0; } /** - * @brief Deallocates all resources acquired by iscsi_connection_create. + * @brief Retrieves the size of a physical block in bytes for a DNBD3 image. * - * Deallocates a data structure of an iSCSI connection - * request and all allocated hash maps which don't - * require closing of external resources like closing - * TCP/IP socket connections. + * This function depends on DNBD3 image + * properties. * - * @param[in] conn Pointer to iSCSI connection structure to be - * deallocated, TCP/IP connections are NOT closed by this - * function, use iscsi_connection_close for this. This may be - * NULL in which case this function does nothing. + * @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. */ -void iscsi_connection_destroy(iscsi_connection *conn) +static inline uint32_t iscsi_scsi_emu_physical_block_get_size(const dnbd3_image_t *image) { - 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 ); - - conn->portal_port = NULL; - } - - if ( conn->portal_host != NULL ) { - free( conn->portal_host ); - - conn->portal_host = NULL; - } - - if ( conn->target_name_short != NULL ) { - free( conn->target_name_short ); - - conn->target_name_short = NULL; - } - - if ( conn->init_adr != NULL ) { - free( conn->init_adr ); - - conn->init_adr = NULL; - } - - if ( conn->init_name != NULL ) { - 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 ); - - 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 ); - - conn->key_value_pairs = NULL; - } - - free( conn ); - } + return ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE; } /** - * @brief Drops all connections based on matching pattern. + * @brief Retrieves the number of total logical blocks for a DNBD3 image. * - * @param[in] conn Pointer to iSCSI connection which may - * NOT be NULL, so be careful. - * @param[in] conn_match Pointer to match string, NULL is - * not allowd here, so take caution. - * @param[in] all Non-zero number indicating removing all - * connections. - * @return 0 on success, a negative error code otherwise. + * 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. */ -int iscsi_connection_drop(iscsi_connection *conn, const uint8_t *conn_match, const int all) +static inline uint64_t iscsi_scsi_emu_block_get_count(const dnbd3_image_t *image) { - // TODO: Implement function. - - return 0; + return (image->virtualFilesize / ISCSI_SCSI_EMU_BLOCK_SIZE); } /** - * @brief Schedules an iSCSI connection. + * @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[in] conn Pointer to ISCSI connection to be - * scheduled. May NOT be NULL, so be careful. + * @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. */ -void iscsi_connection_schedule(iscsi_connection *conn) +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) { - // TODO: Implement function. + *offset_blocks = (offset_bytes / ISCSI_SCSI_EMU_BLOCK_SIZE); + *num_blocks = (num_bytes / ISCSI_SCSI_EMU_BLOCK_SIZE); + + return ((offset_bytes % ISCSI_SCSI_EMU_BLOCK_SIZE) | (num_bytes % ISCSI_SCSI_EMU_BLOCK_SIZE)); } /** - * @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. + * @brief Converts offset and length specified by a block size to offset and length in bytes. * - * Returns ISCSI_CONNECT_PDU_READ_ERR_FATAL if the operation - * indicates a fatal error with the TCP connection (including - * if the TCP connection was closed unexpectedly). + * This function uses bit shifting if + * the block size is a power of two. * - * Otherwise returns the number of bytes successfully read. + * @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. + * @return Number of blocks in bytes. */ -int32_t iscsi_connection_read(const iscsi_connection *conn, uint8_t *buf, const uint32_t len) +static uint64_t iscsi_scsi_emu_blocks_to_bytes(uint64_t *offset_bytes, const uint64_t offset_blocks, const uint64_t num_blocks) { - if ( len == 0UL ) - return 0L; - - const int32_t rc = (int32_t) recv( conn->sock, buf, (size_t) len, MSG_WAITALL ); + *offset_bytes = (offset_blocks * ISCSI_SCSI_EMU_BLOCK_SIZE); - return ((rc > 0L) ? rc : (int32_t) ISCSI_CONNECT_PDU_READ_ERR_FATAL); + return (num_blocks * ISCSI_SCSI_EMU_BLOCK_SIZE); } /** - * @brief Writes data for the specified iSCSI connection to its TCP socket. - * - * The TCP socket is marked as non-blocking, so this function may not read - * all data requested. + * @brief Called when data requested via an uplink server has arrived. * - * Returns ISCSI_CONNECT_PDU_READ_ERR_FATAL if the operation - * indicates a fatal error with the TCP connection (including - * if the TCP connection was closed unexpectedly). + * This function is used to retrieve + * block data which is NOT locally + * available. * - * Otherwise returns the number of bytes successfully written. + * @param[in] data Pointer to related scsi_task. May NOT + * be NULL, so be careful. + * @param[in] handle Pointer to destination buffer, as passed to + * iscsi_scsi_emu_io_block_read(). + * @param[in] start Start of range in bytes. + * @param[in] length Length of range in bytes, as passed to + * uplink_request(). + * @param[in] buffer Data for requested range. */ -int32_t iscsi_connection_write(const iscsi_connection *conn, uint8_t *buf, const uint32_t len) +static void iscsi_uplink_callback(void *data, uint64_t handle UNUSED, uint64_t start UNUSED, uint32_t length, const char *buffer) { - if ( len == 0UL ) - return 0L; + iscsi_scsi_task *scsi_task = (iscsi_scsi_task *) data; - const int32_t rc = (int32_t) sock_sendAll( conn->sock, buf, (size_t) len, ISCSI_CONNECT_SOCKET_WRITE_RETRIES ); + memcpy( scsi_task->buf, buffer, length ); - return ((rc > 0L) ? rc : (int32_t) ISCSI_CONNECT_PDU_READ_ERR_FATAL); + pthread_mutex_lock( &scsi_task->uplink_mutex ); + pthread_cond_signal( &scsi_task->uplink_cond ); + pthread_mutex_unlock( &scsi_task->uplink_mutex ); } /** - * @brief This function handles all queued iSCSI SCSI Data In tasks. + * @brief Reads a number of blocks from a block offset of a DNBD3 image to a specified buffer. * - * This function also creates a sub task - * if the data transfer length exceeds - * the maximum allowed chunk size. + * This function enqueues the I/O read + * process which invokes a callback + * function when the read operation has + * been finished. * - * @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. + * @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] 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. + * @return 0 on successful operation, a negative + * error code otherwise. */ -int iscsi_connection_handle_scsi_data_in_queued_tasks(iscsi_connection *conn) +static int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, dnbd3_image_t *image, const uint64_t offset_blocks, const uint64_t num_blocks) { - 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; + uint64_t offset_bytes; + const uint64_t num_bytes = iscsi_scsi_emu_blocks_to_bytes( &offset_bytes, offset_blocks, num_blocks ); - sub_task->scsi_task.buf = NULL; - sub_task->scsi_task.pos = task->pos; + dnbd3_cache_map_t *cache = ref_get_cachemap( image ); + bool readFromFile; - pthread_rwlock_rdlock( &conn->device->luns_rwlock ); + if ( cache == NULL ) { + readFromFile = true; + } else { + // This is a proxyed image, check if we need to relay the request... + const uint64_t start = (offset_bytes & ~(uint64_t)(DNBD3_BLOCK_SIZE - 1)); + const uint64_t end = ((offset_bytes + num_bytes + DNBD3_BLOCK_SIZE - 1) & ~(uint64_t) (DNBD3_BLOCK_SIZE - 1)); - if ( iscsi_device_find_lun( conn->device, task->lun_id ) == NULL ) { - pthread_rwlock_unlock( &conn->device->luns_rwlock ); + readFromFile = image_isRangeCachedUnsafe( cache, start, end ); + ref_put( &cache->reference ); - iscsi_list_remove( &task->node ); + if ( !readFromFile ) { + // Not cached, request via uplink + pthread_mutex_init( &scsi_task->uplink_mutex, NULL ); + pthread_cond_init( &scsi_task->uplink_cond, NULL ); + pthread_mutex_lock( &scsi_task->uplink_mutex ); - task->pos += len; - sub_task->scsi_task.len = 0UL; - sub_task->scsi_task.xfer_len = len; + if ( !uplink_request( image, scsi_task, iscsi_uplink_callback, 0, offset_bytes, num_bytes ) ) { + pthread_mutex_unlock( &scsi_task->uplink_mutex ); - iscsi_scsi_task_lun_process_none( &sub_task->scsi_task ); - iscsi_scsi_task_xfer_complete( &sub_task->scsi_task ); + logadd( LOG_DEBUG1, "Could not relay uncached request to upstream proxy for image %s:%d", + image->name, image->rid ); - return ISCSI_CONNECT_PDU_READ_OK; + return -EIO; } - 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 ); + // Wait sync (Maybe use pthread_cond_timedwait to detect unavailable uplink instead of hanging...) + pthread_cond_wait( &scsi_task->uplink_cond, &scsi_task->uplink_mutex ); + pthread_mutex_unlock( &scsi_task->uplink_mutex ); + pthread_cond_destroy( &scsi_task->uplink_cond ); + pthread_mutex_destroy( &scsi_task->uplink_mutex ); } + } + + bool success; - if ( task->pos == task->scsi_task.xfer_len ) - iscsi_list_remove( &task->node ); + if ( readFromFile ) { + const int64_t len = pread( image->readFd, scsi_task->buf, (size_t) num_bytes, offset_bytes ); + success = ((uint64_t) len == num_bytes); + } else { + success = true; } - return ISCSI_CONNECT_PDU_READ_OK; -} + 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 ); -/** - * @brief Initializes a key and value pair hash table with default values for an iSCSI connection. - * - * This function only initializes the default key - * and value pairs used by iSCSI connectionss. - * - * @param[in] key_value_pairs Pointer to key and value pair hash map - * which should store all the default values for - * its keys and may NOT be NULL, so take caution. - * @return 0 on success, a negative error code otherwise. - */ -int iscsi_connection_init_key_value_pairs(iscsi_hashmap *key_value_pairs) -{ - return iscsi_init_key_value_pairs( key_value_pairs, &iscsi_connection_key_value_pair_lut[0] ); + return (success ? 0 : -1); } /** - * @brief Appends a special key and value pair to DataSegment packet data. + * @brief Executes a read or write operation on a DNBD3 image. * - * This function adds MaxRecvDataSegmentLength, - * FirstBurstLength and MaxBurstLength, which - * require special handling to an output - * DataSegment buffer and truncates if - * necessary. + * This function also sets the SCSI + * status result code accordingly. * - * @param[in] conn Pointer to iSCSI connection to handle the - * 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. - * @param[in] key Pointer to special key to be written to - * output buffer. NULL is NOT allowed, - * take caution. - * @param[in] buf Pointer to output buffer to write the - * special key and value pair to. NULL is - * prohibited, so be careful. - * @param[in] pos Position of buffer in bytes to start - * writing to. - * @param[in] len Total length of buffer in bytes. - * @return New buffer position in bytes or a negative - * error code. + * @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 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) +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) { - if ( key_value_pair == NULL ) - return pos; + scsi_task->xfer_pos = 0UL; - if ( (key_value_pair->flags & ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_OVERRIDE_DEFAULT) != 0 ) { - if ( pos >= len ) - return -1L; + if ( (flags & ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE) != 0 ) { + 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 ); - pos += (uint32_t) (snprintf( (char *) (buf + pos), (len - pos), "%s=%" PRId32, key, (uint32_t) ISCSI_DEFAULT_MAX_RECV_DS_LEN ) + 1); + return ISCSI_SCSI_TASK_RUN_COMPLETE; } - if ( (key_value_pair->flags & ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_USE_OTHER_MAX_VALUE) != 0 ) { - if ( pos >= len ) - return -1L; + 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; + } - uint8_t *first_burst_len_val = NULL; - 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 )); + const uint64_t imgBlockCount = iscsi_scsi_emu_block_get_count( image ); - uint8_t *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 ( (imgBlockCount <= lba) || ((imgBlockCount - 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 ); - if ( first_burst_len > max_burst_len ) { - first_burst_len = max_burst_len; + return ISCSI_SCSI_TASK_RUN_COMPLETE; + } - if ( first_burst_len_val != NULL ) { - sprintf( (char *) first_burst_len_val, "%" PRId32, first_burst_len ); - } - } + if ( xfer_len == 0UL ) { + scsi_task->status = ISCSI_SCSI_STATUS_GOOD; - pos += (uint32_t) (snprintf( (char *) (buf + pos), (len - pos), "%s=%" PRId32, key, first_burst_len ) + 1); + return ISCSI_SCSI_TASK_RUN_COMPLETE; } - return pos; -} + const uint32_t max_xfer_len = ISCSI_MAX_DS_SIZE / ISCSI_SCSI_EMU_BLOCK_SIZE; -/** - * @brief Appends a key and value pair to DataSegment packet data. - * - * This function adds any non-declarative key - * and value pair to an output DataSegment - * buffer and truncates if necessary. - * - * @param[in] key_value_pair Pointer to key and value pair containing - * 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 - * be written to output buffer which may - * NOT be NULL, so take caution. - * @param[in] buf Pointer to output buffer to write the - * key and value pair to. NULL is - * prohibited, so be careful. - * @param[in] pos Position of buffer in bytes to start - * writing to. - * @param[in] len Total length of buffer in bytes. - * @return New buffer position in bytes or a negative - * error code. - */ -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 == 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; + 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; + } + + 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 ) != 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; + + scsi_task->buf = (uint8_t *) malloc( scsi_task->len ); + + if ( scsi_task->buf == NULL ) { + iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_HARDWARE_ERR, + ISCSI_SCSI_ASC_INTERNAL_TARGET_FAIL, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE ); + + return ISCSI_SCSI_TASK_RUN_COMPLETE; + } + + rc = iscsi_scsi_emu_io_blocks_read( scsi_task, image, offset_blocks, num_blocks ); + + if ( rc < 0 ) { + if ( rc == -ENOMEM ) { + iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_HARDWARE_ERR, + ISCSI_SCSI_ASC_INTERNAL_TARGET_FAIL, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE ); + + return ISCSI_SCSI_TASK_RUN_COMPLETE; + } + + 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 ); - pos += (uint32_t) (snprintf( (char *) (buf + pos), (len - pos), "%s=%s", key, value ) + 1); + return ISCSI_SCSI_TASK_RUN_COMPLETE; } - return pos; + scsi_task->xfer_pos = scsi_task->len; + + return ISCSI_SCSI_TASK_RUN_COMPLETE; } /** - * @brief Negotiates key and value pair of list type. + * @brief Executes SCSI block emulation on a DNBD3 image. * - * This function checks if a key of list type has a - * valid value according to the iSCSI specification. + * This function determines the block + * based SCSI opcode and executes it. * - * @param[in] key_value_pair Pointer to key and value pair. May NOT - * be NULL, so be careful. - * @param[in] old_value Pointer to string containing the old - * value. NULL is not allowed, so take caution. - * @return Pointer to original value, if the value is - * allowed or NULL otherwise. + * @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 uint8_t *iscsi_negotiate_key_value_pair_list(const iscsi_key_value_pair *key_value_pair, const uint8_t *old_value) +static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) { - 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 )); + uint64_t lba; + uint32_t xfer_len; + dnbd3_image_t *image = scsi_task->connection->client->image; + + 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; + + 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( image, scsi_task, lba, xfer_len, 0 ); + } + 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; + + 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( image, scsi_task, lba, xfer_len, 0 ); + } + 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; + + 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( image, scsi_task, lba, xfer_len, 0 ); + } + 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; + + 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( image, scsi_task, lba, xfer_len, 0 ); + } + case ISCSI_SCSI_OPCODE_READCAPACITY10 : { + 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( 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 ); - for ( ;; ) { - const size_t len = strlen( (char *) list ); + iscsi_put_be32( (uint8_t *) &buf->block_len, ISCSI_SCSI_EMU_BLOCK_SIZE ); - if ( (val_len == len) && (strncasecmp( (char *) list, (char *) old_value, len ) == 0) ) - return list; + uint len = scsi_task->len; - list += (len + 1); + 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 - if ( list[0] == '\0' ) { - if ( value == NULL ) - break; + scsi_task->buf = (uint8_t *) buf; + scsi_task->xfer_pos = len; + scsi_task->status = ISCSI_SCSI_STATUS_GOOD; - 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 )); + 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; - return NULL; -} + if ( ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_GET_ACTION(cdb_servce_in_action_16->action) + != ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_ACTION_READ_CAPACITY_16 ) { + return ISCSI_SCSI_TASK_RUN_UNKNOWN; + } + 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) ); -/** - * @brief Negotiates key and value pair of numeric type. - * - * This function checks if a key of numeric type has - * a valid interger value and clamps within the - * allowed minimum and maximum range according to - * the iSCSI specification. - * - * @param[in] key_value_pair Pointer to key and value pair. May NOT - * be NULL, so be careful. - * @param[in] old_value Pointer to string containing the old - * value. NULL is not allowed, take caution. - * @param[in] value Pointer to string containing the current - * value which may NOT be NULL, so be careful. - * @return Pointer to original value, if the value is - * allowed or NULL otherwise. - */ -static uint8_t *iscsi_negotiate_key_value_pair_num(const iscsi_key_value_pair *key_value_pair, uint8_t *old_value, uint8_t *value) -{ - int32_t old_int_val = (int32_t) atol( (char *) key_value_pair->value ); + 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 ); - if ( (key_value_pair->flags & ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_USE_PREVIOUS_VALUE) != 0 ) - old_int_val = (int32_t) atol( (char *) old_value ); + return ISCSI_SCSI_TASK_RUN_COMPLETE; + } - int32_t int_val = (int32_t) atol( (char *) value ); + lba = iscsi_scsi_emu_block_get_count( image ) - 1ULL; - 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) ); + iscsi_put_be64( (uint8_t *) &buf->lba, lba ); + iscsi_put_be32( (uint8_t *) &buf->block_len, ISCSI_SCSI_EMU_BLOCK_SIZE ); - if ( (old_int_val < range_min) || (old_int_val > range_max) ) - return NULL; + buf->flags = 0; - switch ( key_value_pair->type ) { - case ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN : { - if ( old_int_val > int_val ) - old_int_val = int_val; + const uint8_t exponent = ISCSI_SCSI_EMU_BLOCK_DIFF_SHIFT; - break; - } - case ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MAX : { - if ( old_int_val < int_val ) - old_int_val = int_val; + 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); + + 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 old_value; + return ISCSI_SCSI_TASK_RUN_UNKNOWN; break; } } - sprintf( (char *) old_value, "%" PRId32, old_int_val ); - - return old_value; + return ISCSI_SCSI_TASK_RUN_COMPLETE; } /** - * @brief Negotiates key and value pair of boolean type. + * @brief Checks whether provided SCSI CDB allocation length is large enough. * - * This function checks if a key of boolean type - * has a valid value according to the iSCSI - * specification and also applies the boolean - * OR / AND function to the current value. + * This function also sets the SCSI + * status result code if the allocation + * size is insufficent. * - * @param[in] key_value_pair Pointer to key and value pair. May NOT - * be NULL, so be careful. - * @param[in] old_value Pointer to string containing the old - * value. NULL is not allowed, so take caution. - * @param[in] value Pointer to string containing the - * current value which may NOT be NULL, so be - * careful. - * @param[in] bool_value Pointer to string containing the - * boolean OR / AND value where NULL is - * prohibited, so take caution. - * @param[out] update_key_value_pair Pointer to integer which - * marks if the key and value pair should be - * updated. May NOT be NULL, so be careful. - * @return Pointer to either boolean AND / OR result, - * default value or NULL in case of invalid - * boolean value. + * @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 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) +static int iscsi_scsi_emu_check_len(iscsi_scsi_task *scsi_task, const uint len, const uint min_len) { - 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 ) + 1UL; - - if ( (strcasecmp( (char *) old_value, (char *) list_bool_true ) != 0) && (strcasecmp( (char *) old_value, (char *) list_bool_false ) != 0) ) { - *update_key_value_pair = 0; + if ( len >= min_len ) + return 0; - return (uint8_t *) "Reject"; - } + 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 ((strcasecmp( (char *) value, (char *) bool_value ) == 0) ? bool_value : old_value); + return -1; } /** - * @brief Negotiates key and value pair of all types. - * - * This function determines the key type and - * calls the suitable negotation handler - * for checking iSCSI standard compliance. + * @brief Calculates the 64-bit IEEE Extended NAA for a name. * - * @param[in] key_value_pair Pointer to key and value pair. May NOT - * be NULL, so be careful. - * @param[in] old_value Pointer to string containing the old - * value. NULL is not allowed, so take caution. - * @param[in] value Pointer to string containing the - * current value which may NOT be NULL, so be - * careful. - * @param[out] update_key_value_pair Pointer to integer which - * marks if the key and value pair should be - * updated. NULL is not allowed, take caution. - * @return Pointer to new negotiated value or NULL - * in case of an invalid negation status. + * @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 uint8_t *iscsi_negotiate_key_value_pair_all(const iscsi_key_value_pair *key_value_pair, uint8_t *old_value, uint8_t *value, int *update_key_value_pair) +static inline void iscsi_scsi_emu_naa_ieee_ext_set(uint64_t *buf, const uint8_t *name) { - switch ( key_value_pair->type ) { - case ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST : { - return iscsi_negotiate_key_value_pair_list( key_value_pair, old_value ); - - break; - } - case ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN : - case ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MAX : - case ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_DECLARATIVE : { - return iscsi_negotiate_key_value_pair_num( key_value_pair, old_value, value ); - - break; - } - case ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_AND : { - return iscsi_negotiate_key_value_pair_bool( key_value_pair, old_value, value, key_value_pair->list_range, update_key_value_pair ); - - break; - } - 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 ) + 1UL; - - return iscsi_negotiate_key_value_pair_bool( key_value_pair, old_value, value, list_bool_false, update_key_value_pair ); - - break; - } - default : { - break; - } - } + const uint64_t wwn = iscsi_target_node_wwn_get( name ); - return key_value_pair->value; + iscsi_put_be64( (uint8_t *) buf, wwn ); } /** - * @brief Negotiates either iSCSI session or connection state. - * - * This function checks and sets the state mask - * of either an iSCSI connection or session - * key and value pair. + * @brief Copies a SCSI name string and zero pads until total string length is aligned to DWORD boundary. * - * @param[in] conn Pointer to iSCSI connection of which to - * determine the key type. NULL is NOT allowed, - * so be careful. - * @param[in] key_value_pair Pointer to key and value pair - * containing the key and value pair attributes. - * NULL is NOT allowed, so be careful. - * @param[in] type Type of key and value pair to negotiate. - * 0 is iSCSI connection key and value pair, - * any other value indicates an iSCSI session - * key and value pair. - * @return 0 on successful state negotation, a - * negative error code otherwise. + * @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 int iscsi_negotiate_key_value_pairs_state(iscsi_connection *conn, const iscsi_key_value_pair *key_value_pair, const int type) +static size_t iscsi_scsi_emu_pad_scsi_name(uint8_t *buf, const uint8_t *name) { - if ( type != 0 ) { - const uint32_t state_mask = (uint32_t) key_value_pair->state_mask; - - if ( ((conn->session_state_negotiated & state_mask) != 0) && ((key_value_pair->flags & ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_TARGET_DECLARATIVE) == 0) ) - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER_XCHG_NOT_ONCE; - - conn->session_state_negotiated |= state_mask; - } else { - const uint16_t state_mask = (uint16_t) key_value_pair->state_mask; + size_t len = strlen( (char *) name ); - if ( ((conn->state_negotiated & state_mask) != 0) && ((key_value_pair->flags & ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_MULTI_NEGOTIATION) == 0) ) - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER_XCHG_NOT_ONCE; + memcpy( buf, name, len ); - conn->state_negotiated |= state_mask; - } + do { + buf[len++] = '\0'; + } while ( (len & (ISCSI_ALIGN_SIZE - 1)) != 0 ); - return ISCSI_CONNECT_PDU_READ_OK; + return len; } /** - * @brief Callback which negotiates a single key and value pairs required for session authentication. - * - * This function is called for each key and value - * pair which needs connection or session - * authentication. - * - * @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 Pointer to integer value which is - * 1 is this is discovery, or 0 if not. - * @return Always returns 0 as this function cannot fail. + * @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. */ -int iscsi_negotiate_key_value_pair_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) +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) { - iscsi_key_value_pair_packet *key_value_pair_packet = (iscsi_key_value_pair_packet *) user_data; - 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 = 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 = 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 0; - - int update_key_value_pair = 1; - uint8_t *conn_sess_val; - - if ( rc < 0 ) { - conn_sess_val = (uint8_t *) "NotUnderstood"; - - 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 = 0; - } else { - rc = iscsi_negotiate_key_value_pairs_state( conn, key_value_pair, type ); - - if ( rc < 0 ) - return rc; - - rc = iscsi_hashmap_get( key_value_pairs, key, key_size, &conn_sess_val ); - } - - 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; - uint32_t first_burst_len = (uint32_t) atol( (char *) value ); - uint32_t max_burst_len; - - 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_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, "%" PRId32, first_burst_len ); - } - - if ( (key_value_pair->flags & ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_TARGET_DECLARATIVE) != 0 ) - 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 ); - } - - if ( conn_sess_val != NULL ) { - if ( update_key_value_pair != 0 ) - iscsi_update_key_value_pair( key_value_pairs, key, conn_sess_val ); - - 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 ( (int32_t) key_value_pair_packet->pos < 0L ) - return key_value_pair_packet->pos; + if ( len < sizeof(struct iscsi_scsi_std_inquiry_data_packet) ) { + scsi_task->xfer_pos = 0UL; - 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 ); + 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 ); - if ( (int32_t) key_value_pair_packet->pos < 0L ) - return key_value_pair_packet->pos; + return -1; } - return 0; -} + const int evpd = (cdb_inquiry->lun_flags & ISCSI_SCSI_CDB_INQUIRY_FLAGS_EVPD); + const uint pc = cdb_inquiry->page_code; -/** - * @brief Negotiates all key and value pairs required for session authentication. - * - * @param[in] conn Pointer to iSCSI connection to be - * negotiated, may NOT be NULL, so be careful. - * @param[in] key_value_pairs Pointer to key and value pair hash map - * which contains the negotiation pairs. NULL - * is prohibited, so take caution. - * @param[in] buf Pointer to output buffer which may NOT - * be NULL, so be careful. - * @param[in] pos Number of bytes already written. - * @param[in] len Length of DataSegment in bytes. - * @return New buffer length in bytes if all keys - * could be negotiated, a negative error - * code otherwise. - */ -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 - 1UL] = '\0'; + 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 len; + return -1; } - uint8_t *type; - int rc = iscsi_get_key_value_pair( key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_SESSION_TYPE, &type ); + 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; + uint alloc_len; + const uint8_t pti = ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PUT_PERIPHERAL_TYPE(ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_DIRECT) | ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PUT_PERIPHERAL_ID(ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_ID_POSSIBLE); - if ( rc < 0 ) - rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_SESSION_TYPE, &type ); + vpd_page_inquiry_data_pkt->peripheral_type_id = pti; + vpd_page_inquiry_data_pkt->page_code = (uint8_t) pc; - const int discovery = (((rc == 0) && (strcasecmp( (char *) type, "Discovery" ) == 0)) ? 1 : 0); + 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_EXTENDED_INQUIRY_DATA; + vpd_page_inquiry_data_pkt->params[4] = ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_BLOCK_LIMITS; + vpd_page_inquiry_data_pkt->params[5] = ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_BLOCK_DEV_CHARS; - 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 ); + alloc_len = 6U; - return key_value_pair_packet.pos; -} + iscsi_put_be16( (uint8_t *) &vpd_page_inquiry_data_pkt->alloc_len, (uint16_t) alloc_len ); -/** - * @brief Copies retrieved key and value pairs into SCSI connection and session structures. - * - * This function converts string representations of - * integer and boolean key and value pairs. - * - * @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. - * @retval 0 All key and value pairs were copied successfully. - */ -int iscsi_connection_copy_key_value_pairs(iscsi_connection *conn) -{ - int32_t int_val; + break; + } + case ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_UNIT_SERIAL_NUMBER : { + const char *name = image->name; - int rc = iscsi_get_int_key_value_pair( conn->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_RECV_DS_LEN, &int_val); + alloc_len = (uint) strlen( name ); - if ( rc != 0 ) - return rc; + 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); - if ( (int_val <= 0L) || (int_val > (int32_t) ISCSI_DEFAULT_MAX_RECV_DS_LEN) ) - int_val = ISCSI_DEFAULT_MAX_RECV_DS_LEN; + 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)) ); - conn->max_recv_ds_len = int_val; + alloc_len++; - uint8_t *value; + iscsi_put_be16( (uint8_t *) &vpd_page_inquiry_data_pkt->alloc_len, (uint16_t) alloc_len ); - rc = iscsi_get_key_value_pair( conn->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_HEADER_DIGEST, &value); + break; + } + case ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_DEVICE_ID : { + const char *port_name = "Horst"; + const uint dev_name_len = (uint) (strlen( image->name ) + 1U); + const uint port_name_len = (uint) (strlen( port_name ) + 1U); - if ( rc != 0 ) - return rc; + 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 - conn->header_digest = ((strcasecmp( (char *) value, "CRC32C" ) == 0) ? ISCSI_DIGEST_SIZE : 0); + 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 ); - rc = iscsi_get_key_value_pair( conn->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST, &value); + return -1; + } - if ( rc != 0 ) - return rc; + 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; - conn->data_digest = ((strcasecmp( (char *) value, "CRC32C" ) == 0) ? ISCSI_DIGEST_SIZE : 0); + 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(ISCSI_DEFAULT_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); - rc = iscsi_get_int_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS, &int_val); + iscsi_scsi_emu_naa_ieee_ext_set( (uint64_t *) vpd_page_design_desc_inquiry_data_pkt->desc, (uint8_t *) image->name ); - if ( rc != 0 ) - return rc; + 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)); - conn->session->max_conns = int_val; + 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(ISCSI_DEFAULT_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); - rc = iscsi_get_int_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_OUTSTANDING_R2T, &int_val); + 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; - if ( rc != 0 ) - return rc; + 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), ' ' ); - conn->session->max_outstanding_r2t = int_val; + 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)); - rc = iscsi_get_int_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_FIRST_BURST_LEN, &int_val); + 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(ISCSI_DEFAULT_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, (const uint8_t*)image->name ); - if ( rc != 0 ) - return rc; + alloc_len += (uint) (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + vpd_page_design_desc_inquiry_data_pkt->len); - conn->session->first_burst_len = int_val; + 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(ISCSI_DEFAULT_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, (const uint8_t*)port_name ); - rc = iscsi_get_int_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_BURST_LEN, &int_val); + alloc_len += (uint) (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + vpd_page_design_desc_inquiry_data_pkt->len); - if ( rc != 0 ) - return rc; + 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(ISCSI_DEFAULT_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); - conn->session->max_burst_len = int_val; + 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; - rc = iscsi_get_bool_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_INITIAL_R2T, &int_val); + 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, 1 ); - if ( rc != 0 ) - return rc; + 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)); - conn->session->flags &= ~(ISCSI_SESSION_FLAGS_INIT_R2T | ISCSI_SESSION_FLAGS_IMMEDIATE_DATA); + 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(ISCSI_DEFAULT_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); - if ( int_val != 0L ) - conn->session->flags |= ISCSI_SESSION_FLAGS_INIT_R2T; + 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; - rc = iscsi_get_bool_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IMMEDIATE_DATA, &int_val); + vpd_page_design_desc_target_port_group_inquiry_data_pkt->reserved = 0U; + vpd_page_design_desc_target_port_group_inquiry_data_pkt->index = 0U; - if ( rc != 0 ) - return rc; + 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)); - if ( int_val != 0L ) - conn->session->flags |= ISCSI_SESSION_FLAGS_IMMEDIATE_DATA; + 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(ISCSI_DEFAULT_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); - return 0; -} + iscsi_scsi_vpd_page_design_desc_logical_unit_group_inquiry_data_packet *vpd_page_design_desc_logical_unit_group_inquiry_data_pkt = vpd_page_design_desc_inquiry_data_pkt->desc; -/** - * @brief Handles stages of CHAP and other authentication methods. - * - * This function handles the various stages of the - * various authentication methods supported by the - * iSCSI protocol.\n - * Currently, only CHAP is implemented. - * - * @param[in] conn Pointer to iSCSI connection which may NOT - * be NULL, so be careful. - * @param[in] key_value_pairs Pointer to hash map containing the - * authentication key and value pairs. NULL - * is NOT allowed here, so take caution. - * @param[in] auth_method Pointer to string containing the - * authentication method. NULL is forbidden, - * so be careful. - * @param[in] buf Pointer to DataSegment buffer. May NOT be - * NULL, so take caution. - * @param[in] pos Remaining number of bytes to read. - * @param[in] len Total number of bytes of DataSegment buffer. - * @return 0 if authentication methods were handled successfully, - * a negative error code otherwise. - */ -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. + 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) ISCSI_DEFAULT_DEVICE_ID ); - return 0; -} + 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)); -/** - * @brief Checks buffer sizes of an iSCSI connection with it's associated session for consistency. - * - * This function ensures that, for example, first - * burst length does not exceed maximum burst - * length and that the buffers don't exceed their - * minimum and maximum allowed values. - * - * @param[in] conn Pointer to iSCSI connection which holds the - * values to be checked for consistency. May NOT be NULL, - * so take caution. - * @retval -1 At least one value check failed the consistency check. - * @retval 0 All consistency checks have passed successfully. - */ -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 < 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; + iscsi_put_be16( (uint8_t *) &vpd_page_inquiry_data_pkt->alloc_len, (uint16_t) alloc_len ); - return 0; -} + 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; -/** - * @brief Updates iSCSI connection and session values after being retrieved from the client. - * - * This function copies the key and value pairs into the - * internal connection and session structure and checks - * them for consistency.\n - * The TCP receive buffer will be adjusted to the new - * updated value but is never lower than 4KiB and never - * higher than 8KiB plus header overhead and a factor of - * 4 for receiving four packets at once. - * - * @param[in] conn Pointer to ISCSI connection which should - * be updated. - * @retval -1 An error occured, e.g. socket is already closed. - * @retval 0 All values have been updated successfully and - * the socket is still alive. - */ -static int iscsi_connection_update_key_value_pairs(iscsi_connection *conn) -{ - int rc = iscsi_connection_copy_key_value_pairs( conn ); + alloc_len = (sizeof(iscsi_scsi_vpd_page_ext_inquiry_data_packet) - sizeof(iscsi_scsi_vpd_page_inquiry_data_packet)); - if ( rc < 0 ) { - if ( conn->state < ISCSI_CONNECT_STATE_EXITING ) - conn->state = ISCSI_CONNECT_STATE_EXITING; + 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 rc; - } + return -1; + } - rc = iscsi_connection_check_key_value_pairs( conn ); + 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; - if ( (rc < 0) && (conn->state < ISCSI_CONNECT_STATE_EXITING) ) - conn->state = ISCSI_CONNECT_STATE_EXITING; + iscsi_put_be16( (uint8_t *) &vpd_page_inquiry_data_pkt->alloc_len, (uint16_t) alloc_len ); - if ( conn->sock < 0 ) - return -1; + 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; - uint recv_buf_len = conn->session->first_burst_len; + 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 ); - if ( recv_buf_len < 4096U ) - recv_buf_len = 4096U; - else if ( recv_buf_len > 8192U ) - recv_buf_len = 8192U; + return -1; + } - 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 <<= 2U; // Receive up to four streams at once. + alloc_len = sizeof(struct iscsi_scsi_vpd_page_block_limits_inquiry_data_packet); - 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. + vpd_page_block_limits_inquiry_data_pkt->flags = 0; - return rc; -} + uint32_t blocks = (ISCSI_MAX_DS_SIZE / ISCSI_SCSI_EMU_BLOCK_SIZE); -/** - * @brief Prepares an iSCSI login response PDU and sends it via TCP/IP. - * - * This function constructs the login response PDU - * to be sent via TCP/IP. - * - * @param[in] conn Pointer to ISCSI connection to send the TCP/IP - * 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. 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. - * @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 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) -{ - const uint32_t ds_len = login_response_pdu->ds_len; + vpd_page_block_limits_inquiry_data_pkt->max_cmp_write_len = (uint8_t) blocks; - login_response_pdu->ds_len = login_response_pdu->len; + iscsi_put_be16( (uint8_t *) &vpd_page_block_limits_inquiry_data_pkt->optimal_granularity_xfer_len, (uint16_t) 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; - 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 ); + 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; - login_response_pkt->version_max = ISCSI_VERSION_MAX; - login_response_pkt->version_active = ISCSI_VERSION_MAX; + 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_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++ ); + iscsi_put_be16( (uint8_t *) &vpd_page_inquiry_data_pkt->alloc_len, (uint16_t) alloc_len ); - if ( conn->session != NULL ) { - iscsi_put_be32( (uint8_t *) &login_response_pkt->exp_cmd_sn, conn->session->exp_cmd_sn ); - iscsi_put_be32( (uint8_t *) &login_response_pkt->max_cmd_sn, conn->session->max_cmd_sn ); - } else { - iscsi_put_be32( (uint8_t *) &login_response_pkt->exp_cmd_sn, login_response_pdu->cmd_sn ); - iscsi_put_be32( (uint8_t *) &login_response_pkt->max_cmd_sn, login_response_pdu->cmd_sn ); - } + 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 ( 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 ); + 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 ); - iscsi_connection_pdu_write( conn, login_response_pdu, callback, (uint8_t *) conn ); + return -1; + } - if ( key_value_pairs != NULL ) { - iscsi_hashmap_iterate( key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL ); - iscsi_hashmap_destroy( key_value_pairs ); - } + alloc_len = sizeof(struct iscsi_scsi_vpd_page_block_dev_chars_inquiry_data_packet); - return ISCSI_CONNECT_PDU_READ_OK; -} + vpd_page_block_dev_chars_inquiry_data_pkt->medium_rotation_rate = ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_MEDIUM_ROTATION_RATE_NONE; + 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; -/** - * @brief Callback function after login response has been sent. - * - * This function is invoked after the login - * 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_login_err_complete(uint8_t *user_data) -{ - iscsi_connection *conn = (iscsi_connection *) user_data; + iscsi_put_be16( (uint8_t *) &vpd_page_inquiry_data_pkt->alloc_len, (uint16_t) alloc_len ); - if ( (conn->flags & ISCSI_CONNECT_FLAGS_FULL_FEATURE) != 0 ) - iscsi_connection_update_key_value_pairs( conn ); -} + break; + } + default : { + scsi_task->xfer_pos = 0UL; -/** - * @brief Callback function after login response has been sent. - * - * This function is invoked after the login - * 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_login_ok_complete(uint8_t *user_data) -{ - iscsi_connection *conn = (iscsi_connection *) user_data; + 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 ); - if ( conn->state >= ISCSI_CONNECT_STATE_EXITING ) - return; + return -1; - if ( (conn->flags & ISCSI_CONNECT_FLAGS_FULL_FEATURE) != 0 ) { - iscsi_connection_update_key_value_pairs( conn ); + break; + } + } - iscsi_connection_schedule( conn ); + return (int) (alloc_len + sizeof(struct iscsi_scsi_vpd_page_inquiry_data_packet)); } -} -/** - * @brief Initializes an iSCSI login response PDU structure. - * - * This function initializes the internal login - * response data structure which is part of the iSCSI - * login procedure. - * - * @param[in] login_response_pdu Pointer to login response PDU, NULL - * is not an allowed value here, so take caution. - * @param[in] pdu Pointer to login request PDU from client, - * may NOT be NULL, so be careful. - * @return 0 if initialization was successful, a negative error - * code otherwise. - */ -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 *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; + const uint8_t pti = ISCSI_SCSI_BASIC_INQUIRY_DATA_PUT_PERIPHERAL_TYPE(ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_DIRECT) | ISCSI_SCSI_BASIC_INQUIRY_DATA_PUT_PERIPHERAL_ID(ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_ID_POSSIBLE); - login_response_pdu->ds_len = 0UL; + std_inquiry_data_pkt->basic_inquiry.peripheral_type_id = pti; + std_inquiry_data_pkt->basic_inquiry.peripheral_type_mod_flags = 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; - 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)); + 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; - 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); + 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), ' ' ); - login_response_pkt->isid.a = login_req_pkt->isid.a; - login_response_pkt->isid.b = login_req_pkt->isid.b; // Copying over doesn't change endianess. - login_response_pkt->isid.c = login_req_pkt->isid.c; - 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); + char image_rev[sizeof(std_inquiry_data_pkt->product_rev_level) + 1]; - 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; + 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), ' ' ); - login_response_pkt->reserved2 = 0U; - login_response_pkt->reserved3 = 0ULL; + 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 ( ((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; - login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; + 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), ' ' ); - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - } else if ( (ISCSI_VERSION_MIN < login_req_pkt->version_min) || (ISCSI_VERSION_MAX > login_req_pkt->version_max) ) { - login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; - login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_WRONG_VERSION; + add_len += sizeof(ext_inquiry_data_pkt->vendor_spec); + } - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - } else if ( (ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_RESERVED) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0) ) { - login_response_pkt->flags &= (int8_t) ~(ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_MASK | ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT | ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_MASK); - login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; - login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; + if ( len >= ISCSI_NEXT_OFFSET(iscsi_scsi_ext_inquiry_data_packet, flags) ) { + ext_inquiry_data_pkt->flags = 0; - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + add_len += sizeof(ext_inquiry_data_pkt->flags); } - 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; -} + if ( len >= ISCSI_NEXT_OFFSET(iscsi_scsi_ext_inquiry_data_packet, reserved) ) { + ext_inquiry_data_pkt->reserved = 0U; -/** - * @brief Saves incoming key / value pairs from the client of a login request PDU. - * - * The login response structure has status detail - * invalid login request type set in case of an error. - * - * @param[in] conn Pointer to iSCSI connection - * used for key and value pair extraction. - * @param[out] key_value_pairs Pointer to hash map which - * stores all the parsed key and value pairs. - * @param[in] login_response_pdu Pointer to iSCSI login response - * PDU, may NOT be NULL, so be careful. - * @param[in] pdu Pointer to iSCSI login request packet - * PDU, may NOT be NULL, so be careful. - * @retval -1 An error occured during parse of - * key and value pairs (memory exhaustion). - * @retval 0 All key and value pairs have been parsed successfully. - */ -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) -{ - iscsi_login_req_packet *login_req_pkt = (iscsi_login_req_packet *) pdu->bhs_pkt; - iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - const int rc = iscsi_parse_key_value_pairs( key_value_pairs, (uint8_t *) pdu->ds_cmd_data, pdu->ds_len, ((login_req_pkt->flags & ISCSI_LOGIN_REQ_FLAGS_CONTINUE) != 0), &conn->partial_pairs ); + add_len += sizeof(ext_inquiry_data_pkt->reserved); + } - if ( rc < 0 ) { - login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; - login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; + 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 ); - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER; + add_len += sizeof(ext_inquiry_data_pkt->version_desc[0]); } - return ISCSI_CONNECT_PDU_READ_OK; -} + 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 ); -/** - * @brief Extracts the Initiator Session ID (ISID) from packet data into a 64-bit unsigned integer. - * - * The ISID is constructed by OR'ing and shifting the - * four parts a, b, c and d into their proper places - * with d being in the LSB area.\n - * Since the ISID is only 48 bit wide, the 16 - * MSB bits are always cleared. - * - * @param[in] isid Pointer to the ISID part of packet data. - * May NOT be NULL, so be careful. - * @return The 64-bit unsigned integer value representing - * the Initiator Session ID (ISID). - */ -static inline uint64_t iscsi_connection_get_isid(const iscsi_isid *isid) -{ - return ((uint64_t) isid->a << 40ULL) | ((uint64_t) iscsi_get_be16(isid->b) << 24ULL) | ((uint64_t) isid->c << 16ULL) | (uint64_t) iscsi_get_be16(isid->d); -} + add_len += sizeof(ext_inquiry_data_pkt->version_desc[1]); + } -/** - * @brief Initializes the login response port names. - * - * This function extracts the initiator name from the - * key and value pair and stores the result in - * the iSCSI connection, as well as a full qualified - * initiator port name. - * - * @param[in] conn Pointer to iSCSI connection where to - * store the initiator name. - * @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 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 - * successfully, a negative error code otherwise - * in which case the status class and detail are - * set as well. - */ -static int iscsi_connection_login_init_port(iscsi_connection *conn, iscsi_pdu *response_pdu, iscsi_hashmap *key_value_pairs, uint8_t **init_port_name) -{ - 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, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_INITIATOR_NAME, &init_name ); + 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 ); - if ( rc != 0 ) { - 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; + add_len += sizeof(ext_inquiry_data_pkt->version_desc[2]); + } - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + 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]); } - conn->init_name = iscsi_sprintf_alloc( "%s", init_name ); + 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 ( conn->init_name == NULL ) { - 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; + 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])); - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + memset( &ext_inquiry_data_pkt->version_desc[4], 0, alloc_len ); + add_len += alloc_len; } - *init_port_name = iscsi_sprintf_alloc( "%s,i,0x%12.12" PRIx64, init_name, iscsi_connection_get_isid( &login_response_pkt->isid ) ); + std_inquiry_data_pkt->basic_inquiry.add_len = (uint8_t) add_len; - if ( *init_port_name == NULL ) { - 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 (int) (add_len + sizeof(struct iscsi_scsi_basic_inquiry_data_packet)); +} + +/** + * @brief Executes a report LUNs operation on a DNBD3 image. + * + * This function also sets the SCSI + * status result code accordingly. + * + * @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_report_luns_parameter_data_lun_list_packet *report_luns_parameter_data_pkt, const uint len, const uint select_report) +{ + const uint64_t lun = iscsi_scsi_lun_get_from_scsi( ISCSI_DEFAULT_LUN ); - free( conn->init_name ); - conn->init_name = NULL; + if ( len < sizeof(struct iscsi_scsi_report_luns_parameter_data_lun_list_packet) + sizeof(lun) ) + return -1; - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + switch ( select_report ) { + case ISCSI_SCSI_CDB_REPORT_LUNS_SELECT_REPORT_LU_ADDR_METHOD : + case ISCSI_SCSI_CDB_REPORT_LUNS_SELECT_REPORT_LU_KNOWN : + case ISCSI_SCSI_CDB_REPORT_LUNS_SELECT_REPORT_LU_ALL : { + break; + } + default : { + return -1; + } } - return ISCSI_CONNECT_PDU_READ_OK; + report_luns_parameter_data_pkt->reserved = 0UL; + iscsi_put_be32( (uint8_t *) &report_luns_parameter_data_pkt->lun_list_len, sizeof(lun) ); + iscsi_put_be64( (uint8_t *) (report_luns_parameter_data_pkt + 1), lun ); + + return (int) (sizeof(lun) + sizeof(struct iscsi_scsi_report_luns_parameter_data_lun_list_packet)); } /** - * @brief Determines the session type of login. + * @brief Initializes a mode sense page or sub page and zero fills the parameter data. * - * This function is used to retrieve the - * login session type and checks the - * relevant key and value pair for - * errors. + * 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] login_response_pdu Pointer to login response PDU, - * NULL is not allowed, so take caution. - * @param[in] key_value_pairs Pointer to key and value pairs which - * contain the session type parameter to be evaluated, - * which may NOT be NULL, so take caution. - * @param[out] type Pointer to integer which stores the - * determined session type and is NOT allowed to be - * NULL, so be careful. - * @return 0 on successful operation, a negative error code - * otherwise. The output session 'type' is unchanged, if - * an invalid session type value was retrieved. + * @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 int iscsi_connection_login_session_type(iscsi_pdu *login_response_pdu, iscsi_hashmap *key_value_pairs, int *type ) +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) { - 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, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_SESSION_TYPE, &type_str ); - - if ( (rc == 0) && (type_str != NULL) ) { - if ( strcasecmp( (char *) type_str, "Discovery" ) == 0 ) { - *type = ISCSI_SESSION_TYPE_DISCOVERY; - } else if ( strcasecmp( (char *) type_str, "Normal" ) == 0 ) { - *type = ISCSI_SESSION_TYPE_NORMAL; - } else { - *type = ISCSI_SESSION_TYPE_INVALID; + if ( mode_sense_mode_page_pkt == NULL ) + return; - 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; + 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)); - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - } + memset( mode_sense_mode_page_pkt->params, 0, (len - offsetof(struct iscsi_scsi_mode_sense_mode_page_data_packet, params)) ); } else { - if ( login_response_pkt->tsih != 0U ) { - *type = ISCSI_SESSION_TYPE_NORMAL; - } else { - 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; + 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; - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - } - } + 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)) ); - return ISCSI_CONNECT_PDU_READ_OK; + memset( mode_sense_mode_sub_page_pkt->params, 0, (len - offsetof(struct iscsi_scsi_mode_sense_mode_sub_page_data_packet, params)) ); + } } /** - * @brief Checks the target node info and sets login response PDU accordingly. + * @brief Handles a specific mode sense page or sub page. * - * This function also checks if the target node is - * redirected and if so, sets the response to the - * client response to the temporarily redirection - * URL.\n - * THe accessibility of the target node is - * also checked. + * This function also sets the SCSI + * status result code accordingly. * - * @param[in] conn Pointer to iSCSI connection which may NOT be + * @param[in] image Pointer to DNBD3 image to get + * the mode sense data from. May NOT be * NULL, so be careful. - * @param[in] login_response_pdu Pointer to login response PDU - * to set the parameters for. NULL is NOT allowed - * here, so take caution. - * @param[in] target_name Pointer to target node name and may - * NOT be NULL, be careful. - * @param[out] Pointer where to store the target node belonging - * to the target name. May NOT be NULL, so take caution. - * @return 0 if the check was successful or a negative - * error code otherwise. + * @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_connection_login_check_target(iscsi_connection *conn, iscsi_pdu *login_response_pdu, uint8_t *target_name, iscsi_target_node **target) +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) { - iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; + uint page_len; + int len = 0; - *target = iscsi_target_node_find( target_name ); + 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 ); - if ( *target == NULL ) { - login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; - login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_NOT_FOUND; + return -1; - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + break; + } } - if ( ((*target)->flags & ISCSI_TARGET_NODE_FLAGS_DESTROYED) != 0 ) { - login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; - login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_TARGET_REMOVED; + 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; - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - } + 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; - uint8_t *redirect_adr = iscsi_target_node_get_redirect( conn, *target ); + page_len = sizeof(struct iscsi_scsi_mode_sense_caching_mode_page_data_packet); - if ( redirect_adr != NULL ) { - 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); + iscsi_scsi_emu_primary_mode_sense_page_init( mode_sense_mode_page_pkt, page_len, page, sub_page ); - 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; + 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; - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + 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); - 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 ); + iscsi_scsi_emu_primary_mode_sense_page_init( mode_sense_mode_page_pkt, page_len, page, sub_page ); - 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; + len += page_len; - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } + break; + } + case ISCSI_SCSI_MODE_SENSE_MODE_SUB_PAGE_CODE_CONTROL_EXT : { + /* Control Extension */ - 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; + page_len = sizeof(struct iscsi_scsi_mode_sense_control_ext_mode_page_data_packet); - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - } + iscsi_scsi_emu_primary_mode_sense_page_init( mode_sense_mode_page_pkt, page_len, page, sub_page ); - if ( iscsi_target_node_access( conn, *target, conn->init_name, conn->init_adr ) < 0 ) { - 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_FAIL; + len += page_len; - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - } + 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 ); - return ISCSI_CONNECT_PDU_READ_OK; -} + break; + } + default : { + break; + } + } -/** - * @brief Retrieves iSCSI session by Target Session Identifying Handle (TSIH). - * - * This function checks if the TSIH is valid and if so, - * retrieves the pointer to its iSCSI session structure. - * - * @param[in] tsih Target Session Identifying Handle (TSIH). - * @return Pointer to related iSCSI session or NULL in case - * the TSIH is invalid or not found. - */ -static iscsi_session *iscsi_session_get_by_tsih(const uint16_t tsih) -{ - if ( tsih == 0U ) - return NULL; + break; + } + case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_XOR_CONTROL : { + if ( sub_page != 0U ) + break; - const uint64_t hash_key = tsih; - iscsi_session *session; + 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 ); - pthread_rwlock_rdlock( &iscsi_globvec->sessions_rwlock ); + len += page_len; - int rc = iscsi_hashmap_get( iscsi_globvec->sessions, (uint8_t *) &hash_key, sizeof(hash_key), (uint8_t **) &session ); + break; + } + case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_POWER_COND : { + if ( sub_page != 0U ) + break; - pthread_rwlock_unlock( &iscsi_globvec->sessions_rwlock ); + page_len = sizeof(struct iscsi_scsi_mode_sense_power_cond_mode_page_data_packet); - return ((rc == 0) ? session : NULL); -} + iscsi_scsi_emu_primary_mode_sense_page_init( mode_sense_mode_page_pkt, page_len, page, sub_page ); -/** - * @brief Appends an iSCSI connection to a session. - * - * This function checks if the maximum number of - * connections per session is not exceeded and if - * there is session spanning. - * @param[in] conn Pointer to iSCSI connection, may NOT - * be NULL, so be careful. - * @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). - * @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) -{ - iscsi_session *session = iscsi_session_get_by_tsih( tsih ); + len += page_len; - 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; + break; + } + case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_INFO_EXCEPTIOS_CONTROL : { + if ( sub_page != 0U ) + break; - 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; + page_len = sizeof(struct iscsi_scsi_mode_sense_info_exceptions_control_mode_page_data_packet); - conn->session = session; + iscsi_scsi_emu_primary_mode_sense_page_init( mode_sense_mode_page_pkt, page_len, page, sub_page ); - iscsi_list_enqueue( &session->conn_list, &conn->node ); + len += page_len; - session->conns++; + break; + } + case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_REPORT_ALL_MODE_PAGES : { + uint i; - return (ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SUCCESS << 8U) | ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SUCCESS; -} + 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 ); + } -/** - * @brief Checks whether the session is valid. - * - * This function also appends the connection - * to a session if it's valid.' - * - * @param[in] conn Pointer to iSCSI connection, - * may NOT be NULL, so take caution. - * @param[in] login_response_pdu Pointer to login response PDU, - * NULL is not allowed, hence be careful. - * @param[in] init_port_name Pointer to initiator port name. - * Non-NULL only, so be careful. - * @param[in] cid Connection ID (CID). - * @return 0 if valid session, a negative error code - * otherwise. - */ -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 = 0; + 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 ); + } - if ( login_response_pkt->tsih != 0U ) { - rc = iscsi_session_append( conn, init_port_name, iscsi_get_be16(login_response_pkt->tsih) ); + 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 ); + } - if ( rc != 0 ) { - login_response_pkt->status_class = (uint8_t) (rc >> 8U); - login_response_pkt->status_detail = (uint8_t) rc; + break; + } + default : { + break; + } + } - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + break; + } + default : { + break; } - } else if ( (iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_ISID_ALLOW_DUPLICATES) != 0 ) { - iscsi_connection_drop( conn, init_port_name, 0 ); } - return rc; -} - -/** - * @brief Initializes a rejecting login response packet. - * - * The login response structure has status detail - * invalid login request type set. - * - * @param[in] login_response_pdu Pointer to iSCSI login response PDU, - * NULL is an invalid value here, so take caution. - * @param[in] pdu Pointer to iSCSI login request PDU, may NOT - * be NULL, so be careful. - */ -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_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; + return len; } /** - * @brief Creates an iSCSI PDU structure used by connections. + * @brief Executes a mode sense operation on a DNBD3 image. * - * The PDU structure is used for allowing partial - * reading from the TCP/IP socket and correctly - * filling the data until everything has been read. + * This function also sets the SCSI + * status result code accordingly. * - * @param[in] conn Pointer to connection to link the PDU with. - * 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). + * @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. */ -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) +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) { - 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 ) { - logadd( LOG_ERROR, "iscsi_pdu_create: Out of memory while allocating iSCSI PDU" ); + 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 ); - return NULL; - } + if ( page_len < 0 ) + return -1; - 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 ); + const uint alloc_len = (hdr_len + block_desc_len + page_len); - if ( bhs_pkt == NULL ) { - logadd( LOG_ERROR, "iscsi_pdu_create: Out of memory while allocating iSCSI PDU packet data" ); + if ( mode_sense_6_parameter_hdr_data_pkt == NULL ) + return alloc_len; - free( pdu ); + 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 = ISCSI_SCSI_MODE_SENSE_6_PARAM_HDR_DATA_FLAGS_WP; + mode_sense_6_parameter_hdr_data_pkt->block_desc_len = (uint8_t) block_desc_len; + } else if ( hdr_len == sizeof(struct iscsi_scsi_mode_sense_10_parameter_header_data_packet) ) { + 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; - return NULL; + 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 = ISCSI_SCSI_MODE_SENSE_10_PARAM_HDR_DATA_FLAGS_WP; + 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 ); + } else { + logadd( LOG_DEBUG1, "iscsi_scsi_emu_primary_mode_sense: invalid parameter header length %u", hdr_len ); + return -1; } - 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; + const uint64_t num_blocks = iscsi_scsi_emu_block_get_count( image ); + const uint32_t block_size = ISCSI_SCSI_EMU_BLOCK_SIZE; - if ( pkt_ds_len != 0UL ) - memset( (((uint8_t *) pdu->ds_cmd_data) + ds_len), 0, (pkt_ds_len - ds_len) ); + 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); - return pdu; -} + 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 ); -/** - * @brief Destroys an iSCSI PDU structure used by connections. - * - * All associated data which has been read so - * far will be freed as well. - * - * @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) && (--pdu->ref == 0UL) ) { - if ( pdu->bhs_pkt != NULL ) { - free( pdu->bhs_pkt ); - - pdu->bhs_pkt = NULL; - pdu->ahs_pkt = NULL; - pdu->header_digest = NULL; - pdu->ds_cmd_data = NULL; - pdu->data_digest = NULL; - } + 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); - free( pdu ); + 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 Appends packet data to an iSCSI PDU structure used by connections. + * @brief Executes SCSI non-block emulation on a DNBD3 image. * - * This function adjusts the pointers if - * the packet data size needs to be - * extended. + * This function determines the + * non-block based SCSI opcode and + * executes it. * - * @param[in] pdu Pointer to iSCSI PDU where to append - * the packet data to. May NOT be NULL, so + * @param[in] scsi_task Pointer to iSCSI SCSI task + * to process the SCSI non-block + * operation for and may NOT be NULL, * 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). + * @return 0 on successful operation, a negative + * error code otherwise. */ -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) +static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) { - 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; + uint alloc_len; + uint len; + int rc; - 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)); + 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 ( new_len > old_len ) { - bhs_pkt = realloc( pdu->bhs_pkt, new_len ); + alloc_len = iscsi_get_be16(cdb_inquiry->alloc_len); + len = alloc_len; - if ( bhs_pkt == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_pdu_append: Out of memory while reallocating iSCSI PDU packet data" ); + if ( len < ISCSI_DEFAULT_RECV_DS_LEN ) + len = ISCSI_DEFAULT_RECV_DS_LEN; - return NULL; + 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; + } } - pdu->bhs_pkt = bhs_pkt; - } else { - bhs_pkt = pdu->bhs_pkt; + rc = iscsi_scsi_emu_primary_inquiry( scsi_task->connection->client->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; - 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; + 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 ( pkt_ds_len != 0UL ) - memset( (((uint8_t *) pdu->ds_cmd_data) + ds_len), 0, (pkt_ds_len - ds_len) ); - } + if ( rc < 0 ) + break; - return pdu->bhs_pkt; -} + len = alloc_len; -/** - * @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; + if ( len < ISCSI_DEFAULT_RECV_DS_LEN ) + len = ISCSI_DEFAULT_RECV_DS_LEN; - pdu->xfer_complete_callback = NULL; + iscsi_scsi_report_luns_parameter_data_lun_list_packet *report_luns_parameter_data_pkt = NULL; - if ( pdu->task != NULL ) - iscsi_task_destroy( pdu->task ); + if ( len > 0U ) { + report_luns_parameter_data_pkt = (iscsi_scsi_report_luns_parameter_data_lun_list_packet *) malloc( len ); - iscsi_connection_pdu_destroy( pdu ); + 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 ); - if ( callback != NULL ) - callback( user_data ); -} + break; + } + } -/** - * @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 + rc = iscsi_scsi_emu_primary_report_luns( report_luns_parameter_data_pkt, len, cdb_report_luns->select_report ); - if ( ahs_pkt == NULL ) - return NULL; + 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 ); - int count = index; - uint ahs_len = pdu->ahs_len; + break; + } - while ( (int) ahs_len > 0 ) { - if ( count-- < 0 ) - return ahs_pkt; + len = rc; - uint len = iscsi_get_be16(ahs_pkt->len) + offsetof(struct iscsi_ahs_packet, data); // Total length of current AHS packet + if ( len > 0U ) { + if ( len > alloc_len ) + len = alloc_len; - 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 - } + scsi_task->buf = (uint8_t *) report_luns_parameter_data_pkt; + } - logadd( LOG_ERROR, "iscsi_connection_pdu_ahs_packet_get: Specified index for AHS packet does not exist" ); + scsi_task->xfer_pos = len; + scsi_task->status = ISCSI_SCSI_STATUS_GOOD; - return NULL; -} + 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; -/** - * @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 + alloc_len = cdb_mode_sense_6->alloc_len; - if ( ahs_pkt == NULL ) - return 0; + 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; - int count = 0; - uint ahs_len = pdu->ahs_len; + rc = iscsi_scsi_emu_primary_mode_sense( scsi_task->connection->client->image, scsi_task, NULL, sizeof(struct iscsi_scsi_mode_sense_6_parameter_header_data_packet), block_desc_len, 0U, pc, page, sub_page ); - 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 + if ( rc < 0 ) + break; - 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++; - } + len = rc; - return count; -} + 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 ); -/// 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}; + 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 ); -/** - * @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]; - } + break; + } - return crc32c; -} + rc = iscsi_scsi_emu_primary_mode_sense( scsi_task->connection->client->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 ); -/** - * @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; + 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 ); - iscsi_put_le32( (uint8_t *) &header_digest->crc32c, crc32c ); -} + break; + } -/** - * @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; + if ( (rc >= 0) && (len > 0U) ) { + if ( len > alloc_len ) + len = alloc_len; - return (iscsi_get_le32(crc32c) == header_digest->crc32c); -} + scsi_task->buf = (uint8_t *) mode_sense_6_parameter_hdr_data_pkt; + rc = len; + } -/** - * @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; + if ( rc >= 0 ) { + scsi_task->xfer_pos = rc; + scsi_task->status = ISCSI_SCSI_STATUS_GOOD; + } - iscsi_put_le32( (uint8_t *) &data_digest->crc32c, crc32c ); -} + 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; -/** - * @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; + alloc_len = iscsi_get_be16(cdb_mode_sense_10->alloc_len); - return (iscsi_get_le32(crc32c) == data_digest->crc32c); -} + 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; -/** - * @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))); -} + rc = iscsi_scsi_emu_primary_mode_sense( scsi_task->connection->client->image, scsi_task, NULL, sizeof(struct iscsi_scsi_mode_sense_10_parameter_header_data_packet), block_desc_len, long_lba, pc10, page10, sub_page10 ); -/** - * @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 ( rc < 0 ) + break; - if ( conn->state >= ISCSI_CONNECT_STATE_EXITING ) - return; + len = rc; - iscsi_list_remove( &pdu->node ); + 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 ( err != 0 ) - conn->state = ISCSI_CONNECT_STATE_EXITING; + 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 ); - 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 ); -} + break; + } -/** - * @brief Writes and sends a response PDU to the client. - * - * This function sends a response PDU to the - * client after being processed by the server.\n - * If a header or data digest (CRC32C) needs to - * be calculated, this is done as well. - * - * @param[in] conn Pointer to iSCSI connection to handle. May - * NOT be NULL, so take caution. - * @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. - * May be NULL in case no further action is required. - * @param[in,out] user_data Data for the callback - * function. May be NULL if the callback function - * doesn't require additional data. - */ -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 ); + rc = iscsi_scsi_emu_primary_mode_sense( scsi_task->connection->client->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 ); - if ( pdu->data_digest != NULL ) - iscsi_connection_pdu_digest_data_update( pdu->data_digest, pdu->ds_cmd_data, pdu->ds_len ); - } + break; + } - pdu->xfer_complete_callback = callback; - pdu->xfer_complete_user_data = user_data; + if ( (rc >= 0) && (len > 0U) ) { + if ( len > alloc_len ) + len = alloc_len; - iscsi_list_enqueue( &conn->pdus_write, &pdu->node ); + scsi_task->buf = (uint8_t *) mode_sense_10_parameter_hdr_data_pkt; + rc = len; + } - if ( conn->state >= ISCSI_CONNECT_STATE_EXITING ) - return; + if ( rc >= 0 ) { + scsi_task->xfer_pos = rc; + scsi_task->status = ISCSI_SCSI_STATUS_GOOD; + } - 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) ); + break; + } + case ISCSI_SCSI_OPCODE_TESTUNITREADY : { + scsi_task->xfer_pos = 0UL; + scsi_task->status = ISCSI_SCSI_STATUS_GOOD; - if ( exec_queue == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_pdu_write: Out of memory while allocating execution queue for PDU write" ); + break; + } + case ISCSI_SCSI_OPCODE_STARTSTOPUNIT : { + scsi_task->xfer_pos = 0UL; + scsi_task->status = ISCSI_SCSI_STATUS_GOOD; - return; - } + break; + } + default : { + return ISCSI_SCSI_TASK_RUN_UNKNOWN; - 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; + break; + } + } - iscsi_list_enqueue( &conn->exec_queue, &exec_queue->node ); + return ISCSI_SCSI_TASK_RUN_COMPLETE; } /** - * @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. + * @brief Calculates the WWN using 64-bit IEEE Extended NAA for a name. * - * @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. + * @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. */ -static inline int iscsi_seq_num_cmp_lt(const uint32_t seq_num, const uint32_t seq_num_2) +static uint64_t iscsi_target_node_wwn_get(const uint8_t *name) { - 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)); -} + uint64_t value = 0ULL; + int i = 0; -/** - * @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)); + while ( name[i] != '\0' ) { + value = (value * 131ULL) + name[i++]; + } + + const uint64_t id_a = ((value & 0xFFF000000ULL) << 24ULL); + + return ((value & 0xFFFFFFULL) | 0x2000000347000000ULL | id_a); } /** - * @brief Removes an acknowledged PDU from SNACK PDU doubly linked list by ExpStatSN. + * @brief Creates and initializes an iSCSI session. * - * This function is invoked when ExpStatSN becomes - * invalid. + * This function creates and initializes all relevant + * data structures of an ISCSI session.\n + * Default key and value pairs are created and + * assigned before they are negotiated at the + * login phase. * - * @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. + * @param[in] conn Pointer to iSCSI connection to associate with the session. + * @param[in] type Session type to initialize the session with. + * @return Pointer to initialized iSCSI session or NULL in case an error + * occured (usually due to memory exhaustion). */ -void iscsi_connection_pdu_ack_remove(iscsi_connection *conn, const uint32_t exp_stat_sn) +static iscsi_session *iscsi_session_create(iscsi_connection *conn, const int type) { - conn->exp_stat_sn = ((exp_stat_sn < conn->stat_sn) ? exp_stat_sn : conn->stat_sn); + iscsi_session *session = malloc( sizeof(struct iscsi_session) ); - 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 ( session == NULL ) { + logadd( LOG_ERROR, "iscsi_session_create: Out of memory allocating iSCSI session" ); - if ( iscsi_seq_num_cmp_lt( stat_sn, conn->exp_stat_sn ) ) { - iscsi_list_remove( &pdu->node ); - iscsi_connection_pdu_free( conn, pdu ); - } + return NULL; } + + session->tsih = 0ULL; + session->type = type; + session->exp_cmd_sn = 0UL; + session->max_cmd_sn = 0UL; + session->current_text_init_task_tag = 0xFFFFFFFFUL; + + return session; } /** - * @brief Constructs and sends an iSCSI reject response to the client. + * @brief Deallocates all resources acquired by iscsi_session_create. * - * This function constructs an reject response PDU with its - * packet data.\n - * The original rejected packet data is appended as DataSegment - * according by iSCSI standard specification. + * This function also frees the associated key and value pairs, + * the attached connections as well as frees the initiator + * port. * - * @param[in] conn Pointer to iSCSI connection for reject packet construction. - * @param[in] pdu Pointer to iSCSI source PDU which contains the rejected packet data. - * @param[in] reason_code Reason code for rejected packet data. - * @retval -1 An error occured during reject packet generation, - * currently only happens on memory exhaustion. - * @retval 0 Reject packet and PDU constructed and sent successfully to the client. + * @param[in] session Pointer to iSCSI session to be freed. + * May be NULL in which case this function does nothing at all. */ -static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu, const int reason_code) +static void iscsi_session_destroy(iscsi_session *session) { - pdu->flags |= ISCSI_PDU_FLAGS_REJECTED; - - 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" ); - - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } - - iscsi_reject_packet *reject_pkt = (iscsi_reject_packet *) response_pdu->bhs_pkt; - - 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, 1UL ); - iscsi_put_be32( (uint8_t *) &reject_pkt->max_cmd_sn, 1UL ); - } - - reject_pkt->reserved4 = 0ULL; - - memcpy( response_pdu->ds_cmd_data, pdu->bhs_pkt, ds_len ); - - iscsi_connection_pdu_write( conn, response_pdu, NULL, NULL ); - - return ISCSI_CONNECT_PDU_READ_OK; + free( session ); } /** - * @brief Updates Command Sequence Number (CmdSN) of an incoming iSCSI PDU request. + * @brief Creates data structure for an iSCSI connection from iSCSI portal and TCP/IP socket. * - * This function updates the Command Sequence - * Number (CmdSN) for incoming data sent by - * the client. + * Creates a data structure for incoming iSCSI connection + * requests from iSCSI packet data. * - * @param[in] conn Pointer to iSCSI connection to handle. May - * NOT be NULL, so take caution. - * @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. + * @param[in] client dnbd3 client to associate the connection with. + * @return Pointer to initialized iSCSI connection structure or NULL in + * case of an error (invalid iSCSI packet data or memory exhaustion). */ -static int iscsi_connection_update_cmd_sn(iscsi_connection *conn, iscsi_pdu *pdu) +static iscsi_connection *iscsi_connection_create(dnbd3_client_t *client) { - iscsi_session *session = conn->session; - - if ( session == NULL ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + iscsi_connection *conn = malloc( sizeof(struct iscsi_connection) ); - 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 ( conn == NULL ) { + logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI connection" ); - 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; + return NULL; } - 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++; + conn->session = NULL; + conn->pdu_processing = NULL; + conn->login_response_pdu = NULL; + conn->id = 0; + conn->client = client; + 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->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->stat_sn = 0UL; - return ISCSI_CONNECT_PDU_READ_OK; + return conn; } /** - * @brief Handles an incoming iSCSI header login request PDU. + * @brief Deallocates all resources acquired by iscsi_connection_create. * - * This function handles login request header - * data sent by the client.\n - * If a response needs to be sent, this will - * be done as well. + * Deallocates a data structure of an iSCSI connection + * request and all allocated hash maps which don't + * require closing of external resources like closing + * TCP/IP socket connections. * - * @param[in] conn Pointer to iSCSI connection to handle. May - * NOT be NULL, so take caution. - * @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. + * @param[in] conn Pointer to iSCSI connection structure to be + * deallocated, TCP/IP connections are NOT closed by this + * function, use iscsi_connection_close for this. This may be + * NULL in which case this function does nothing. */ -static int iscsi_connection_pdu_header_handle_login_req(iscsi_connection *conn, iscsi_pdu *pdu) +static void iscsi_connection_destroy(iscsi_connection *conn) { - if ( ((conn->flags & ISCSI_CONNECT_FLAGS_FULL_FEATURE) != 0) && (conn->session != NULL) && (conn->session->type == ISCSI_SESSION_TYPE_DISCOVERY) ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - - const iscsi_login_req_packet *login_req_pkt = (iscsi_login_req_packet *) pdu->bhs_pkt; - - pdu->cmd_sn = iscsi_get_be32(login_req_pkt->cmd_sn); - - 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, 0U, 0, ISCSI_DEFAULT_RECV_DS_LEN, 0 ); - - if ( login_response_pdu == NULL ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - - 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 ); - - return ISCSI_CONNECT_PDU_READ_OK; + if ( conn != NULL ) { + iscsi_session_destroy( conn->session ); + iscsi_connection_pdu_destroy( conn->pdu_processing ); + free( conn ); } - - conn->login_response_pdu = login_response_pdu; - - return ISCSI_CONNECT_PDU_READ_OK; } /** - * @brief Handles an incoming iSCSI header NOP-Out request PDU. + * @brief Reads data for the specified iSCSI connection from its TCP socket. * - * This function handles NOP-Out request header - * data sent by the client.\n - * If a response needs to be sent, this will - * be done as well. + * The TCP socket is marked as non-blocking, so this function + * may not read all data requested. * - * @param[in] conn Pointer to iSCSI connection to handle. May - * NOT be NULL, so take caution. - * @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. + * Returns ISCSI_CONNECT_PDU_READ_ERR_FATAL if the operation + * indicates a fatal error with the TCP connection (including + * if the TCP connection was closed unexpectedly). + * + * Otherwise returns the number of bytes successfully read. */ -static int iscsi_connection_pdu_header_handle_nop_out(iscsi_connection *conn, iscsi_pdu *pdu) +static int32_t iscsi_connection_read(const iscsi_connection *conn, uint8_t *buf, const uint32_t len) { - if ( conn->session->type == ISCSI_SESSION_TYPE_DISCOVERY ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - - if ( pdu->ds_len > ISCSI_DEFAULT_MAX_RECV_DS_LEN ) - return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); - - iscsi_nop_out_packet *nop_out_pkt = (iscsi_nop_out_packet *) pdu->bhs_pkt; - 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 != (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 ( len == 0UL ) + return 0L; - if ( (init_task_tag == 0xFFFFFFFFUL) && (nop_out_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + int32_t rc; + do { + rc = (int32_t) recv( conn->client->sock, buf, (size_t) len, MSG_WAITALL ); + } while ( rc == -1 && errno == EINTR ); - return ISCSI_CONNECT_PDU_READ_OK; + if ( rc == 0 ) + return -1; // EOF + return rc; } /** - * @brief Handles an incoming iSCSI header SCSI command request PDU. + * @brief Appends a key and value pair to DataSegment packet data. * - * This function handles SCSI command request - * header data sent by the client.\n - * If a response needs to be sent, this will - * be done as well. + * This function adds any non-declarative key + * and value pair to an output DataSegment + * buffer and truncates if necessary. * - * @param[in] conn Pointer to iSCSI connection to handle. May + * @param[in] number true = int, false = char* + * @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 + * be written to output buffer which may * NOT be NULL, so take caution. - * @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. + * @param[in] buf Pointer to output buffer to write the + * key and value pair to. NULL is + * prohibited, so be careful. + * @param[in] pos Position of buffer in bytes to start + * writing to. + * @param[in] buflen Total length of buffer in bytes. + * @return -1 if buffer is already full, otherwise the number + * of bytes that are written or would have been written to + * the buffer. */ -static int iscsi_connection_pdu_header_handle_scsi_cmd(iscsi_connection *conn, iscsi_pdu *pdu) +static int iscsi_append_key_value_pair_packet(const bool number, const char *key, const char *value, char *buf, const uint32_t pos, const uint32_t buflen) { - 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 ); - - 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 ( pos >= buflen ) + return -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 ssize_t maxlen = buflen - pos; + if ( number ) { + return (int)snprintf( (buf + pos), maxlen, "%s=%d", key, (const int)(const size_t)value ) + 1; } + return (int)snprintf( (buf + pos), maxlen, "%s=%s", key, value ) + 1; +} - 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 ); +#define CLAMP(val, min, max) ((val) < (min) ? (min) : ((val) > (max) ? (max) : (val))) - pthread_rwlock_unlock( &conn->device->luns_rwlock ); +/** + * @brief Updates iSCSI connection and session values after being retrieved from the client. + * + * This function copies the key and value pairs into the + * internal connection and session structure and checks + * them for consistency.\n + * The TCP receive buffer will be adjusted to the new + * updated value but is never lower than 4KiB and never + * higher than 8KiB plus header overhead and a factor of + * 16 for receiving 16 packets at once. + * + * @param[in] conn Pointer to ISCSI connection which should + * be updated. + * @retval -1 An error occured, e.g. socket is already closed. + * @retval 0 All values have been updated successfully and + * the socket is still alive. + */ +static void iscsi_connection_update_key_value_pairs(iscsi_connection *conn, iscsi_negotiation_kvp *pairs) +{ + conn->session->opts.MaxBurstLength = CLAMP(pairs->MaxBurstLength, 512, ISCSI_MAX_DS_SIZE); + conn->session->opts.FirstBurstLength = CLAMP(pairs->FirstBurstLength, 512, pairs->MaxBurstLength); + conn->session->opts.MaxRecvDataSegmentLength = CLAMP(pairs->MaxRecvDataSegmentLength, 512, ISCSI_MAX_DS_SIZE); +} - if ( task->scsi_task.lun == NULL ) { - iscsi_scsi_task_lun_process_none( &task->scsi_task ); - iscsi_scsi_task_xfer_complete( &task->scsi_task ); +/** + * @brief Prepares an iSCSI login response PDU and sends it via TCP/IP. + * + * This function constructs the login response PDU + * to be sent via TCP/IP. + * + * @param[in] conn Pointer to ISCSI connection to send the TCP/IP + * packet with. May NOT be NULL, so be + * careful. + * @param[in] resp_pdu Pointer to login response PDU to + * be sent via TCP/IP. NULL is NOT + * allowed here, take caution. + * @return 0 if the login response has been sent + * successfully, a negative error code otherwise. + */ +static int iscsi_connection_pdu_login_response(iscsi_connection *conn, iscsi_pdu *resp_pdu) +{ + iscsi_login_response_packet *login_response_pkt = + (iscsi_login_response_packet *) iscsi_connection_pdu_resize( resp_pdu, resp_pdu->ahs_len, resp_pdu->ds_write_pos ); - return ISCSI_CONNECT_PDU_READ_OK; - } + login_response_pkt->version_max = ISCSI_VERSION_MAX; + login_response_pkt->version_active = ISCSI_VERSION_MAX; - 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 ); + iscsi_put_be32( (uint8_t *) &login_response_pkt->total_ahs_len, resp_pdu->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++ ); - return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_INVALID_PDU_FIELD ); + if ( conn->session != NULL ) { // TODO: Needed? MC/S? + iscsi_put_be32( (uint8_t *) &login_response_pkt->exp_cmd_sn, conn->session->exp_cmd_sn ); + iscsi_put_be32( (uint8_t *) &login_response_pkt->max_cmd_sn, conn->session->max_cmd_sn ); + } else { + iscsi_put_be32( (uint8_t *) &login_response_pkt->exp_cmd_sn, resp_pdu->cmd_sn ); + iscsi_put_be32( (uint8_t *) &login_response_pkt->max_cmd_sn, resp_pdu->cmd_sn ); } - 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; - } - } + 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 ); - pdu->task = task; + iscsi_connection_pdu_write( conn, resp_pdu ); return ISCSI_CONNECT_PDU_READ_OK; } /** - * @brief Handles an incoming iSCSI header task management function request PDU. + * @brief Initializes an iSCSI login response PDU structure. * - * This function handles task management function - * request header data sent by the client.\n - * If a response needs to be sent, this will - * be done as well. + * This function initializes the internal login + * response data structure which is part of the iSCSI + * login procedure. * - * @param[in] conn Pointer to iSCSI connection to handle. May - * NOT be NULL, so take caution. - * @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. + * @param[in] login_response_pdu Pointer to login response PDU, NULL + * is not an allowed value here, so take caution. + * @param[in] pdu Pointer to login request PDU from client, + * may NOT be NULL, so be careful. + * @return 0 if initialization was successful, a negative error + * code otherwise. */ -static int iscsi_connection_pdu_header_handle_task_func_req(iscsi_connection *conn, iscsi_pdu *pdu) +static int iscsi_connection_pdu_login_response_init(iscsi_pdu *login_response_pdu, const iscsi_pdu *pdu) { - // TODO: Implement opcode. + iscsi_login_req_packet *login_req_pkt = (iscsi_login_req_packet *) pdu->bhs_pkt; + iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - return 0; -} + 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)); -/** - * @brief Handles an incoming iSCSI header text request PDU. - * - * This function handles text request header - * 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] 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) -{ - 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 ); + 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); - iscsi_text_req_packet *text_req_pkt = (iscsi_text_req_packet *) pdu->bhs_pkt; + login_response_pkt->isid = login_req_pkt->isid; + 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); - 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 ( 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; - if ( exp_stat_sn != conn->stat_sn ) - conn->stat_sn = exp_stat_sn; + login_response_pkt->reserved2 = 0U; + login_response_pkt->reserved3 = 0ULL; - 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 ( ((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; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; - 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_ERR_LOGIN_RESPONSE; + } else if ( (ISCSI_VERSION_MIN < login_req_pkt->version_min) || (ISCSI_VERSION_MAX > login_req_pkt->version_max) ) { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_WRONG_VERSION; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } else if ( (ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_RESERVED) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0) ) { + login_response_pkt->flags &= (int8_t) ~(ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_MASK | ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT | ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_MASK); + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } + + 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; } /** - * @brief Searches an iSCSI PDU by Basic Header Segment (BHS) in the Ready To Transfer (R2T) active and queued task hash map. + * @brief Determines the session type of login. * - * This function searches for an iSCSI PDU by - * iterating through the iSCSI connection active - * and queued Ready To Transfer tasks hash map. + * This function is used to retrieve the + * login session type and checks the + * relevant key and value pair for + * errors. * - * @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). + * @param[in] login_response_pdu Pointer to login response PDU, + * NULL is not allowed, so take caution. + * @param[in] type_str Pointer to key and value pairs which + * contain the session type parameter to be evaluated, + * which may NOT be NULL, so take caution. + * @return 0 on successful operation, a negative error code + * otherwise. The output session 'type' is unchanged, if + * an invalid session type value was retrieved. */ -iscsi_pdu *iscsi_r2t_find_pdu_bhs(iscsi_connection *conn, iscsi_pdu *pdu) +static int iscsi_login_parse_session_type(iscsi_pdu *login_response_pdu, const char *type_str, int *type) { - iscsi_task *task; + iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - 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; + if ( type_str != NULL && strcasecmp( type_str, "Normal" ) == 0 ) { + *type = ISCSI_SESSION_TYPE_NORMAL; + return ISCSI_CONNECT_PDU_READ_OK; } - 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; - } + *type = ISCSI_SESSION_TYPE_INVALID; + logadd( LOG_DEBUG1, "Unsupported session type: %s", type_str ); + 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 NULL; + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; } /** - * @brief Sends an iSCSI Ready To Transfer Sequence Number (R2TSN) packet to the initiator. + * @brief Checks the target node info and sets login response PDU accordingly. * - * This function allocates and initializes a - * Ready To Transfer Sequence Number (R2TSN) - * packet to be sent to the client. + * This function also checks if the target node is + * redirected and if so, sets the response to the + * client response to the temporarily redirection + * URL.\n + * THe accessibility of the target node is + * also checked. * - * @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 + * @param[in] conn Pointer to iSCSI connection which may NOT be + * NULL, so be careful. + * @param[in] login_response_pdu Pointer to login response PDU + * to set the parameters for. NULL is NOT allowed + * here, so take caution. + * @param[in] target_name Pointer to target node name and may + * NOT be NULL, be careful. + * @return 0 if the check was successful or 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) +static int iscsi_image_from_target(iscsi_connection *conn, iscsi_pdu *login_response_pdu, const char *target_name) { - iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 0U, conn->header_digest, 0UL, conn->data_digest ); + iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - if ( response_pdu == NULL ) { - logadd( LOG_ERROR, "iscsi_r2t_send: Out of memory while allocating iSCSI Ready To Transfer response PDU" ); + char *image_rev = NULL; + char *tmpbuf = strdup( target_name ); + char *image_name = tmpbuf; + char *tmp = strchr( tmpbuf, ':' ); - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + if ( tmpbuf == NULL ) { + logadd( LOG_ERROR, "iscsi_target_node_image_get: Out of memory while allocating DNBD3 image name for iSCSI target node" ); + 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_LOGIN_RESPONSE; + } + + while ( tmp != NULL ) { + *tmp++ = '\0'; + if ( image_rev != NULL ) { + image_name = image_rev; + } + image_rev = tmp; + tmp = strchr( tmp, ':' ); } - iscsi_r2t_packet *r2t_pkt = (iscsi_r2t_packet *) response_pdu->bhs_pkt; + uint16_t rev = 0; + if ( image_rev != NULL ) { + char *end = NULL; + long rid = strtol( image_rev, &end, 10 ); + if ( end == NULL || *end != '\0' || rid < 0 || rid > 0xFFFF ) { + logadd( LOG_DEBUG1, "iscsi_image_from_target: Invalid revision number (%s) in iSCSI target node name: '%s'", image_rev, target_name ); + } else { + rev = (uint16_t)rid; + } + } + dnbd3_image_t *image = image_getOrLoad( image_name, rev ); - 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. + if ( image == NULL && image_rev != NULL ) { + image = image_getOrLoad( image_rev, rev ); + } - const uint64_t lun = iscsi_scsi_lun_get_from_scsi( task->lun_id ); + if ( image == NULL && strncasecmp( image_name, ISCSI_TARGET_NODE_WWN_NAME_PREFIX, ISCSI_STRLEN(ISCSI_TARGET_NODE_WWN_NAME_PREFIX) ) == 0 ) { + uint64_t wwn = strtoull( (image_name + ISCSI_STRLEN(ISCSI_TARGET_NODE_WWN_NAME_PREFIX)), NULL, 16 ); - 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)++ ); + image = image_getByWwn( wwn, rev, true ); - task->r2t_data_sn = 0UL; + if ( image == NULL ) { + wwn = strtoull( (tmp + ISCSI_STRLEN(ISCSI_TARGET_NODE_WWN_NAME_PREFIX)), NULL, 16 ); + image = image_getByWwn( wwn, rev, true ); + } + } - 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 ); + free( tmpbuf ); - response_pdu->task = task; - task->scsi_task.ref++; + if ( image == NULL ) { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_NOT_FOUND; - iscsi_connection_pdu_write( conn, response_pdu, NULL, NULL ); + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } + conn->client->image = image; 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). + * @brief Initializes a rejecting login response packet. + * + * The login response structure has status detail + * invalid login request type set. + * + * @param[in] login_response_pdu Pointer to iSCSI login response PDU, + * NULL is an invalid value here, so take caution. + * @param[in] pdu Pointer to iSCSI login request PDU, may NOT + * be NULL, so be careful. */ -static iscsi_pdu *iscsi_r2t_remove_pdu_from_snack_list(iscsi_connection *conn, iscsi_task *task, const uint32_t r2t_sn) +static void iscsi_connection_login_response_reject(iscsi_pdu *login_response_pdu, const iscsi_pdu *pdu) { - 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; - } - } - } + iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - return NULL; + 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->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; } /** - * @brief Resends the Ready To Transfer (R2T) packet data. + * @brief Creates an iSCSI PDU structure used by connections. * - * This function either sends a new R2T packet or - * resends an already sent one. + * The PDU structure is used for allowing partial + * reading from the TCP/IP socket and correctly + * filling the data until everything has been read. * - * @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. + * @param[in] conn Pointer to connection to link the PDU with. + * If this is NULL the connection has to be + * linked later. + * @param[in] ds_len Length of DataSegment packet data to be appended. + * May not exceed 16MiB - 1 (16777215 bytes). + * @return Pointer to allocated and zero filled PDU or NULL + * in case of an error (usually memory exhaustion). */ -static int iscsi_r2t_recovery_send(iscsi_connection *conn, iscsi_task *task, const uint32_t r2t_sn_ack, const int r2t_sn_send_new) +static iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint32_t ds_len) { - 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 ( ds_len > ISCSI_MAX_DS_SIZE ) { + logadd( LOG_ERROR, "iscsi_pdu_create: Invalid DS length" ); + return NULL; + } - if ( r2t_sn_send_new != 0 ) { - const uint32_t des_data_xfer_len = r2t_pkt->des_data_xfer_len; + const uint32_t pkt_ds_len = ISCSI_ALIGN( ds_len, ISCSI_ALIGN_SIZE ); + const uint32_t len = (uint32_t) ( sizeof(struct iscsi_bhs_packet) + pkt_ds_len ); - task->r2t_sn_ack++; + iscsi_pdu *pdu = malloc( sizeof(struct iscsi_pdu) ); + if ( pdu == NULL ) { + logadd( LOG_ERROR, "iscsi_pdu_create: Out of memory while allocating iSCSI PDU" ); - uint32_t len = (des_data_xfer_len - task->r2t_next_exp_pos); + return NULL; + } - if ( len > conn->session->max_burst_len ) - len = conn->session->max_burst_len; + iscsi_bhs_packet *bhs_pkt = malloc( len ); + if ( bhs_pkt == NULL ) { + free( pdu ); + logadd( LOG_ERROR, "iscsi_pdu_create: Out of memory while allocating iSCSI BHS packet" ); - iscsi_connection_pdu_free( conn, pdu ); + return NULL; + } - const int rc = iscsi_r2t_send( conn, task, &task->r2t_sn, task->r2t_next_exp_pos, len, task->target_xfer_tag ); + pdu->bhs_pkt = bhs_pkt; + pdu->ahs_pkt = NULL; + pdu->ds_cmd_data = (pkt_ds_len != 0UL) ? (iscsi_scsi_ds_cmd_data *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet)) : NULL; + pdu->task = NULL; + pdu->flags = 0; + pdu->bhs_pos = 0U; + pdu->ahs_len = 0; + pdu->ds_len = ds_len; + pdu->ds_write_pos = 0; + pdu->cmd_sn = 0UL; + pdu->recv_pos = 0; - 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 ); + if ( pkt_ds_len != 0UL ) { + memset( (((uint8_t *) pdu->ds_cmd_data) + ds_len), 0, (pkt_ds_len - ds_len) ); } - return ISCSI_CONNECT_PDU_READ_OK; + return pdu; } /** - * @brief Handles an incoming iSCSI header SCSI data out PDU. + * @brief Destroys an iSCSI PDU structure used by connections. * - * This function handles header SCSI data out - * sent by the client.\n - * If a response needs to be sent, this will - * be done as well. + * All associated data which has been read so + * far will be freed as well. * - * @param[in] conn Pointer to iSCSI connection to handle. May - * NOT be NULL, so take caution. - * @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. + * @param[in] pdu Pointer to PDU structure to be deallocated, + * may be NULL in which case this function + * does nothing. */ -static int iscsi_connection_pdu_header_handle_scsi_data_out(iscsi_connection *conn, iscsi_pdu *pdu) +static void iscsi_connection_pdu_destroy(iscsi_pdu *pdu) { - if ( conn->session->type == ISCSI_SESSION_TYPE_DISCOVERY ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - - 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 ); + if ( pdu == NULL ) + return; + free( pdu->bhs_pkt ); + free( pdu ); +} - iscsi_scsi_lun *lun = iscsi_device_find_lun( conn->device, task->lun_id ); +/** + * @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] ds_len Length of DataSegment packet data to be appended. + * May not exceed 16MiB - 1 (16777215 bytes). + * @return Pointer to allocated and zero filled PDU or NULL + * in case of an error (usually memory exhaustion). + */ +static iscsi_bhs_packet *iscsi_connection_pdu_resize(iscsi_pdu *pdu, const uint ahs_len, const uint32_t ds_len) +{ + if ( (ahs_len > ISCSI_MAX_AHS_SIZE) || (ds_len > ISCSI_MAX_DS_SIZE) || (ahs_len % 4 != 0) ) { + logadd( LOG_ERROR, "iscsi_connection_pdu_resize: Invalid AHS or DataSegment packet size" ); + return NULL; + } - pthread_rwlock_unlock( &conn->device->luns_rwlock ); + if ( (ahs_len != pdu->ahs_len) || (ds_len != pdu->ds_len) ) { + 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 + ISCSI_ALIGN(pdu->ds_len, ISCSI_ALIGN_SIZE)); + const uint32_t new_len = (uint32_t) (sizeof(struct iscsi_bhs_packet) + (uint32_t) ahs_len + pkt_ds_len); - if ( pdu->ds_len > ISCSI_DEFAULT_MAX_RECV_DS_LEN ) - return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); + if ( new_len > old_len ) { + bhs_pkt = realloc( pdu->bhs_pkt, new_len ); - const uint32_t init_task_tag = iscsi_get_be32(scsi_data_out_req_pkt->init_task_tag); + if ( bhs_pkt == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_pdu_resize: Out of memory while reallocating iSCSI PDU packet data" ); - if ( task->init_task_tag != init_task_tag ) - return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_INVALID_PDU_FIELD ); + return NULL; + } - const uint32_t data_sn = iscsi_get_be32(scsi_data_out_req_pkt->data_sn); + pdu->bhs_pkt = bhs_pkt; + } else { + bhs_pkt = pdu->bhs_pkt; + } - 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 ); + pdu->ahs_pkt = (ahs_len != 0U) ? (iscsi_ahs_packet *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet)) : 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) : NULL; + pdu->ahs_len = ahs_len; + pdu->ds_len = ds_len; - if ( rc == 0 ) - return ISCSI_CONNECT_PDU_READ_OK; + if ( pkt_ds_len != 0UL ) { + memset( (((uint8_t *) pdu->ds_cmd_data) + ds_len), 0, (pkt_ds_len - ds_len) ); } - - 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); + return pdu->bhs_pkt; +} - if ( len > conn->session->max_burst_len ) - len = conn->session->max_burst_len; +/** + * @brief Writes and sends a response PDU to the client. + * + * This function sends a response PDU to the + * client after being processed by the server.\n + * If a header or data digest (CRC32C) needs to + * be calculated, this is done as well. + * + * @param[in] conn Pointer to iSCSI connection to handle. May + * NOT be NULL, so take caution. Will be freed after sending, + * so don't access afterwards. + * @param[in] pdu Pointer to iSCSI server response PDU to send. + * May NOT be NULL, so be careful. + */ +static bool iscsi_connection_pdu_write(iscsi_connection *conn, iscsi_pdu *pdu) +{ + if ( conn->state >= ISCSI_CONNECT_STATE_EXITING ) { + iscsi_connection_pdu_destroy( pdu ); + return false; + } - const int rc = iscsi_r2t_send( conn, task, &task->r2t_sn, task->r2t_next_exp_pos, len, task->target_xfer_tag ); + // During allocation we already round up to ISCSI_ALIGN_SIZE, but store the requested size in the ds_len + // member, so it's safe to round up here before sending, the accessed memory will be valid and zeroed + const size_t len = (sizeof(struct iscsi_bhs_packet) + pdu->ahs_len + ISCSI_ALIGN(pdu->ds_len, ISCSI_ALIGN_SIZE)); + const ssize_t rc = sock_sendAll( conn->client->sock, pdu->bhs_pkt, len, ISCSI_CONNECT_SOCKET_WRITE_RETRIES ); - if ( rc < 0 ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + iscsi_connection_pdu_destroy( pdu ); - task->r2t_next_exp_pos += len; + if ( rc != (ssize_t)len ) { + conn->state = ISCSI_CONNECT_STATE_EXITING; + return false; } + return true; +} - 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; - } +/** + * @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)); +} - return ISCSI_CONNECT_PDU_READ_OK; +/** + * @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 Handles an incoming iSCSI header logout request PDU. + * @brief Constructs and sends an iSCSI reject response to the client. * - * This function handles logout request header - * data sent by the client.\n - * If a response needs to be sent, this will - * be done as well. + * This function constructs an reject response PDU with its + * packet data.\n + * The original rejected packet data is appended as DataSegment + * according by iSCSI standard specification. * - * @param[in] conn Pointer to iSCSI connection to handle. May - * NOT be NULL, so take caution. - * @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. + * @param[in] conn Pointer to iSCSI connection for reject packet construction. + * @param[in] pdu Pointer to iSCSI source PDU which contains the rejected packet data. + * @param[in] reason_code Reason code for rejected packet data. + * @retval -1 An error ocurred during reject packet generation, + * currently only happens on memory exhaustion. + * @retval 0 Reject packet and PDU constructed and sent successfully to the client. */ -static int iscsi_connection_pdu_header_handle_logout_req(iscsi_connection *conn, iscsi_pdu *pdu) +static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu, const int reason_code) { - iscsi_logout_req_packet *logout_req_pkt = (iscsi_logout_req_packet *) pdu->bhs_pkt; - - 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; + pdu->flags |= ISCSI_PDU_FLAGS_REJECTED; - iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 0U, conn->header_digest, 0UL, conn->data_digest ); + 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, ds_len ); if ( response_pdu == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_pdu_header_handle_logout_req: Out of memory while allocating iSCSI logout response PDU" ); + logadd( LOG_ERROR, "iscsi_connection_handle_reject: Out of memory while allocating iSCSI reject 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; - } + iscsi_reject_packet *reject_pkt = (iscsi_reject_packet *) response_pdu->bhs_pkt; - 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++ ); + 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 ) { - 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 ); + 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 *) &logout_response_pkt->exp_cmd_sn, pdu->cmd_sn ); - iscsi_put_be32( (uint8_t *) &logout_response_pkt->max_cmd_sn, pdu->cmd_sn ); + iscsi_put_be32( (uint8_t *) &reject_pkt->exp_cmd_sn, 1UL ); + iscsi_put_be32( (uint8_t *) &reject_pkt->max_cmd_sn, 1UL ); } - logout_response_pkt->reserved4 = 0UL; - logout_response_pkt->time_wait = 0U; - logout_response_pkt->time_retain = 0U; - logout_response_pkt->reserved5 = 0UL; + reject_pkt->reserved4 = 0ULL; - iscsi_connection_pdu_write( conn, response_pdu, NULL, NULL ); + memcpy( response_pdu->ds_cmd_data, pdu->bhs_pkt, ds_len ); + + iscsi_connection_pdu_write( conn, response_pdu ); return ISCSI_CONNECT_PDU_READ_OK; } /** - * @brief Handles an incoming iSCSI header SNACK request PDU. + * @brief Updates Command Sequence Number (CmdSN) of an incoming iSCSI PDU request. * - * This function handles SNACK request header - * data sent by the client.\n - * If a response needs to be sent, this will - * be done as well. + * This function updates the Command Sequence + * Number (CmdSN) for incoming data sent by + * the client. * * @param[in] conn Pointer to iSCSI connection to handle. May * NOT be NULL, so take caution. @@ -12196,110 +3141,47 @@ static int iscsi_connection_pdu_header_handle_logout_req(iscsi_connection *conn, * @return 0 on success. A negative value indicates * an error. A positive value a warning. */ -static int iscsi_connection_pdu_header_handle_snack_req(iscsi_connection *conn, iscsi_pdu *pdu) +static int iscsi_connection_update_cmd_sn(iscsi_connection *conn, iscsi_pdu *pdu) { - // TODO: Implement opcode. - - return 0; -} + iscsi_session *session = conn->session; -/** - * @brief Handles an incoming iSCSI header PDU. - * - * This function handles all header data sent - * by the client, including authentication.\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] 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(iscsi_connection *conn, iscsi_pdu *pdu) -{ - if ( pdu == NULL ) + if ( session == NULL ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - const int opcode = ISCSI_GET_OPCODE(pdu->bhs_pkt->opcode); - - if ( opcode == ISCSI_OPCODE_CLIENT_LOGIN_REQ ) - return iscsi_connection_pdu_header_handle_login_req( conn, pdu ); + iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) pdu->bhs_pkt; + const int opcode = ISCSI_GET_OPCODE(scsi_cmd_pkt->opcode); - 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, 0U, 0, 0UL, 0 ); + pdu->cmd_sn = iscsi_get_be32(scsi_cmd_pkt->cmd_sn); - if ( login_response_pdu == NULL ) + 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)) ) { + logadd( LOG_WARNING, "Seqnum messup. Is: %u, want >= %u, < %u", + pdu->cmd_sn, session->exp_cmd_sn, session->max_cmd_sn ); return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - - 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; - } else if ( conn->state == ISCSI_CONNECT_STATE_INVALID ) { + } + } else if ( (pdu->cmd_sn != session->exp_cmd_sn) && (opcode != ISCSI_OPCODE_CLIENT_NOP_OUT) ) { + logadd( LOG_WARNING, "Seqnum messup. Is: %u, want: %u", + pdu->cmd_sn, session->exp_cmd_sn ); return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } - int rc = iscsi_connection_update_cmd_sn( conn, pdu ); - - if ( rc != 0 ) - return rc; - - switch ( opcode ) { - case ISCSI_OPCODE_CLIENT_NOP_OUT : { - rc = iscsi_connection_pdu_header_handle_nop_out( conn, pdu ); - - break; - } - case ISCSI_OPCODE_CLIENT_SCSI_CMD : { - rc = iscsi_connection_pdu_header_handle_scsi_cmd( conn, pdu ); - - break; - } - case ISCSI_OPCODE_CLIENT_TASK_FUNC_REQ : { - rc = iscsi_connection_pdu_header_handle_task_func_req( conn, pdu ); - - break; - } - case ISCSI_OPCODE_CLIENT_TEXT_REQ : { - rc = iscsi_connection_pdu_header_handle_text_req( conn, pdu ); - - break; - } - case ISCSI_OPCODE_CLIENT_SCSI_DATA_OUT : { - rc = iscsi_connection_pdu_header_handle_scsi_data_out( conn, pdu ); - - break; - } - case ISCSI_OPCODE_CLIENT_LOGOUT_REQ : { - rc = iscsi_connection_pdu_header_handle_logout_req( conn, pdu ); - - break; - } - case ISCSI_OPCODE_CLIENT_SNACK_REQ : { - rc = iscsi_connection_pdu_header_handle_snack_req( conn, pdu ); - - break; - } - default : { - return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); + uint32_t exp_stat_sn = iscsi_get_be32(scsi_cmd_pkt->exp_stat_sn); - break; - } - } + if ( iscsi_seq_num_cmp_gt( exp_stat_sn, conn->stat_sn ) ) + exp_stat_sn = conn->stat_sn; - 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)") ); + if ( ((scsi_cmd_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0) && (opcode != ISCSI_OPCODE_CLIENT_SCSI_DATA_OUT) ) + session->exp_cmd_sn++; - return rc; + return ISCSI_CONNECT_PDU_READ_OK; } /** - * @brief Handles an incoming iSCSI payload data NOP-Out request PDU. + * @brief Handles an incoming iSCSI header login request PDU. * - * This function handles NOP-Out request payload + * This function handles login request header * data sent by the client.\n * If a response needs to be sent, this will * be done as well. @@ -12311,196 +3193,138 @@ static int iscsi_connection_pdu_header_handle(iscsi_connection *conn, iscsi_pdu * @return 0 on success. A negative value indicates * an error. A positive value a warning. */ -static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscsi_pdu *pdu) +static int iscsi_connection_pdu_header_handle_login_req(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; - - if ( ds_len > conn->max_recv_ds_len ) - ds_len = conn->max_recv_ds_len; - - const uint64_t lun = iscsi_get_be64(nop_out_pkt->lun); - const uint32_t init_task_tag = iscsi_get_be32(nop_out_pkt->init_task_tag); - - conn->flags &= ~ISCSI_CONNECT_FLAGS_NOP_OUTSTANDING; - - if ( init_task_tag == 0xFFFFFFFFUL ) - return ISCSI_CONNECT_PDU_READ_OK; - - 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" ); - + if ( ((conn->flags & ISCSI_CONNECT_FLAGS_FULL_FEATURE) != 0) && (conn->session != NULL) + && (conn->session->type == ISCSI_SESSION_TYPE_DISCOVERY) ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } - - iscsi_nop_in_packet *nop_in_pkt = (iscsi_nop_in_packet *) response_pdu->bhs_pkt; - - nop_in_pkt->opcode = ISCSI_OPCODE_SERVER_NOP_IN; - nop_in_pkt->flags = -0x80; - 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 ); - 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 & 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; + const iscsi_login_req_packet *login_req_pkt = (iscsi_login_req_packet *) pdu->bhs_pkt; - if ( ds_len != 0UL ) - memcpy( response_pdu->ds_cmd_data, pdu->ds_cmd_data, ds_len ); + pdu->cmd_sn = iscsi_get_be32(login_req_pkt->cmd_sn); - iscsi_connection_pdu_write( conn, response_pdu, NULL, NULL ); + if ( pdu->ds_len > ISCSI_DEFAULT_RECV_DS_LEN ) + return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); - // conn->nop_in_last = iscsi_get_ticks(); + iscsi_pdu *login_response_pdu = iscsi_connection_pdu_create( conn, 8192 ); - return ISCSI_CONNECT_PDU_READ_OK; -} + if ( login_response_pdu == NULL ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; -/** - * @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; + const int rc = iscsi_connection_pdu_login_response_init( login_response_pdu, pdu ); - iscsi_task_queue( conn, task ); + if ( rc < 0 ) { + iscsi_connection_pdu_login_response( conn, login_response_pdu ); 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 ); + conn->login_response_pdu = login_response_pdu; - return iscsi_connection_handle_scsi_data_in_queued_tasks( conn ); + return ISCSI_CONNECT_PDU_READ_OK; } /** - * @brief Creates and submits a sub task for writing. - * - * This function is also assigns the task with - * an iSCSI PDU. + * @brief Handles an incoming iSCSI header NOP-Out request 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. + * This function handles NOP-Out request header + * 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] 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_task_sub_task_submit_write(iscsi_connection *conn, iscsi_task *task, iscsi_pdu *pdu, uint8_t *buf) +static int iscsi_connection_pdu_header_handle_nop_out(iscsi_connection *conn, iscsi_pdu *pdu) { - iscsi_task *sub_task = iscsi_task_create( conn, task, iscsi_scsi_task_xfer_complete ); - - if ( sub_task == NULL ) + if ( conn->session->type == ISCSI_SESSION_TYPE_DISCOVERY ) 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; + if ( pdu->ds_len > ISCSI_DEFAULT_MAX_RECV_DS_LEN ) + return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); - pdu->task = sub_task; - pdu->ref++; + iscsi_nop_out_packet *nop_out_pkt = (iscsi_nop_out_packet *) pdu->bhs_pkt; + 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); - task->pos += pdu->ds_len; + 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. - iscsi_task_queue( conn, sub_task ); + if ( (init_task_tag == 0xFFFFFFFFUL) && (nop_out_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; return ISCSI_CONNECT_PDU_READ_OK; } /** - * @brief Handles an incoming iSCSI payload data SCSI write command request PDU. + * @brief Handles an incoming iSCSI header SCSI command request PDU. * - * This function handles SCSI write command - * request payload data sent by the client.\n + * This function handles SCSI command request + * header 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. + * @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_write(iscsi_connection *conn, iscsi_task *task) +static int iscsi_connection_pdu_header_handle_scsi_cmd(iscsi_connection *conn, iscsi_pdu *pdu) { - iscsi_pdu *pdu = task->pdu; + 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; - 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 ( (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; + } - if ( rc < 0 ) { - iscsi_task_destroy( task ); + iscsi_task *task = iscsi_task_create( conn ); - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } + if ( task == NULL ) + 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 ); + uint32_t exp_xfer_len = iscsi_get_be32(scsi_cmd_pkt->exp_xfer_len); - if ( rc < 0 ) { - iscsi_task_destroy( task ); + 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->init_task_tag = iscsi_get_be32(scsi_cmd_pkt->init_task_tag); - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } - } + const uint64_t lun = iscsi_get_be64(scsi_cmd_pkt->lun); + task->lun_id = iscsi_scsi_lun_get_from_iscsi( lun ); - 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 ( pdu->ds_len == xfer_len ) { - iscsi_scsi_task *scsi_task = &task->scsi_task; + if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_READ) != 0 ) + task->scsi_task.flags |= ISCSI_SCSI_TASK_FLAGS_XFER_READ; - scsi_task->buf = (uint8_t *) pdu->ds_cmd_data; - scsi_task->len = xfer_len; + if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_WRITE) != 0 ) { + return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_COMMAND_NOT_SUPPORTED ); } - iscsi_task_queue( conn, task ); + pdu->task = task; return ISCSI_CONNECT_PDU_READ_OK; } /** - * @brief Handles an incoming iSCSI payload data SCSI command request PDU. + * @brief Handles an incoming iSCSI header text request PDU. * - * This function handles SCSI command request payload + * This function handles text request header * data sent by the client.\n * If a response needs to be sent, this will * be done as well. @@ -12512,395 +3336,311 @@ static int iscsi_connection_pdu_data_handle_scsi_cmd_write(iscsi_connection *con * @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) +static int iscsi_connection_pdu_header_handle_text_req(iscsi_connection *conn, iscsi_pdu *pdu) { - iscsi_task *task = pdu->task; + if ( pdu->ds_len > (uint) (sizeof(struct iscsi_bhs_packet) + ISCSI_DEFAULT_RECV_DS_LEN) ) + return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); - if ( task == NULL ) - return ISCSI_CONNECT_PDU_READ_OK; + iscsi_text_req_packet *text_req_pkt = (iscsi_text_req_packet *) pdu->bhs_pkt; - pthread_rwlock_rdlock( &conn->device->luns_rwlock ); + 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 ( 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 ); + if ( exp_stat_sn != conn->stat_sn ) + conn->stat_sn = exp_stat_sn; - return ISCSI_CONNECT_PDU_READ_OK; + 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; } - pthread_rwlock_unlock( &conn->device->luns_rwlock ); - - 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 ); - + if ( conn->session->current_text_init_task_tag == 0xFFFFFFFFUL ) { + conn->session->current_text_init_task_tag = init_task_tag; return ISCSI_CONNECT_PDU_READ_OK; } - - pdu->task = NULL; - iscsi_task_destroy( task ); - - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); } /** - * @brief Negotiates connection authentication method (none or CHAP). + * @brief Handles an incoming iSCSI header logout request PDU. * - * This function updates the key and value pairs if, and only if - * CHAP is either disabled or required. + * This function handles logout request header + * 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 update the key - * and value pairs for. May NOT be NULL, so be careful. - * @return 0 on successful update, a negative error code otherwise. + * @param[in] conn Pointer to iSCSI connection to handle. May + * NOT be NULL, so take caution. + * @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_chap_negotiate(iscsi_connection *conn) +static int iscsi_connection_pdu_header_handle_logout_req(iscsi_connection *conn, iscsi_pdu *pdu) { - int rc = 0; + iscsi_logout_req_packet *logout_req_pkt = (iscsi_logout_req_packet *) pdu->bhs_pkt; - if ( (conn->flags & ISCSI_CONNECT_FLAGS_CHAP_DISABLE) != 0 ) - 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, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD, (uint8_t *) "CHAP" ); + 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; - return rc; -} + iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 0UL ); -/** - * @brief Discovers iSCSI CHAP authentication session. - * - * This function copies over the global CHAP configuration - * into the iSCSI connection structure and then negotiates. - * - * @param[in] conn Pointer to iSCSI connection for iSCSI - * CHAP authentication discovery. May NOT be - * NULL, so be careful. - * @return 0 on successful negotiation, a negative error - * code otherwise. - */ -static int iscsi_connection_login_session_chap_discovery(iscsi_connection *conn) -{ - conn->flags &= ~(ISCSI_CONNECT_FLAGS_CHAP_DISABLE | ISCSI_CONNECT_FLAGS_CHAP_REQUIRE | ISCSI_CONNECT_FLAGS_CHAP_MUTUAL); + if ( response_pdu == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_pdu_header_handle_logout_req: Out of memory while allocating iSCSI logout response PDU" ); - if ( (iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_CHAP_DISABLE) != 0 ) - conn->flags |= ISCSI_CONNECT_FLAGS_CHAP_DISABLE; + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + } - if ( (iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_CHAP_REQUIRE) != 0 ) - conn->flags |= ISCSI_CONNECT_FLAGS_CHAP_REQUIRE; + iscsi_logout_response_packet *logout_response_pkt = (iscsi_logout_response_packet *) response_pdu->bhs_pkt; - if ( (iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_CHAP_MUTUAL) != 0 ) - conn->flags |= ISCSI_CONNECT_FLAGS_CHAP_MUTUAL; + logout_response_pkt->opcode = ISCSI_OPCODE_SERVER_LOGOUT_RES; + logout_response_pkt->flags = -0x80; - conn->chap_group = iscsi_globvec->chap_group; + const uint16_t cid = iscsi_get_be16(logout_req_pkt->cid); - return iscsi_connection_chap_negotiate( conn ); -} + if ( cid == conn->cid ) { + conn->flags |= ISCSI_CONNECT_FLAGS_LOGGED_OUT; -/** - * @brief Negotiates CHAP authentication. - * - * This function updates the key and value pairs if, and only if - * CHAP authentication is either disabled or required in the - * target node. - * - * @param[in] conn Pointer to iSCSI connection to update the key - * and value pairs for. May NOT be NULL, so be careful. - * @param[in] target Pointer to iSCSI target node used to check - * the CHAP authentication. NULL is not allowed here, - * so take caution. - * @return 0 on successful update, a negative error code otherwise. - */ -static int iscsi_connection_login_chap_negotiate(iscsi_connection *conn, const iscsi_target_node *target) -{ - conn->flags &= ~(ISCSI_CONNECT_FLAGS_CHAP_DISABLE | ISCSI_CONNECT_FLAGS_CHAP_REQUIRE | ISCSI_CONNECT_FLAGS_CHAP_MUTUAL); + 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 ( (target->flags & ISCSI_TARGET_NODE_FLAGS_CHAP_DISABLE) != 0 ) - conn->flags |= ISCSI_CONNECT_FLAGS_CHAP_DISABLE; + if ( conn->session != NULL ) { + conn->session->max_cmd_sn++; - if ( (target->flags & ISCSI_TARGET_NODE_FLAGS_CHAP_REQUIRE) != 0 ) - conn->flags |= ISCSI_CONNECT_FLAGS_CHAP_REQUIRE; + 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 ); + } - if ( (target->flags & ISCSI_TARGET_NODE_FLAGS_CHAP_MUTUAL) != 0 ) - conn->flags |= ISCSI_CONNECT_FLAGS_CHAP_MUTUAL; + logout_response_pkt->reserved4 = 0UL; + logout_response_pkt->time_wait = 0U; + logout_response_pkt->time_retain = 0U; + logout_response_pkt->reserved5 = 0UL; - conn->chap_group = target->chap_group; + iscsi_connection_pdu_write( conn, response_pdu ); - return iscsi_connection_chap_negotiate( conn ); + return ISCSI_CONNECT_PDU_READ_OK; } /** - * @brief Negotiates connection header and data digest (CRC32C). + * @brief Handles an incoming iSCSI header PDU. * - * This function updates the key and value pairs if, and only if - * header and data digests are enabled in the target node. + * This function handles all header data sent + * by the client, including authentication.\n + * If a response needs to be sent, this will + * be done as well. * - * @param[in] conn Pointer to iSCSI connection to update the key - * and value pairs for. May NOT be NULL, so be careful. - * @param[in] target Pointer to iSCSI target node used to check - * the digest status. NULL is not allowed here, so take - * caution. - * @return 0 on successful update, a negative error code otherwise. + * @param[in] conn Pointer to iSCSI connection to handle. May + * NOT be NULL, so take caution. + * @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_login_digest_negotiate(iscsi_connection *conn, const iscsi_target_node *target) +static int iscsi_connection_pdu_header_handle(iscsi_connection *conn, iscsi_pdu *pdu) { - int rc = 0; + if ( pdu == NULL ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - if ( target->header_digest != 0 ) - rc = iscsi_update_key_value_pair( conn->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_HEADER_DIGEST, (uint8_t *) "CRC32C" ); + const int opcode = ISCSI_GET_OPCODE(pdu->bhs_pkt->opcode); - if ( target->data_digest != 0 ) - rc = iscsi_update_key_value_pair( conn->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST, (uint8_t *) "CRC32C" ); + if ( opcode == ISCSI_OPCODE_CLIENT_LOGIN_REQ ) + return iscsi_connection_pdu_header_handle_login_req( conn, pdu ); - return rc; -} + 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, 0UL ); -/** - * @brief Determines iSCSI session login steps for normal authentication. - * - * This function also does related validation checks. - * - * @param[in] conn Pointer to iSCSI connection, may NOT be - * NULL, so take caution. - * @param[in] login_response_pdu Pointer to login response PDU where - * NULL is not allowed, so be careful. - * @param[in] key_value_pairs Pointer to hash map containing the login - * request key and value pairs and may NOT be NULL, so - * take caution. - * @param[in] init_port_name Pointer to iSCSI initiator port name. NULL - * is NOT an allowed value, so be careful. - * @param[in] cid Connection ID (CID). - * @return 0 on successful operation, a negative error code - * otherwise. - */ -static int iscsi_connection_login_session_normal(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_hashmap *key_value_pairs, uint8_t *init_port_name, const uint cid) -{ - 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, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_NAME, &target_name ); + if ( login_response_pdu == NULL ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - if ( (rc < 0) || (target_name == NULL) ) { - 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; + iscsi_connection_login_response_reject( login_response_pdu, pdu ); + iscsi_connection_pdu_write( conn, login_response_pdu ); return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; } - - const uint8_t *target_name_short = (uint8_t *) strstr( (char *) target_name, ":" ); - - conn->target_name_short = iscsi_sprintf_alloc( "%s", ((target_name_short != NULL) ? ++target_name_short : target_name) ); - - if ( conn->target_name_short == NULL ) { - 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; - + if ( conn->state == ISCSI_CONNECT_STATE_INVALID ) { return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } - rc = iscsi_connection_login_check_target( conn, login_response_pdu, target_name, &target ); - - if ( rc < 0 ) - return rc; - - conn->device = target->device; - conn->target = target; - conn->target_port = iscsi_device_find_port_by_portal_group_tag( target->device, conn->pg_tag ); - - rc = iscsi_connection_login_check_session( conn, login_response_pdu, init_port_name, cid ); + int rc = iscsi_connection_update_cmd_sn( conn, pdu ); - if ( rc < 0 ) + if ( rc != 0 ) return rc; - rc = iscsi_connection_login_chap_negotiate( conn, target ); - - if ( rc == 0 ) - rc = iscsi_connection_login_digest_negotiate( conn, target ); - - if (rc != 0) { - 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; - } - - return rc; -} - -/* - * This function is used to set the info in the connection data structure - * return - * 0: success - * otherwise: error - */ -static int iscsi_connection_login_set_info(iscsi_connection *conn, iscsi_pdu *login_response_pdu, const uint8_t *init_port_name, const int type, const uint cid) -{ - conn->flags &= ~ISCSI_CONNECT_FLAGS_AUTH; - conn->auth_chap.phase = ISCSI_AUTH_CHAP_PHASE_WAIT_A; - conn->cid = (uint16_t) cid; - - if ( conn->session == NULL ) { - iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - iscsi_target_node *target = conn->target; - const uint64_t isid = iscsi_connection_get_isid( &login_response_pkt->isid ); - iscsi_port *init_port = iscsi_port_create( init_port_name, isid, 0U ); - - if ( init_port == NULL ) { - 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; + switch ( opcode ) { + case ISCSI_OPCODE_CLIENT_NOP_OUT : { + rc = iscsi_connection_pdu_header_handle_nop_out( conn, pdu ); - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + break; } + case ISCSI_OPCODE_CLIENT_SCSI_CMD : { + rc = iscsi_connection_pdu_header_handle_scsi_cmd( conn, pdu ); - conn->session = iscsi_session_create( conn, target, type ); - - if ( conn->session == NULL ) { - iscsi_port_destroy( init_port ); - - 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_LOGIN_RESPONSE; + break; } + case ISCSI_OPCODE_CLIENT_TEXT_REQ : { + rc = iscsi_connection_pdu_header_handle_text_req( conn, pdu ); - 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; + break; } + case ISCSI_OPCODE_CLIENT_LOGOUT_REQ : { + rc = iscsi_connection_pdu_header_handle_logout_req( conn, pdu ); - rc = iscsi_port_transport_id_set( conn->session->init_port, conn->init_name, isid ); - - if ( rc < 0 ) { - iscsi_session_destroy( conn->session ); - conn->session = NULL; - - iscsi_port_destroy( init_port ); - - return rc; + break; } + default : { + rc = iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_COMMAND_NOT_SUPPORTED ); - 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 = (uint32_t) (login_response_pdu->cmd_sn + (uint32_t) conn->session->queue_depth - 1UL); + break; + } } - conn->init_port = conn->session->init_port; - - return ISCSI_CONNECT_PDU_READ_OK; + return rc; } /** - * @brief Sets iSCSI session target info key and value pairs. + * @brief Handles an incoming iSCSI payload data NOP-Out request PDU. * - * This function also sets the login response PDU - * key and value pairs. + * This function handles NOP-Out 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 of which - * the target info should be set, may NOT be NULL, - * so take caution. - * @param[in] login_response_pdu Pointer to login response PDU and - * NULL is not allowed here, so be careful. - * @param[in] type iSCSI session type. - * @return 0 on successfully setting target info, a - * negative error code otherwise. + * @param[in] conn Pointer to iSCSI connection to handle. May + * NOT be NULL, so take caution. + * @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_login_set_target_info(iscsi_connection *conn, iscsi_pdu *login_response_pdu, const int type) +static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscsi_pdu *pdu) { - iscsi_target_node *target = conn->target; - int rc; - - if ( target != NULL ) { - 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 *) "") ); + iscsi_nop_out_packet *nop_out_pkt = (iscsi_nop_out_packet *) pdu->bhs_pkt; + uint32_t ds_len = pdu->ds_len; - if ( rc < 0 ) - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER; - } + if ( ds_len > conn->session->opts.MaxRecvDataSegmentLength ) + ds_len = conn->session->opts.MaxRecvDataSegmentLength; - uint8_t *tmp_buf = iscsi_sprintf_alloc( "%s:%s,%" PRIu64, conn->portal_host, conn->portal_port, conn->pg_tag ); + const uint64_t lun = iscsi_get_be64(nop_out_pkt->lun); + const uint32_t init_task_tag = iscsi_get_be32(nop_out_pkt->init_task_tag); - if ( tmp_buf == NULL ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + conn->flags &= ~ISCSI_CONNECT_FLAGS_NOP_OUTSTANDING; - rc = iscsi_update_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, tmp_buf ); + if ( init_task_tag == 0xFFFFFFFFUL ) + return ISCSI_CONNECT_PDU_READ_OK; - free( tmp_buf ); + iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, ds_len ); - if ( rc < 0 ) - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER; + if ( response_pdu == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_pdu_data_handle_nop_out: Out of memory while allocating iSCSI NOP-In response PDU" ); - 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 ); + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + } - if ( rc < 0 ) - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER; + iscsi_nop_in_packet *nop_in_pkt = (iscsi_nop_in_packet *) response_pdu->bhs_pkt; - if ( target != NULL ) { - rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS, &tmp_buf ); + nop_in_pkt->opcode = ISCSI_OPCODE_SERVER_NOP_IN; + nop_in_pkt->flags = -0x80; + 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; // Minus one does not require endianess conversion + iscsi_put_be32( (uint8_t *) &nop_in_pkt->init_task_tag, init_task_tag ); + iscsi_put_be32( (uint8_t *) &nop_in_pkt->stat_sn, conn->stat_sn++ ); - if ( (rc == 0) && (strlen( (char *) tmp_buf ) != 0) ) { - 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 ( (nop_out_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) + conn->session->max_cmd_sn++; - if ( rc < 0 ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + 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; - 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 != 0UL ) + memcpy( response_pdu->ds_cmd_data, pdu->ds_cmd_data, ds_len ); - if ( ds_len < 0L ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + iscsi_connection_pdu_write( conn, response_pdu ); - login_response_pdu->len = ds_len; - } + return ISCSI_CONNECT_PDU_READ_OK; +} - if ( type == ISCSI_SESSION_TYPE_DISCOVERY ) { - rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, &tmp_buf ); +/** + * @brief Handles an incoming iSCSI payload data SCSI command request PDU. + * + * This function handles SCSI 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] 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) +{ + iscsi_task *task = pdu->task; - if ( (rc == 0) && (strlen( (char *) tmp_buf ) != 0) ) { - 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 ( task == NULL ) + return ISCSI_CONNECT_PDU_READ_OK; - if ( rc < 0 ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + if ( task->lun_id != ISCSI_DEFAULT_LUN ) { + logadd( LOG_WARNING, "Received SCSI command for unknown LUN %d", task->lun_id ); + iscsi_scsi_task_lun_process_none( &task->scsi_task ); + iscsi_scsi_task_xfer_complete( &task->scsi_task, pdu ); - 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 ); + return ISCSI_CONNECT_PDU_READ_OK; + } - if ( ds_len < 0L ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + if ( (task->scsi_task.flags & ISCSI_SCSI_TASK_FLAGS_XFER_READ) != 0 ) { + task->scsi_task.buf = NULL; + task->scsi_task.pos = 0UL; + task->scsi_task.len = task->scsi_task.xfer_len; + } + iscsi_scsi_lun_task_run( &task->scsi_task, pdu ); - login_response_pdu->ds_len = ds_len; - } - } + return ISCSI_CONNECT_PDU_READ_OK; +} - rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG, &tmp_buf ); +/* + * This function is used to set the info in the connection data structure + * return + * 0: success + * otherwise: error + */ +static int iscsi_connection_login_set_info(iscsi_connection *conn, iscsi_pdu *login_response_pdu, const int type, const uint cid) +{ + conn->flags &= ~ISCSI_CONNECT_FLAGS_AUTH; + conn->cid = (uint16_t) cid; - if ( rc == 0 ) { - 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 ( conn->session == NULL ) { + iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; + conn->session = iscsi_session_create( conn, type ); - if ( rc < 0 ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + if ( conn->session == NULL ) { + 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_PORTAL_GROUP_TAG, tmp_buf, (uint8_t *) login_response_pdu->ds_cmd_data, login_response_pdu->ds_len, login_response_pdu->len ); + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } - if ( ds_len < 0L ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + conn->stat_sn = iscsi_get_be32(login_response_pkt->stat_sn); - login_response_pdu->ds_len = ds_len; - } + conn->session->exp_cmd_sn = login_response_pdu->cmd_sn; + conn->session->max_cmd_sn = (uint32_t) (login_response_pdu->cmd_sn + ISCSI_DEFAULT_QUEUE_DEPTH - 1UL); } return ISCSI_CONNECT_PDU_READ_OK; @@ -12916,218 +3656,135 @@ static int iscsi_connection_login_set_target_info(iscsi_connection *conn, iscsi_ * may NOT be NULL, so be careful. * @param[in] login_response_pdu Pointer to login response PDU. * NULL is not allowed here, so take caution. - * @param[in] key_value_pairs Pointer to key and value pairs. + * @param[in] kvpairs Pointer to key and value pairs. * which may NOT be NULL, so take caution. * @param[in] cid Connection ID (CID). * @return 0 on success, a negative error code otherwise. */ -static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_hashmap *key_value_pairs, uint cid) +static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_negotiation_kvp *kvpairs, uint cid) { - uint8_t *init_port_name = NULL; + int type, rc; iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - conn->device = NULL; - conn->target = NULL; - - int rc = iscsi_connection_login_init_port( conn, login_response_pdu, key_value_pairs, &init_port_name ); - - 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 ) { - free( init_port_name ); + rc = iscsi_login_parse_session_type( login_response_pdu, kvpairs->SessionType, &type ); + if ( rc < 0 ) 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 ); - } else if ( type == ISCSI_SESSION_TYPE_DISCOVERY ) { - login_response_pkt->tsih = 0U; - - rc = iscsi_connection_login_session_chap_discovery( conn ); + if ( kvpairs->TargetName != NULL && type == ISCSI_SESSION_TYPE_NORMAL ) { + rc = iscsi_image_from_target( conn, login_response_pdu, kvpairs->TargetName ); } else { 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; - rc = ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - } - - if ( rc < 0 ) { - free( init_port_name ); - - return rc; + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; } - 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 = 1UL; - - 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; + return iscsi_connection_login_set_info( conn, login_response_pdu, type, cid ); +} + +static int iscsi_write_login_options_to_pdu( iscsi_connection *conn, iscsi_negotiation_kvp *pairs, iscsi_pdu *response_pdu ) +{ + uint payload_len = response_pdu->ds_write_pos; + +# define ADD_KV_INTERNAL(num, key, value) do { \ +int rc = iscsi_append_key_value_pair_packet( num, key, value, (char *)response_pdu->ds_cmd_data, payload_len, response_pdu->ds_len ); \ +if ( rc < 0 ) return -1; \ +payload_len += rc; \ +} while (0) +# define ADD_KV_OPTION_INT(key) do { \ +if ( pairs->key != -1 ) ADD_KV_INTERNAL( true, #key, (const char *)(size_t)conn->session->opts.key ); \ +} while (0) +# define ADD_KV_OPTION_STR(key) do { \ +if ( pairs->key != NULL ) ADD_KV_INTERNAL( false, #key, conn->session->opts.key ); \ +} while (0) +# define ADD_KV_PLAIN_INT(key, value) do { \ +if ( pairs->key != -1 ) ADD_KV_INTERNAL( true, #key, (const char *)(size_t)(value) ); \ +} while (0) +# define ADD_KV_PLAIN_STR(key, value) do { \ +if ( pairs->key != NULL ) ADD_KV_INTERNAL( false, #key, value ); \ +} while (0) + ADD_KV_OPTION_INT( MaxRecvDataSegmentLength ); + ADD_KV_OPTION_INT( MaxBurstLength ); + ADD_KV_OPTION_INT( FirstBurstLength ); + ADD_KV_PLAIN_INT( MaxConnections, 1 ); + ADD_KV_PLAIN_INT( ErrorRecoveryLevel, 0 ); + ADD_KV_PLAIN_STR( HeaderDigest, "None" ); + ADD_KV_PLAIN_STR( DataDigest, "None" ); +# undef ADD_KV_PLAIN +# undef ADD_KV_OPTION_INT +# undef ADD_KV_OPTION_STR + + if ( payload_len <= response_pdu->ds_len ) { + response_pdu->ds_write_pos = payload_len; + } else { + response_pdu->ds_write_pos = response_pdu->ds_len; } - - return iscsi_connection_login_set_target_info( conn, login_response_pdu, type ); + return (int)payload_len; } /** - * @brief Handles the Current Stage (CSG) bit of login response. + * @brief Handles iSCSI connection login response. * - * This function determines the authentication method - * and processes the various authentication stages. + * This function negotiates the login parameters + * and determines the authentication method. * - * @param[in] conn Pointer to iSCSI connection, may NOT be - * NULL, so take caution. + * @param[in] conn Pointer to iSCSI connection, + * may NOT be NULL, so be careful. * @param[in] login_response_pdu Pointer to login response PDU. - * NULL is NOT an allowed value here, so be careful. - * @param[in] key_value_pairs Pointer to key and value pairs to - * retrieve authentication details from. This - * is NOT allowed to be NULL. Be careful! - * @return 0 if authentication has been handled successfully, - * a negative error code otherwise. + * NULL is not allowed here, so take caution. + * @return 0 on success, a negative error code otherwise. */ -static int iscsi_connecction_handle_login_response_csg_bit(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_hashmap *key_value_pairs) +static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_negotiation_kvp *pairs) { iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; + iscsi_connection_update_key_value_pairs( conn, pairs ); + int payload_len = iscsi_write_login_options_to_pdu( conn, pairs, login_response_pdu ); - 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, 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; - login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISSING_PARAMETER; - - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - } - - if ( strcasecmp( (char *) auth_method, "None" ) == 0 ) { - conn->flags |= ISCSI_CONNECT_FLAGS_AUTH; - } else { - 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 < 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 = ds_len; - - if ( (conn->flags & ISCSI_CONNECT_FLAGS_AUTH) == 0 ) - login_response_pkt->flags &= (int8_t) ~ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT; - } - - break; - } - case ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION : { - if ( conn->state == ISCSI_CONNECT_STATE_INVALID ) { - if ( conn->flags & ISCSI_CONNECT_FLAGS_CHAP_REQUIRE ) { - 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; - } else { - conn->flags |= ISCSI_CONNECT_FLAGS_AUTH; - } - } - - if ( (conn->flags & ISCSI_CONNECT_FLAGS_AUTH) == 0 ) { - 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; + if ( payload_len < 0 || payload_len > login_response_pdu->ds_len ) { + 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_LOGIN_RESPONSE; - } + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } - break; - } - case ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FULL_FEATURE_PHASE : { + // Handle current stage (CSG bits) + switch ( ISCSI_LOGIN_RESPONSE_FLAGS_GET_CURRENT_STAGE(login_response_pkt->flags) ) { + case ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_SECURITY_NEGOTIATION : { + if ( pairs->AuthMethod != NULL && strcasecmp( pairs->AuthMethod, "None" ) == 0 ) { + conn->flags |= ISCSI_CONNECT_FLAGS_AUTH; + } else { + // Only "None" supported login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; - login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_ERR; return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - - break; } - default : { - login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; - login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - - break; - } + break; } + case ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION : { + if ( conn->state == ISCSI_CONNECT_STATE_INVALID ) { + conn->flags |= ISCSI_CONNECT_FLAGS_AUTH; + } - return ISCSI_CONNECT_PDU_READ_OK; -} - -/** - * - * @brief Checks whether the session type is valid. - * - * This function also can be used to output - * debugging info about the session, which - * is currently not implemented. - * - * @param[in] conn Pointer to iSCSI connection to be checked for - * validity. May NOT be NULL, so be careful. - * @param[in] login_response_pdu Pointer to login response PDU to - * set status class and detail in case of an error. - * NULL is not an allowed value here, take caution. - * @return 0 if the session type is valid, a negative error code - * otherwise. - */ -static int iscsi_connection_login_session_info_notify(iscsi_connection *conn, iscsi_pdu *login_response_pdu) -{ - if ( (conn->session->type != ISCSI_SESSION_TYPE_NORMAL) && (conn->session->type != ISCSI_SESSION_TYPE_DISCOVERY) ) { - iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - + break; + } + case ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FULL_FEATURE_PHASE : + default : { login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; } + } - return ISCSI_CONNECT_PDU_READ_OK; -} - -/** - * @brief Handles the Transit (T) bit of login response. - * - * This function handles the transitional stages - * of the authentication process. - * - * @param[in] conn Pointer to iSCSI connection, may NOT be - * NULL, so take caution. - * @param[in] login_response_pdu Pointer to login response PDU. - * NULL is NOT an allowed value here, so be careful. - * @return 0 if transition has been handled successfully, - * a negative error code otherwise. - */ -static int iscsi_connecction_handle_login_response_t_bit(iscsi_connection *conn, iscsi_pdu *login_response_pdu) -{ - 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) ) { + if ( (login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0 ) { + // Client set the transition bit - requests to move on to next stage + switch ( ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) ) { case ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION : { conn->login_phase = ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION; @@ -13143,10 +3800,15 @@ static int iscsi_connecction_handle_login_response_t_bit(iscsi_connection *conn, 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 ); + if ( (conn->session->type != ISCSI_SESSION_TYPE_NORMAL) && (conn->session->type != ISCSI_SESSION_TYPE_DISCOVERY) ) { + logadd( LOG_DEBUG1, "Unsupported session type %d, rejecting", conn->session->type ); + iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - if ( rc < 0 ) - return rc; + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } conn->flags |= ISCSI_CONNECT_FLAGS_FULL_FEATURE; @@ -13157,51 +3819,11 @@ static int iscsi_connecction_handle_login_response_t_bit(iscsi_connection *conn, login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - - break; } - } - - return ISCSI_CONNECT_PDU_READ_OK; -} - -/** - * @brief Handles iSCSI connection login response. - * - * This function negotiates the login parameters - * and determines the authentication method. - * - * @param[in] conn Pointer to iSCSI connection, - * may NOT be NULL, so be careful. - * @param[in] login_response_pdu Pointer to login response PDU. - * NULL is not allowed here, so take caution. - * @param[in] key_value_pairs Pointer to key and value pairs. - * which may NOT be NULL, so take caution. - * @return 0 on success, a negative error code otherwise. - */ -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 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 < 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 = (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 ) - rc = iscsi_connecction_handle_login_response_t_bit( conn, login_response_pdu ); + } + } - return rc; + return ISCSI_CONNECT_PDU_READ_OK; } /** @@ -13221,68 +3843,47 @@ static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi */ static int iscsi_connection_pdu_data_handle_login_req(iscsi_connection *conn, iscsi_pdu *pdu) { + int rc; iscsi_pdu *login_response_pdu = (iscsi_pdu *) conn->login_response_pdu; if ( login_response_pdu == NULL ) return ISCSI_CONNECT_PDU_READ_OK; - 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; - iscsi_login_req_packet *login_req_pkt = (iscsi_login_req_packet *) pdu->bhs_pkt; uint cid = iscsi_get_be16(login_req_pkt->cid); - int rc = iscsi_connection_save_incoming_key_value_pairs( conn, key_value_pairs, login_response_pdu, pdu ); - if ( rc < 0 ) { - iscsi_connection_pdu_login_response( conn, login_response_pdu, NULL, iscsi_connection_pdu_login_err_complete ); + iscsi_negotiation_kvp pairs; + iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; + rc = iscsi_parse_login_key_value_pairs( &pairs, (uint8_t *) pdu->ds_cmd_data, pdu->ds_len ); - return ISCSI_CONNECT_PDU_READ_OK; + if ( rc < 0 || rc > login_response_pdu->ds_len ) { + 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; } if ( conn->state == ISCSI_CONNECT_STATE_INVALID ) { - rc = iscsi_connection_handle_login_phase_none( conn, login_response_pdu, key_value_pairs, cid ); + rc = iscsi_connection_handle_login_phase_none( conn, login_response_pdu, &pairs, cid ); 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 ); + iscsi_connection_pdu_login_response( conn, login_response_pdu ); return ISCSI_CONNECT_PDU_READ_OK; } } - rc = iscsi_connecction_handle_login_response( conn, login_response_pdu, key_value_pairs ); + rc = iscsi_connecction_handle_login_response( conn, login_response_pdu, &pairs ); - 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 ISCSI_CONNECT_PDU_READ_OK; + if ( rc == ISCSI_CONNECT_PDU_READ_OK ) { + conn->state = ISCSI_CONNECT_STATE_RUNNING; } - conn->state = ISCSI_CONNECT_STATE_RUNNING; - - iscsi_connection_pdu_login_response( conn, login_response_pdu, key_value_pairs, iscsi_connection_pdu_login_ok_complete ); + iscsi_connection_pdu_login_response( conn, login_response_pdu ); 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 ); -} - /** * @brief Handles an incoming iSCSI payload data text request PDU. * @@ -13300,132 +3901,40 @@ static void iscsi_connection_pdu_text_complete(uint8_t *user_data) */ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, iscsi_pdu *pdu) { - 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; - 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 ); + iscsi_negotiation_kvp pairs; + int rc = iscsi_parse_login_key_value_pairs( &pairs, (uint8_t *) pdu->ds_cmd_data, pdu->ds_len ); 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 ); + iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 8192 ); 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; + iscsi_connection_update_key_value_pairs( conn, &pairs ); - 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 ); + int payload_len = iscsi_write_login_options_to_pdu( conn, &pairs, response_pdu ); - if ( ds_len < 0L ) { - iscsi_hashmap_iterate( key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL ); - iscsi_hashmap_destroy( key_value_pairs ); + if ( payload_len < 0 || payload_len > response_pdu->ds_len ) { 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; + iscsi_text_response_packet *text_response_pkt = (iscsi_text_response_packet *) iscsi_connection_pdu_resize( response_pdu, 0, response_pdu->ds_write_pos ); 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->flags = (int8_t) ISCSI_TEXT_RESPONSE_FLAGS_FINAL; - 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 ); + text_response_pkt->reserved = 0; - 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. + iscsi_put_be32( (uint8_t *) &text_response_pkt->total_ahs_len, payload_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. @@ -13434,7 +3943,7 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc 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->target_xfer_tag, (uint32_t) conn->id + 1UL ); } iscsi_put_be32( (uint8_t *) &text_response_pkt->stat_sn, conn->stat_sn++ ); @@ -13447,33 +3956,11 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc 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 ); + iscsi_connection_pdu_write( conn, response_pdu ); return ISCSI_CONNECT_PDU_READ_OK; } -/** - * @brief Handles an incoming iSCSI payload data SCSI data out request PDU. - * - * This function handles SCSI data out 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] 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_data_out(iscsi_connection *conn, iscsi_pdu *pdu) -{ - // TODO: Implement opcode. - - return 0; -} - /** * @brief Handles an incoming iSCSI payload data PDU. * @@ -13516,11 +4003,6 @@ static int iscsi_connection_pdu_data_handle(iscsi_connection *conn, iscsi_pdu *p break; } - case ISCSI_OPCODE_CLIENT_SCSI_DATA_OUT : { - rc = iscsi_connection_pdu_data_handle_scsi_data_out( conn, pdu ); - - break; - } case ISCSI_OPCODE_CLIENT_TASK_FUNC_REQ : case ISCSI_OPCODE_CLIENT_LOGOUT_REQ : case ISCSI_OPCODE_CLIENT_SNACK_REQ : { @@ -13533,9 +4015,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)") ); - + if ( rc < 0 ) { + logadd( LOG_ERROR, "Fatal error during payload handler (opcode 0x%02x) detected for client %s", (int) opcode, conn->client->hostName ); + } return rc; } @@ -13543,12 +4025,14 @@ static int iscsi_connection_pdu_data_handle(iscsi_connection *conn, iscsi_pdu *p * @brief Retrieves and merges splitted iSCSI PDU data read from TCP/IP socket. * * This function handles partial reads of data - * packet.\n - * Since iSCSI data can span multiple packets, not - * only by TCP/IP itself, but also by iSCSI protocol - * specifications, multiple calls are needed in order - * to be sure that all data packets have been - * received. + * packets.\n + * The function is guaranteed to read as many bytes as indicated by the + * PDU struct, unless the read timeout is reached, or the connection + * is closed/reset.\n + * Care is also taken for padding bytes that have to be read. It is + * assumed the according buffer will have enough space for the padding + * bytes, which is always guaranteed when using the create and resize + * helpers for allocating PDUs. * * @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. @@ -13556,38 +4040,21 @@ static int iscsi_connection_pdu_data_handle(iscsi_connection *conn, iscsi_pdu *p * @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) +static int iscsi_connection_pdu_data_read(iscsi_connection *conn, iscsi_pdu *pdu) { - const uint32_t ds_len = pdu->ds_len; + const uint32_t ds_len = ISCSI_ALIGN( pdu->ds_len, ISCSI_ALIGN_SIZE ); - 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 ( pdu->recv_pos < ds_len ) { + const int32_t len = iscsi_connection_read( conn, (((uint8_t *) pdu->ds_cmd_data) + pdu->recv_pos), (ds_len - pdu->recv_pos) ); if ( len < 0L ) return len; - pdu->pos += len; + pdu->recv_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; - } + if ( pdu->recv_pos < ds_len ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; return ISCSI_CONNECT_PDU_READ_OK; } @@ -13619,7 +4086,8 @@ 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, 0U, conn->header_digest, 0UL, conn->data_digest ); + assert( conn->pdu_processing == NULL ); + conn->pdu_processing = iscsi_connection_pdu_create( conn, 0UL ); if ( conn->pdu_processing == NULL ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; @@ -13629,116 +4097,62 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn) break; } case ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_HDR : { - if ( pdu->bhs_pos < sizeof(struct iscsi_bhs_packet) ) { + while ( 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 < 0L ) { conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; - break; + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } pdu->bhs_pos += len; - - if ( pdu->bhs_pos < sizeof(struct iscsi_bhs_packet) ) - return ISCSI_CONNECT_PDU_READ_OK; } if ( (conn->flags & ISCSI_CONNECT_FLAGS_LOGGED_OUT) != 0 ) { conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; - break; + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } iscsi_bhs_packet *bhs_pkt = pdu->bhs_pkt; - const uint ahs_len = ((uint) bhs_pkt->total_ahs_len << 2U); + const uint ahs_len = ((uint) bhs_pkt->total_ahs_len * 4); const uint32_t ds_len = iscsi_get_be24(bhs_pkt->ds_len); - bhs_pkt = iscsi_connection_pdu_append( pdu, ahs_len, conn->header_digest, ds_len, conn->data_digest ); + bhs_pkt = iscsi_connection_pdu_resize( pdu, ahs_len, ds_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; - - stat_rc = iscsi_hashmap_put( conn->stat_iscsi_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 ( 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) ); + int pos = 0; + while ( pos < ahs_len ) { + const int32_t len = iscsi_connection_read( conn, (((uint8_t *) pdu->ahs_pkt) + pos), (ahs_len - pos) ); if ( len < 0L ) { conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; - break; - } - - pdu->ahs_pos += len; - - if ( pdu->ahs_pos < ahs_len ) - return ISCSI_CONNECT_PDU_READ_OK; - } - - 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 ( len < 0L ) { - conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; - - break; - } - - pdu->header_digest_pos += len; - - if ( (int) pdu->header_digest_pos < pdu->header_digest_size ) - return ISCSI_CONNECT_PDU_READ_OK; + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } - 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; - } + pos += len; } - 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); + if ( iscsi_connection_pdu_header_handle( conn, pdu ) < 0 ) { + conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; + } else { + conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_DATA; + } break; } case ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_DATA : { - if ( pdu->ds_len != 0UL ) { + if ( pdu->ds_len != 0U ) { const int len = iscsi_connection_pdu_data_read( conn, pdu ); if ( len < 0 ) { conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; - break; + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } else if ( len > 0 ) { return ISCSI_CONNECT_PDU_READ_OK; } @@ -13746,21 +4160,21 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn) int rc; - if ( (conn->flags & ISCSI_CONNECT_FLAGS_REJECTED) != 0 ) + conn->pdu_processing = NULL; + if ( (conn->flags & ISCSI_CONNECT_FLAGS_REJECTED) != 0 ) { rc = 0; - else + } else { rc = iscsi_connection_pdu_data_handle( conn, pdu ); + } - if ( rc == 0 ) { - iscsi_connection_pdu_destroy( pdu ); + iscsi_connection_pdu_destroy( pdu ); - conn->pdu_processing = NULL; + if ( rc == 0 ) { conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_READY; return ISCSI_CONNECT_PDU_READ_PROCESSED; - } else { - conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; } + conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; break; } @@ -13770,7 +4184,8 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn) break; } default : { - logadd( LOG_ERROR, "iscsi_connection_pdu_read: Fatal error reading, unknown packet status. Should NEVER happen! Please report this bug to the developer" ); + logadd( LOG_ERROR, "iscsi_connection_pdu_read: Fatal error reading, unknown packet status." + " Should NEVER happen! Please report this bug to the developer" ); break; } @@ -13780,64 +4195,6 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn) return 0; } -#define ISCSI_PDU_HANDLE_COUNT 16 - -/** - * @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] 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) -{ - int i; - - for ( i = 0; i < ISCSI_PDU_HANDLE_COUNT; i++ ) { - 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 ); - - break; - } - default : { - break; - } - } - - free( exec_queue ); - } - - if ( rc == ISCSI_CONNECT_PDU_READ_OK ) - break; - else if ( rc == ISCSI_CONNECT_PDU_READ_ERR_FATAL ) - return rc; - - if ( (conn->flags & ISCSI_CONNECT_FLAGS_STOPPED) != 0 ) - break; - } - - return i; -} - /** * @brief Handles an iSCSI connection until connection is closed. * @@ -13854,173 +4211,22 @@ int iscsi_connection_pdu_handle(iscsi_connection *conn) */ 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!" ); + _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 ); + iscsi_connection *conn = iscsi_connection_create( client ); 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 ); + conn->pdu_processing = iscsi_connection_pdu_create( conn, 0UL ); 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; } @@ -14028,52 +4234,15 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ 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; + conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_HDR; - 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 ); + static atomic_int CONN_ID = 0; + conn->id = ++CONN_ID; - 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 ); + while ( iscsi_connection_pdu_read( conn ) >= ISCSI_CONNECT_PDU_READ_OK ) { + if ( (conn->flags & ISCSI_CONNECT_FLAGS_STOPPED) != 0 ) + break; } 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 ded2d1d..0c279fe 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -32,10 +32,6 @@ #ifndef DNBD3_ISCSI_H_ #define DNBD3_ISCSI_H_ -#ifdef __cplusplus -extern "C" { -#endif - #include #include #include @@ -48,23 +44,23 @@ extern "C" { #include "image.h" #if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) - // GCC-compatible compiler, targeting x86/x86-64 - #include + // GCC-compatible compiler, targeting x86/x86-64 + #include #elif defined(__GNUC__) && defined(__ARM_NEON__) - // GCC-compatible compiler, targeting ARM with NEON - #include + // GCC-compatible compiler, targeting ARM with NEON + #include #elif defined(__GNUC__) && defined(__IWMMXT__) - // GCC-compatible compiler, targeting ARM with WMMX - #include + // GCC-compatible compiler, targeting ARM with WMMX + #include #elif (defined(__GNUC__) || defined(__xlC__)) && (defined(__VEC__) || defined(__ALTIVEC__)) - // XLC or GCC-compatible compiler, targeting PowerPC with VMX/VSX - #include + // XLC or GCC-compatible compiler, targeting PowerPC with VMX/VSX + #include #elif defined(__GNUC__) && defined(__SPE__) - // GCC-compatible compiler, targeting PowerPC with SPE - #include + // GCC-compatible compiler, targeting PowerPC with SPE + #include #elif defined(_MSC_VER) - // Microsoft C/C++-compatible compiler - #include + // Microsoft C/C++-compatible compiler + #include #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__) @@ -241,76 +237,6 @@ 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)) @@ -365,434 +291,7 @@ static inline uint32_t iscsi_get_log2_of_pow2(const uint32_t value) /// 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. -#define ISCSI_HASHMAP_DEFAULT_CAPACITY_SHIFT 5UL - -/// Default capacity is 32 buckets. -#define ISCSI_HASHMAP_DEFAULT_CAPACITY (1UL << (ISCSI_HASHMAP_DEFAULT_CAPACITY_SHIFT)) - -/// Number of bits to shift left when resizing. -#define ISCSI_HASHMAP_RESIZE_SHIFT 1UL - -/// 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. -#define ISCSI_HASHMAP_KEY_ALIGN (1UL << (ISCSI_HASHMAP_KEY_ALIGN_SHIFT)) - - -/// Initial hash code. -#define ISCSI_HASHMAP_HASH_INITIAL 0x811C9DC5UL - -/// Value to multiply hash code with. -#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. - * - * This structure is used by the iSCSI hash map implementation - * in order to maintain the elements. - */ -typedef struct iscsi_hashmap_bucket { - /// Next bucket, MUST be first element. - iscsi_node node; - - /// Data used as key, MUST be aligned to 8 bytes and zero padded. - uint8_t *key; - - /// Size of key. - size_t key_size; - - /// Hash code for the key. - uint32_t hash; - - /// Associate4d value to the key, NULL is allowed. - uint8_t *value; -} iscsi_hashmap_bucket; - -/** - * @brief Hash map containing an expandable list of buckets - * - * This structure is used by the ultra performant hash map - * implementation. It uses a linked list allowing fast - * 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; - - /// Current count of buckets. - uint count; -} iscsi_hashmap; - -/** - * @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 - * through a hash map. It is also used for replacing - * already existing keys or for key removal. - * - * @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 User data to be used by the - * callback function. User data can be modified if - * desired and may also be NULL if the callback - * function handles this case. See the documentation - * of the callback implementation for details. - * @return A negative result indicates as fatal error, - * 0 means successful operation and a positive value - * indicates a non-fatal error or a warning. - */ -typedef int (*iscsi_hashmap_callback)(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); - -iscsi_hashmap *iscsi_hashmap_create(const uint capacity); // Creates an empty hash map with either specified or default capacity -void iscsi_hashmap_destroy(iscsi_hashmap *map); // Deallocates the hash map objects and buckets, not elements - // 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_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); // 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 +#define ISCSI_STRLEN(x) ((sizeof(x) / sizeof(uint8_t)) - 1) /* iSCSI protocol stuff (all WORD/DWORD/QWORD values are big endian by default @@ -805,7 +304,7 @@ int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, u #define ISCSI_MAX_AHS_SIZE (255UL << 2UL) /// iSCSI DataSegment maximum allowed size. -#define ISCSI_MAX_DS_SIZE 16777215UL +#define ISCSI_MAX_DS_SIZE 16777215 /// iSCSI packet data alignment (BHS, AHS and DataSegment). #define ISCSI_ALIGN_SIZE 4UL @@ -815,10 +314,10 @@ 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 +#define ISCSI_DEFAULT_RECV_DS_LEN 131072UL /// iSCSI default maximum DataSegment receive length in bytes. -#define ISCSI_DEFAULT_MAX_RECV_DS_LEN 65536UL +#define ISCSI_DEFAULT_MAX_RECV_DS_LEN 524288UL /// iSCSI default maximum Ready To Transfer (R2T) active tasks. @@ -941,6 +440,7 @@ int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, u /// 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) +#define ASSERT_IS_BHS(structname) _Static_assert( sizeof(structname) == ISCSI_BHS_SIZE, #structname " messed up" ) /** * @brief iSCSI Basic Header Segment packet data. @@ -951,32 +451,34 @@ int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, u * has been determined. */ typedef struct __attribute__((packed)) iscsi_bhs_packet { - /// Command opcode. - uint8_t opcode; + /// Command opcode. + uint8_t opcode; - /// Opcode-specific fields. - uint8_t opcode_fields[3]; + /// Opcode-specific fields. + uint8_t opcode_fields[3]; - /// Total length of AHS (Advanced Header Segment). - uint8_t total_ahs_len; + /// Total length of AHS (Advanced Header Segment). + uint8_t total_ahs_len; - /// Length of Data Segment. - uint8_t ds_len[3]; + /// Length of Data Segment. + uint8_t ds_len[3]; - union { - /// SCSI LUN bit mask. - uint64_t lun; + union { + /// SCSI LUN bit mask. + uint64_t lun; - /// Opcode-specific fields. - uint8_t opcode_spec[8]; - } lun_opcode; + /// Opcode-specific fields. + uint8_t opcode_spec[8]; + } lun_opcode; - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; - /// Opcode-specific fields. - uint8_t opcode_spec_fields[28]; + /// Opcode-specific fields. + uint8_t opcode_spec_fields[28]; } iscsi_bhs_packet; +ASSERT_IS_BHS( iscsi_bhs_packet ); + /// iSCSI AHS type: Extended Command Descriptor Block (CDB). @@ -995,61 +497,19 @@ typedef struct __attribute__((packed)) iscsi_bhs_packet { * has been determined. */ typedef struct __attribute__((packed)) iscsi_ahs_packet { - /// AHSLength. - uint16_t len; + /// AHSLength. + uint16_t len; - /// AHSType. - uint8_t type; + /// AHSType. + uint8_t type; - /// AHS-Specific. - uint8_t specific; + /// AHS-Specific. + uint8_t specific; - /// AHS-Specific data. - uint8_t data[0]; + /// AHS-Specific data. + uint8_t data[0]; } iscsi_ahs_packet; - -/** - * @brief iSCSI Extended CDB AHS packet data structure. - * - * This type of AHS MUST NOT be used if the CDBLength is less than 17. - * The length includes the reserved byte 3. - */ -typedef struct __attribute__((packed)) iscsi_ext_cdb_ahs_packet { - /// AHSLength: AHSLength - (CDBLength - 15). - uint16_t len; - - // AHSType: Identifier (always 1 according to iSCSI specifications). - uint8_t type; - - /// Reserved for future usage, always MUST be 0. - uint8_t reserved; - - /// ExtendedCDB. - uint8_t data[0]; -} iscsi_ext_cdb_ahs_packet; - -/** - * @brief iSCSI Bidirectional Read Expected Data Transfer Length AHS packet data structure. - * - * This structure is used to determine the bidirectional read - * expected data transfer length. - */ -typedef struct __attribute__((packed)) iscsi_bidi_read_exp_xfer_ahs_packet { - /// AHSLength: Always 5 according to iSCSI specifications for now. - uint16_t len; - - /// 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. - uint8_t reserved; - - /// Bidirectional Read Expected Data Transfer Length. - uint32_t bidi_read_exp_xfer_len; -} iscsi_bidi_read_exp_xfer_ahs_packet; - - /** * @brief DataSegment Error: Unexpected unsolicited data. * @@ -1091,84 +551,6 @@ typedef struct __attribute__((packed)) iscsi_bidi_read_exp_xfer_ahs_packet { */ #define ISCSI_DS_ERROR_INCORRECT_AMOUNT_OF_DATA_ASCQ 0x0D - -/** - * @brief DataSegment Error: Protocol Service CRC error. - * - * Certain iSCSI conditions result in the command being terminated at - * the target (response code of Command Completed at Target) with a SCSI - * CHECK CONDITION Status as outlined in the following definitions - * (Sense key: Aborted Command 0x0B). - */ -#define ISCSI_DS_ERROR_PROTOCOL_SERVICE_CRC_ERROR_ASC 0x47 - -/** - * @brief DataSegment Error: Protocol Service CRC error. - * - * Certain iSCSI conditions result in the command being terminated at - * the target (response code of Command Completed at Target) with a SCSI - * CHECK CONDITION Status as outlined in the following definitions - * (Sense key: Aborted Command 0x0B). - */ -#define ISCSI_DS_ERROR_PROTOCOL_SERVICE_CRC_ERROR_ASCQ 0x05 - - -/** - * @brief DataSegment Error: Selective Negative / Sequence Number Acknowledgment (SNACK) rejected. - * - * Certain iSCSI conditions result in the command being terminated at - * the target (response code of Command Completed at Target) with a SCSI - * CHECK CONDITION Status as outlined in the following definitions - * (Sense key: Aborted Command 0x0B). - */ -#define ISCSI_DS_ERROR_SNACK_REJECTED_ASC 0x11 - -/** - * @brief DataSegment Error: Selective Negative / Sequence Number Acknowledgment (SNACK) rejected. - * - * Certain iSCSI conditions result in the command being terminated at - * the target (response code of Command Completed at Target) with a SCSI - * CHECK CONDITION Status as outlined in the following definitions - * (Sense key: Aborted Command 0x0B). - */ -#define ISCSI_DS_ERROR_SNACK_REJECTED_ASCQ 0x13 - - -/** - * @brief iSCSI header digest in case CRC32C has been negotiated. - * - * Optional header and data digests protect the integrity of the header - * and data, respectively. The digests, if present, are located, - * respectively, after the header and PDU-specific data and cover, - * respectively, the header and the PDU data, each including the padding - * bytes, if any. - * - * The existence and type of digests are negotiated during the Login - * Phase. - */ -typedef struct __attribute__((packed)) iscsi_header_digest { - /// Header digest is a CRC32C for ensuring integrity. - uint32_t crc32c; -} iscsi_header_digest; - -/** - * @brief iSCSI data digest in case CRC32C has been negotiated. - * - * Optional header and data digests protect the integrity of the header - * and data, respectively. The digests, if present, are located, - * respectively, after the header and PDU-specific data and cover, - * respectively, the header and the PDU data, each including the padding - * bytes, if any. - * - * The existence and type of digests are negotiated during the Login - * Phase. - */ -typedef struct __attribute__((packed)) iscsi_data_digest { - /// Data digest is a CRC32C for ensuring integrity. - uint32_t crc32c; -} iscsi_data_digest; - - /** * @brief iSCSI SCSI CDB packet data structure. * @@ -1177,11 +559,11 @@ typedef struct __attribute__((packed)) iscsi_data_digest { * MUST be used to contain the CDB spillover. */ typedef struct __attribute__((packed)) iscsi_scsi_cdb { - /// SCSI opcode. - uint8_t opcode; + /// SCSI opcode. + uint8_t opcode; - /// Additional op-code specific data. - uint8_t data[0]; + /// Additional op-code specific data. + uint8_t data[0]; } iscsi_scsi_cdb; @@ -1198,20 +580,20 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb { * 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; + /// SCSI opcode. + iscsi_scsi_cdb cdb; - /// Logical Unit Number (LUN), CMMDT and EVPD. - uint8_t lun_flags; + /// Logical Unit Number (LUN), CMMDT and EVPD. + uint8_t lun_flags; - /// Page code. - uint8_t page_code; + /// Page code. + uint8_t page_code; - /// Allocation length in bytes. - uint16_t alloc_len; + /// Allocation length in bytes. + uint16_t alloc_len; - /// Control. - uint8_t control; + /// Control. + uint8_t control; } iscsi_scsi_cdb_inquiry; @@ -1221,17 +603,17 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_inquiry { * 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; + /// SCSI opcode. + iscsi_scsi_cdb cdb; - /// Logical Block Address (LBA). - uint8_t lba[3]; + /// Logical Block Address (LBA). + uint8_t lba[3]; - /// Transfer length in bytes. - uint8_t xfer_len; + /// Transfer length in bytes. + uint8_t xfer_len; - /// Control. - uint8_t control; + /// Control. + uint8_t control; } iscsi_scsi_cdb_read_write_6; @@ -1241,23 +623,23 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_6 { * 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; + /// SCSI opcode. + iscsi_scsi_cdb cdb; - /// Flags. - int8_t flags; + /// Flags. + int8_t flags; - /// Logical Block Address (LBA). - uint32_t lba; + /// Logical Block Address (LBA). + uint32_t lba; - /// Group number. - int8_t group_num; + /// Group number. + int8_t group_num; - /// Transfer length in bytes. - uint16_t xfer_len; + /// Transfer length in bytes. + uint16_t xfer_len; - /// Control. - uint8_t control; + /// Control. + uint8_t control; } iscsi_scsi_cdb_read_write_10; @@ -1267,23 +649,23 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_10 { * 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; + /// SCSI opcode. + iscsi_scsi_cdb cdb; - /// Flags. - int8_t flags; + /// Flags. + int8_t flags; - /// Logical Block Address (LBA). - uint32_t lba; + /// Logical Block Address (LBA). + uint32_t lba; - /// Transfer length in bytes. - uint32_t xfer_len; + /// Transfer length in bytes. + uint32_t xfer_len; - /// Restricted for MMC-6 and group number. - int8_t restrict_group_num; + /// Restricted for MMC-6 and group number. + int8_t restrict_group_num; - /// Control. - uint8_t control; + /// Control. + uint8_t control; } iscsi_scsi_cdb_read_write_12; @@ -1293,23 +675,23 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_12 { * 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; + /// SCSI opcode. + iscsi_scsi_cdb cdb; - /// Flags. - int8_t flags; + /// Flags. + int8_t flags; - /// Logical Block Address (LBA). - uint64_t lba; + /// Logical Block Address (LBA). + uint64_t lba; - /// Transfer length in bytes. - uint32_t xfer_len; + /// Transfer length in bytes. + uint32_t xfer_len; - /// Restricted for MMC-6 and group number. - int8_t restrict_group_num; + /// Restricted for MMC-6 and group number. + int8_t restrict_group_num; - /// Control. - uint8_t control; + /// Control. + uint8_t control; } iscsi_scsi_cdb_read_write_16; @@ -1329,100 +711,46 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_16 { * 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; + /// SCSI opcode. + iscsi_scsi_cdb cdb; - /// Reserved for future usage (always MUST be 0 for now). - uint8_t reserved; + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved; - /// Select report. - uint8_t select_report; + /// 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). + uint16_t reserved2; - /// Reserved for future usage (always MUST be 0 for now). - uint8_t reserved3; + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved3; - /// Allocation length in bytes. - uint32_t alloc_len; + /// Allocation length in bytes. + uint32_t alloc_len; - /// Reserved for future usage (always MUST be 0 for now). - uint8_t reserved4; + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved4; - /// Control. - uint8_t control; + /// 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 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 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 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 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 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 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 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 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: 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)) @@ -1437,196 +765,26 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_cmp_write { * 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; + /// SCSI opcode. + iscsi_scsi_cdb cdb; - /// Service action. - uint8_t action; + /// Service action. + uint8_t action; - /// Logical Block Address (LBA), obselete by now. - uint64_t lba; + /// Logical Block Address (LBA), obselete by now. + uint64_t lba; - /// Allocation length in bytes. - uint32_t alloc_len; + /// Allocation length in bytes. + uint32_t alloc_len; - /// Reserved for future usage (always MUST be 0 for now). - uint8_t reserved; + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved; - /// Control. - uint8_t control; + /// 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) @@ -1680,23 +838,23 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_mode_select_10 { * 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; + /// SCSI opcode. + iscsi_scsi_cdb cdb; - /// Flags. - int8_t flags; + /// Flags. + int8_t flags; - /// Page code and page control. - uint8_t page_code_control; + /// Page code and page control. + uint8_t page_code_control; - /// Sub page code. - uint8_t sub_page_code; + /// Sub page code. + uint8_t sub_page_code; - /// Allocation length in bytes. - uint8_t alloc_len; + /// Allocation length in bytes. + uint8_t alloc_len; - /// Control. - uint8_t control; + /// Control. + uint8_t control; } iscsi_scsi_cdb_mode_sense_6; @@ -1756,654 +914,249 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_mode_sense_6 { * 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; + /// SCSI opcode. + iscsi_scsi_cdb cdb; - /// Flags. - int8_t flags; + /// Flags. + int8_t flags; - /// Page code and page control. - uint8_t page_code_control; + /// Page code and page control. + uint8_t page_code_control; - /// Sub page code. - uint8_t sub_page_code; + /// 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). + uint16_t reserved; - /// Reserved for future usage (always MUST be 0 for now). - uint8_t reserved2; + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved2; - /// Allocation length in bytes. - uint16_t alloc_len; + /// Allocation length in bytes. + uint16_t alloc_len; - /// Control. - uint8_t control; + /// 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. + * @brief iSCSI SCSI DataSegment Command packet structure. * - * There are 6 bytes in the CDB field for this command. + * iSCSI targets MUST support and enable Autosense. If Status is CHECK + * CONDITION (0x02), then the data segment MUST contain sense data for + * the failed command. + * + * For some iSCSI responses, the response data segment MAY contain some + * 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_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; - +typedef struct __attribute__((packed)) iscsi_scsi_ds_cmd_data { + /// SenseLength: This field indicates the length of Sense Data. + uint16_t len; -/// 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) + /// The Sense Data contains detailed information about a CHECK CONDITION. SPC3 specifies the format and content of the Sense Data. + uint8_t sense_data[0]; + /// Response Data. + uint8_t res_data[0]; +} iscsi_scsi_ds_cmd_data; -/// 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 Basic Inquiry Data peripheral type: Direct access device. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_DIRECT 0x00 -/// 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 Basic Inquiry Data peripheral type: Sequential access device. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_SEQ 0x01 -/// 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 Basic Inquiry Data peripheral type: Printer device. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_PRINTER 0x02 -/// 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 Basic Inquiry Data peripheral type: Processor device. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_PROCESSOR 0x03 -/// 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 Basic Inquiry Data peripheral type: Write once device. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_WORM 0x04 -/// 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 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 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 Basic Inquiry Data peripheral type: Scanner device. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_SCANNER 0x06 -/// 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 Basic Inquiry Data peripheral type: Optical memory device. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_OPTICAL 0x07 -/// 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 Basic Inquiry Data peripheral type: Medium changer device. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_CHANGER 0x08 -/// 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 Basic Inquiry Data peripheral type: Communications device. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_COMM 0x09 -/// 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 Basic Inquiry Data peripheral type: Unknown or no device. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_UNKNOWN 0x1F -/// 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 Basic Inquiry Data peripheral type: First bit of the five bits. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_FIRST_BIT 0 -/// 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 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 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 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 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 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 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 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 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 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 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 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 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 Basic Inquiry Data peripheral identifier: Vendor specific. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_ID_VENDOR_UNIQ 0x4 -/// 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 Basic Inquiry Data peripheral identifier: First bit of the three bits. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_ID_FIRST_BIT 5 -/// 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 Basic Inquiry Data peripheral type modifier: Removable media. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_MOD_FLAGS_REMOVABLE_MEDIA (1 << 7) -/// 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)) +/// 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 -/** - * @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; +/// iSCSI SCSI Basic Inquiry Data ANSI version: SPC2. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_SPC2 0x4 - /// Execution flags. - int8_t exec_flags; +/// iSCSI SCSI Basic Inquiry Data ANSI version: SPC3. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_SPC3 0x5 - /// Reserved for future usage (always MUST be 0 for now). - uint8_t reserved; +/// iSCSI SCSI Basic Inquiry Data ANSI version: SPC4. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_SPC4 0x6 - /// Power condition modifier. - uint8_t power_cond_mod; +/// iSCSI SCSI Basic Inquiry Data ANSI version: SPC5. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_SPC5 0x7 - /// Flags. - int8_t flags; +/// iSCSI SCSI Basic Inquiry Data ANSI version: First bit of the three bits. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_FIRST_BIT 0 - /// Control. - uint8_t control; -} iscsi_scsi_cdb_start_stop_unit; +/// 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 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 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 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 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 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 Basic Inquiry Data ECMA version: First bit of the three bits. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ECMA_FIRST_BIT 3 -/// 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 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 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 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 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 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 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 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 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 Basic Inquiry Data ISO version: First bit of the two bits. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ISO_FIRST_BIT 6 -/// 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 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 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 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 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 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 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 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 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)) +/// 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 -/** - * @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; +/// 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 - /// Service action. - uint8_t action; +/// 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 - /// Scope and reservation type. - uint8_t scope_type; +/// 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 - /// Reserved for future usage (always MUST be 0 for now). - uint16_t reserved; +/// 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) - /// Parameter list length in bytes. - uint32_t param_list_len; +/// 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)) - /// Control. - uint8_t control; -} iscsi_scsi_cdb_pr_reserve_out; +/// 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 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 Basic Inquiry Data response data format flags: Hierarchical Support. +#define ISCSI_SCSI_BASIC_INQUIRY_DATA_RESPONSE_DATA_FMT_FLAGS_HISUP (1 << 4) -/// 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 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 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 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 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 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 - * the failed command. - * - * For some iSCSI responses, the response data segment MAY contain some - * 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_scsi_ds_cmd_data { - /// SenseLength: This field indicates the length of Sense Data. - uint16_t len; - - /// The Sense Data contains detailed information about a CHECK CONDITION. SPC3 specifies the format and content of the Sense Data. - uint8_t sense_data[0]; - - /// Response Data. - uint8_t res_data[0]; -} 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) +/// 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) /** @@ -2414,20 +1167,20 @@ typedef struct __attribute__((packed)) iscsi_scsi_ds_cmd_data { * cleared. */ typedef struct __attribute__((packed)) iscsi_scsi_basic_inquiry_data_packet { - /// Peripheral device type and qualifier. - uint8_t peripheral_type_id; + /// Peripheral device type and qualifier. + uint8_t peripheral_type_id; - /// Peripheral device type modifier and removable media bit. - int8_t peripheral_type_mod_flags; + /// Peripheral device type modifier and removable media bit. + int8_t peripheral_type_mod_flags; - /// ANSI-Approved, ECMA and ISO version. - uint8_t version; + /// ANSI-Approved, ECMA and ISO version. + uint8_t version; - /// Response data format, HISUP, NORMACA, AENC and TrmIOP flags. - int8_t response_data_fmt_flags; + /// Response data format, HISUP, NORMACA, AENC and TrmIOP flags. + int8_t response_data_fmt_flags; - /// Additional length in bytes. - uint8_t add_len; + /// Additional length in bytes. + uint8_t add_len; } iscsi_scsi_basic_inquiry_data_packet; @@ -2503,26 +1256,26 @@ typedef struct __attribute__((packed)) iscsi_scsi_basic_inquiry_data_packet { * 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; + /// iSCSI SCSI basic inquiry data packet. + iscsi_scsi_basic_inquiry_data_packet basic_inquiry; - /// PROTECT, 3PC, TPGS, ACC and SCCS. - uint8_t tpgs_flags; + /// PROTECT, 3PC, TPGS, ACC and SCCS. + uint8_t tpgs_flags; - /// MULTIP, VS and ENCSERV. - int8_t services_flags; + /// MULTIP, VS and ENCSERV. + int8_t services_flags; - /// Flags. - int8_t flags; + /// Flags. + int8_t flags; - /// Vendor identification. - uint8_t vendor_id[8]; + /// Vendor identification. + uint8_t vendor_id[8]; - /// Product identification. - uint8_t product_id[16]; + /// Product identification. + uint8_t product_id[16]; - /// Product revision level. - uint8_t product_rev_level[4]; + /// Product revision level. + uint8_t product_rev_level[4]; } iscsi_scsi_std_inquiry_data_packet; @@ -2551,29 +1304,29 @@ typedef struct __attribute__((packed)) iscsi_scsi_std_inquiry_data_packet { * 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; + /// iSCSI SCSI standard inquiry data packet. + iscsi_scsi_std_inquiry_data_packet std_inquiry; - /// Vendor specific. - uint8_t vendor_spec[20]; + /// Vendor specific. + uint8_t vendor_spec[20]; - /// Flags. - int8_t flags; + /// Flags. + int8_t flags; - /// Reserved for future usage (always MUST be 0). - uint8_t reserved; + /// Reserved for future usage (always MUST be 0). + uint8_t reserved; - /// Version descriptors. - uint16_t version_desc[8]; + /// 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). + uint64_t reserved2[2]; - /// Reserved for future usage (always MUST be 0). - uint32_t reserved3; + /// Reserved for future usage (always MUST be 0). + uint32_t reserved3; - /// Reserved for future usage (always MUST be 0). - uint16_t reserved4; + /// Reserved for future usage (always MUST be 0). + uint16_t reserved4; } iscsi_scsi_ext_inquiry_data_packet; @@ -2699,17 +1452,17 @@ typedef struct __attribute__((packed)) iscsi_scsi_ext_inquiry_data_packet { * set. */ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_inquiry_data_packet { - /// Peripheral device type and qualifier. - uint8_t peripheral_type_id; + /// Peripheral device type and qualifier. + uint8_t peripheral_type_id; - /// Page code. - uint8_t page_code; + /// Page code. + uint8_t page_code; - /// Allocation length in bytes. - uint16_t alloc_len; + /// Allocation length in bytes. + uint16_t alloc_len; - /// Parameters. - uint8_t params[0]; + /// Parameters. + uint8_t params[0]; } iscsi_scsi_vpd_page_inquiry_data_packet; @@ -2834,20 +1587,20 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_inquiry_data_packet { * 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; + /// Protocol identifier and code set. + uint8_t protocol_id_code_set; - /// Flags. - int8_t flags; + /// Flags. + int8_t flags; - /// Reserved for future usage (always MUST be 0). - uint8_t reserved; + /// Reserved for future usage (always MUST be 0). + uint8_t reserved; - /// Length in bytes. - uint8_t len; + /// Length in bytes. + uint8_t len; - /// Designation descriptor. - uint8_t desc[0]; + /// Designation descriptor. + uint8_t desc[0]; } iscsi_scsi_vpd_page_design_desc_inquiry_data_packet; @@ -2859,8 +1612,8 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_design_desc_inquiry_d * 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; + /// IEEE NAA Extended. + uint64_t ieee_naa_ext; } iscsi_scsi_vpd_page_design_desc_ieee_naa_ext_inquiry_data_packet; @@ -2872,14 +1625,14 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_design_desc_ieee_naa_ * set. */ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_design_desc_t10_vendor_id_inquiry_data_packet { - /// Vendor identification. - uint8_t vendor_id[8]; + /// Vendor identification. + uint8_t vendor_id[8]; - /// Product identification. - uint8_t product_id[16]; + /// Product identification. + uint8_t product_id[16]; - /// Unit serial number. - uint8_t unit_serial_num[32]; + /// Unit serial number. + uint8_t unit_serial_num[32]; } iscsi_scsi_vpd_page_design_desc_t10_vendor_id_inquiry_data_packet; @@ -2891,11 +1644,11 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_design_desc_t10_vendo * 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; + /// Reserved for future usage (always MUST be 0). + uint16_t reserved; - /// Port index. - uint16_t index; + /// Port index. + uint16_t index; } iscsi_scsi_vpd_page_design_desc_rel_target_port_inquiry_data_packet; @@ -2907,11 +1660,11 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_design_desc_rel_targe * 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; + /// Reserved for future usage (always MUST be 0). + uint16_t reserved; - /// Port group index. - uint16_t index; + /// Port group index. + uint16_t index; } iscsi_scsi_vpd_page_design_desc_target_port_group_inquiry_data_packet; @@ -2923,11 +1676,11 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_design_desc_target_po * 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; + /// Reserved for future usage (always MUST be 0). + uint16_t reserved; - /// Logical unit identifier. - uint16_t id; + /// Logical unit identifier. + uint16_t id; } iscsi_scsi_vpd_page_design_desc_logical_unit_group_inquiry_data_packet; @@ -3075,296 +1828,47 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_design_desc_logical_u * set. */ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_ext_inquiry_data_packet { - /// Peripheral device type and qualifier. - uint8_t peripheral_type_id; + /// Peripheral device type and qualifier. + uint8_t peripheral_type_id; - /// Page code. - uint8_t page_code; + /// Page code. + uint8_t page_code; - /// Reserved for future usage (always MUST be 0). - uint8_t reserved; + /// Reserved for future usage (always MUST be 0). + uint8_t reserved; - /// Page length in bytes. - uint8_t page_len; + /// Page length in bytes. + uint8_t page_len; - /// Check flags. - int8_t check_flags; + /// Check flags. + int8_t check_flags; - /// Support flags. - int8_t support_flags; + /// Support flags. + int8_t support_flags; - /// More support flags. - int8_t support_flags_2; + /// More support flags. + int8_t support_flags_2; - /// LUICLR. - uint8_t luiclr; + /// LUICLR. + uint8_t luiclr; - /// CBCS. - uint8_t cbcs; + /// CBCS. + uint8_t cbcs; - /// Micro DL. - uint8_t micro_dl; + /// 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). + uint64_t reserved2[6]; - /// Reserved for future usage (always MUST be 0). - uint32_t reserved3; + /// Reserved for future usage (always MUST be 0). + uint32_t reserved3; - /// Reserved for future usage (always MUST be 0). - uint16_t reserved4; + /// 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 @@ -3392,44 +1896,44 @@ typedef struct iscsi_scsi_emu_primary_inquiry_ports_fill { * set. */ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_block_limits_inquiry_data_packet { - /// Flags. - int8_t flags; + /// Flags. + int8_t flags; - /// Maximum COMPARE AND WRITE length in logical blocks. - uint8_t max_cmp_write_len; + /// 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; + /// Optimal transfer length granularity in logical blocks. + uint16_t optimal_granularity_xfer_len; - /// Maximum transfer length in logical blocks. - uint32_t max_xfer_len; + /// Maximum transfer length in logical blocks. + uint32_t max_xfer_len; - /// Optimal transfer length in logical blocks. - uint32_t optimal_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 prefetch length in logical blocks. + uint32_t max_prefetch_len; - /// Maximum UNMAP LBA count in LBAs. - uint32_t max_unmap_lba_cnt; + /// 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; + /// 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; + /// 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; + /// 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; + /// 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). + uint64_t reserved[2]; - /// Reserved for future usage (always MUST be 0). - uint32_t reserved2; + /// 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. @@ -3525,131 +2029,32 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_block_limits_inquiry_ * set. */ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_block_dev_chars_inquiry_data_packet { - /// Medium rotation rate. - uint16_t medium_rotation_rate; + /// Medium rotation rate. + uint16_t medium_rotation_rate; - /// Product type. - uint8_t product_type; + /// Product type. + uint8_t product_type; - /// Flags. - int8_t flags; + /// Flags. + int8_t flags; - /// Support flags. - uint8_t support_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). + uint64_t reserved[6]; - /// Reserved for future usage (always MUST be 0). - uint32_t reserved2; + /// 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). + uint16_t reserved3; - /// Reserved for future usage (always MUST be 0). - uint8_t reserved4; + /// 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 @@ -3707,20 +2112,20 @@ typedef struct __attribute__((packed)) iscsi_scsi_sense_event_data_packet { * all SCSI sense data. */ typedef struct __attribute__((packed)) iscsi_scsi_sense_data_packet { - /// Response code. - int8_t response_code; + /// Response code. + int8_t response_code; - /// Reserved for future usage (always MUST be 0). - uint8_t reserved; + /// Reserved for future usage (always MUST be 0). + uint8_t reserved; - /// Sense key and flags. - int8_t sense_key_flags; + /// Sense key and flags. + int8_t sense_key_flags; - /// Information. - uint32_t info; + /// Information. + uint32_t info; - /// Additional sense length in bytes. - uint8_t add_len; + /// Additional sense length in bytes. + uint8_t add_len; } iscsi_scsi_sense_data_packet; /// iSCSI SCSI maximum sense data length. @@ -3753,26 +2158,26 @@ typedef struct __attribute__((packed)) iscsi_scsi_sense_data_packet { * 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; + /// Basic SCSI sense data packet. + iscsi_scsi_sense_data_packet sense_data; - /// Information. - uint32_t cmd_spec_info; + /// Information. + uint32_t cmd_spec_info; - /// Additional Sense Code (ASC). - uint8_t asc; + /// Additional Sense Code (ASC). + uint8_t asc; - /// Additional Sense Code Qualifier (ASCQ). - uint8_t ascq; + /// Additional Sense Code Qualifier (ASCQ). + uint8_t ascq; - /// Field replaceable unit code. - uint8_t field_rep_unit_code; + /// Field replaceable unit code. + uint32_t field_rep_unit_code; - /// Sense key specific. - uint8_t sense_key_spec_flags; + /// Sense key specific. + uint8_t sense_key_spec_flags; - /// Sense key specific. - uint16_t sense_key_spec; + /// Sense key specific. + uint16_t sense_key_spec; } iscsi_scsi_sense_data_check_cond_packet; @@ -3783,11 +2188,11 @@ typedef struct __attribute__((packed)) iscsi_scsi_sense_data_check_cond_packet { * 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; + /// Last valid Logical Block Address (LBA). + uint32_t lba; - /// Block length in bytes. - uint32_t block_len; + /// Block length in bytes. + uint32_t block_len; } iscsi_scsi_read_capacity_10_parameter_data_packet; @@ -3885,23 +2290,23 @@ typedef struct __attribute__((packed)) iscsi_scsi_read_capacity_10_parameter_dat * 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; + /// Last valid Logical Block Address (LBA). + uint64_t lba; - /// Block length in bytes. - uint32_t block_len; + /// Block length in bytes. + uint32_t block_len; - /// Flags: RC_BASIS, P_TYPE and PROT_EN. - int8_t flags; + /// Flags: RC_BASIS, P_TYPE and PROT_EN. + int8_t flags; - /// P_I_EXPONENT and logical blocks per physical block exponent. - uint8_t exponents; + /// 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; + /// 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]; + /// Reserved for future usage (always MUST be 0 for now). + uint64_t reserved[2]; } iscsi_scsi_service_action_in_16_parameter_data_packet; @@ -3912,49 +2317,24 @@ typedef struct __attribute__((packed)) iscsi_scsi_service_action_in_16_parameter * 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; + /// 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; + /// 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 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; /** @@ -3963,8 +2343,8 @@ typedef struct iscsi_scsi_emu_primary_report_luns_fill { * 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; + /// Vendor specific data. + uint32_t vendor_data; } iscsi_scsi_mode_select_6_parameter_list_packet; @@ -3974,8 +2354,8 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_select_6_parameter_list_p * 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; + /// Vendor specific data. + uint64_t vendor_data; } iscsi_scsi_mode_select_10_parameter_list_packet; @@ -3993,17 +2373,17 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_select_10_parameter_list_ * data. */ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_6_parameter_header_data_packet { - /// Mode data length in bytes. - uint8_t mode_data_len; + /// Mode data length in bytes. + uint8_t mode_data_len; - /// Medium type. - uint8_t medium_type; + /// Medium type. + uint8_t medium_type; - /// Flags. - int8_t flags; + /// Flags. + int8_t flags; - /// Block descriptor length in bytes. - uint8_t block_desc_len; + /// Block descriptor length in bytes. + uint8_t block_desc_len; } iscsi_scsi_mode_sense_6_parameter_header_data_packet; @@ -4025,23 +2405,23 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_6_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; + /// Mode data length in bytes. + uint16_t mode_data_len; - /// Medium type. - uint8_t medium_type; + /// Medium type. + uint8_t medium_type; - /// Flags. - int8_t flags; + /// Flags. + int8_t flags; - /// Long Logical Block Address (LONGLBA). - uint8_t long_lba; + /// Long Logical Block Address (LONGLBA). + uint8_t long_lba; - /// Reserved for future usage (always MUST be 0 for now). - uint8_t reserved; + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved; - /// Block descriptor length in bytes. - uint16_t block_desc_len; + /// Block descriptor length in bytes. + uint16_t block_desc_len; } iscsi_scsi_mode_sense_10_parameter_header_data_packet; @@ -4053,14 +2433,14 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_10_parameter_header * 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; + /// Number of blocks in logical blocks. + uint32_t num_blocks; - /// Reserved for future usage (always MUST be 0 for now). - uint8_t reserved; + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved; - /// Logical blcok length in bytes. - uint8_t block_len[3]; + /// Logical blcok length in bytes. + uint8_t block_len[3]; } iscsi_scsi_mode_sense_lba_parameter_block_desc_data_packet; @@ -4072,14 +2452,14 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_lba_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; + /// Number of blocks in logical blocks. + uint64_t num_blocks; - /// Reserved for future usage (always MUST be 0 for now). - uint32_t reserved; + /// Reserved for future usage (always MUST be 0 for now). + uint32_t reserved; - /// Logical blcok length in bytes. - uint32_t block_len; + /// Logical blcok length in bytes. + uint32_t block_len; } iscsi_scsi_mode_sense_long_lba_parameter_block_desc_data_packet; @@ -4318,14 +2698,14 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_long_lba_parameter_ * 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 code and flags. + int8_t page_code_flags; - /// Page length in bytes. - uint8_t page_len; + /// Page length in bytes. + uint8_t page_len; - /// Mode parameters. - uint8_t params[0]; + /// Mode parameters. + uint8_t params[0]; } iscsi_scsi_mode_sense_mode_page_data_packet; @@ -4335,17 +2715,17 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_mode_page_data_pack * 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; + /// Page code and flags. + int8_t page_code_flags; - /// Sub page code. - uint8_t sub_page_code; + /// Sub page code. + uint8_t sub_page_code; - /// Page length in bytes. - uint16_t page_len; + /// Page length in bytes. + uint16_t page_len; - /// Mode parameters. - uint8_t params[0]; + /// Mode parameters. + uint8_t params[0]; } iscsi_scsi_mode_sense_mode_sub_page_data_packet; @@ -4355,29 +2735,29 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_mode_sub_page_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; + /// Mode page. + iscsi_scsi_mode_sense_mode_page_data_packet mode_page; - /// Flags. - int8_t flags; + /// Flags. + int8_t flags; - /// Read retry count. - uint8_t read_retry_cnt; + /// Read retry count. + uint8_t read_retry_cnt; - /// Obselete. - uint8_t obselete[3]; + /// Obselete. + uint8_t obselete[3]; - /// Restricted for MMC-6. - uint8_t restrict_mmc_6; + /// Restricted for MMC-6. + uint8_t restrict_mmc_6; - /// Write_retry count. - uint8_t write_retry_cnt; + /// Write_retry count. + uint8_t write_retry_cnt; - /// Reserved for future usage (always MUST be 0 for now). - uint8_t reserved; + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved; - /// Recovery time limit. - uint16_t recovery_time_limit; + /// Recovery time limit. + uint16_t recovery_time_limit; } iscsi_scsi_mode_sense_read_write_err_recovery_mode_page_data_packet; @@ -4387,32 +2767,32 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_read_write_err_reco * 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; + /// 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; + /// Reserved for future usage (always MUST be 0 for now). + uint16_t reserved; - /// Bus inactivity time limit. - uint16_t bus_inactivity_time_limit; + /// Bus inactivity time limit. + uint16_t bus_inactivity_time_limit; - /// Reserved for future usage (always MUST be 0 for now). - uint16_t reserved2; + /// Reserved for future usage (always MUST be 0 for now). + uint16_t reserved2; - /// Maximum connect time limit. - uint16_t max_connect_time_limit; + /// Maximum connect time limit. + uint16_t max_connect_time_limit; - /// Maximum burst size. - uint16_t max_burst_size; + /// Maximum burst size. + uint16_t max_burst_size; - /// Restricted. - uint8_t restricted; + /// Restricted. + uint8_t restricted; - /// Reserved for future usage (always MUST be 0 for now). - uint8_t reserved3; + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved3; - /// First burst size. - uint16_t first_burst_size; + /// First burst size. + uint16_t first_burst_size; } iscsi_scsi_mode_sense_disconnect_reconnect_mode_page_data_packet; @@ -4422,35 +2802,35 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_disconnect_reconnec * 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; + /// Mode page. + iscsi_scsi_mode_sense_mode_page_data_packet mode_page; - /// Flags. - int8_t flags; + /// Flags. + int8_t flags; - /// Verify retry count. - uint8_t verify_retry_cnt; + /// Verify retry count. + uint8_t verify_retry_cnt; - /// Obselete. - uint8_t obselete; + /// Obselete. + uint8_t obselete; - /// Head offset count. - uint8_t head_offset_cnt; + /// Head offset count. + uint8_t head_offset_cnt; - /// Data strobe offset count. - uint8_t data_strobe_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; + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved; - /// Write retry count. - uint8_t write_retry_cnt; + /// Write retry count. + uint8_t write_retry_cnt; - /// Reserved for future usage (always MUST be 0 for now). - uint8_t reserved2; + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved2; - /// Verify_recovery time limit. - uint16_t verify_recovery_time_limit; + /// Verify_recovery time limit. + uint16_t verify_recovery_time_limit; } iscsi_scsi_mode_sense_verify_err_recovery_mode_page_data_packet; @@ -4485,41 +2865,41 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_verify_err_recovery * 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; + /// Mode page. + iscsi_scsi_mode_sense_mode_page_data_packet mode_page; - /// Flags. - int8_t flags; + /// Flags. + int8_t flags; - /// Retention priority. - uint8_t retention_pri; + /// Retention priority. + uint8_t retention_pri; - /// Disable prefetch transfer length. - uint16_t disable_prefetch_xfer_len; + /// Disable prefetch transfer length. + uint16_t disable_prefetch_xfer_len; - /// Minimum prefetch. - uint16_t min_prefetch; + /// Minimum prefetch. + uint16_t min_prefetch; - /// Maximum prefetch. - uint16_t max_prefetch; + /// Maximum prefetch. + uint16_t max_prefetch; - /// Maximum prefetch ceiling. - uint16_t max_prefetch_ceil; + /// Maximum prefetch ceiling. + uint16_t max_prefetch_ceil; - /// Cache flags. - int8_t cache_flags; + /// Cache flags. + int8_t cache_flags; - /// Number of cache segments. - uint8_t num_cache_segs; + /// Number of cache segments. + uint8_t num_cache_segs; - /// Cache segment size. - uint16_t cache_seg_size; + /// Cache segment size. + uint16_t cache_seg_size; - /// Reserved for future usage (always MUST be 0 for now). - uint8_t reserved; + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved; - /// Obselete. - uint8_t obselete[3]; + /// Obselete. + uint8_t obselete[3]; } iscsi_scsi_mode_sense_caching_mode_page_data_packet; @@ -4529,29 +2909,29 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_caching_mode_page_d * 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; + /// Mode page. + iscsi_scsi_mode_sense_mode_page_data_packet mode_page; - /// Flags. - int8_t flags; + /// Flags. + int8_t flags; - /// Queue flags. - int8_t queue_flags; + /// Queue flags. + int8_t queue_flags; - /// Control flags. - int8_t control_flags; + /// Control flags. + int8_t control_flags; - /// Application task flags. - int8_t app_task_flags; + /// Application task flags. + int8_t app_task_flags; - /// Ready AER holdoff period. - uint16_t ready_aer_holdoff_period; + /// Ready AER holdoff period. + uint16_t ready_aer_holdoff_period; - /// Busy timeout period. - uint16_t busy_timeout_period; + /// Busy timeout period. + uint16_t busy_timeout_period; - /// Extended self-test completition time. - uint16_t ext_self_test_complete_time; + /// Extended self-test completition time. + uint16_t ext_self_test_complete_time; } iscsi_scsi_mode_sense_control_mode_page_data_packet; @@ -4561,23 +2941,23 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_control_mode_page_d * 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; + /// Mode page. + iscsi_scsi_mode_sense_mode_sub_page_data_packet mode_sub_page; - /// Flags. - int8_t flags; + /// Flags. + int8_t flags; - /// Initial command priority. - uint8_t init_cmd_pri; + /// Initial command priority. + uint8_t init_cmd_pri; - /// Maximum sense data length in bytes. - uint8_t max_sense_data_len; + /// 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). + uint64_t reserved[3]; - /// Reserved for future usage (always MUST be 0 for now). - uint8_t reserved2; + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved2; } iscsi_scsi_mode_sense_control_ext_mode_page_data_packet; @@ -4587,32 +2967,32 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_control_ext_mode_pa * 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; + /// Mode page. + iscsi_scsi_mode_sense_mode_page_data_packet mode_page; - /// Flags. - int8_t flags; + /// Flags. + int8_t flags; - /// Reserved for future usage (always MUST be 0 for now). - uint8_t reserved; + /// 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; + /// 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; + /// Reserved for future usage (always MUST be 0 for now). + uint32_t reserved2; - /// Maximum regenerate size in logical blocks. - uint32_t max_regenerate_size; + /// 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). + uint32_t reserved3; - /// Reserved for future usage (always MUST be 0 for now). - uint16_t reserved4; + /// Reserved for future usage (always MUST be 0 for now). + uint16_t reserved4; - /// Rebuild delay. - uint16_t rebuild_delay; + /// Rebuild delay. + uint16_t rebuild_delay; } iscsi_scsi_mode_sense_xor_ext_mode_page_data_packet; @@ -4622,44 +3002,44 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_xor_ext_mode_page_d * 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; + /// Mode page. + iscsi_scsi_mode_sense_mode_page_data_packet mode_page; - /// Flags. - int8_t flags; + /// Flags. + int8_t flags; - /// Idle and standby flags. - int8_t idle_standby_flags; + /// Idle and standby flags. + int8_t idle_standby_flags; - /// idle_a condition timer. - uint32_t idle_a_cond_timer; + /// idle_a condition timer. + uint32_t idle_a_cond_timer; - /// standby_z condition timer. - uint32_t standby_z_cond_timer; + /// standby_z condition timer. + uint32_t standby_z_cond_timer; - /// idle_b condition timer. - uint32_t idle_b_cond_timer; + /// idle_b condition timer. + uint32_t idle_b_cond_timer; - /// idle_c condition timer. - uint32_t idle_c_cond_timer; + /// idle_c condition timer. + uint32_t idle_c_cond_timer; - /// standby_y condition timer. - uint32_t standby_y_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). + 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). + 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). + uint16_t reserved3; - /// Reserved for future usage (always MUST be 0 for now). - uint8_t reserved4; + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved4; - /// Check Condition From (CCF) flags. - int8_t ccf_flags; + /// Check Condition From (CCF) flags. + int8_t ccf_flags; } iscsi_scsi_mode_sense_power_cond_mode_page_data_packet; @@ -4669,65 +3049,23 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_power_cond_mode_pag * 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; + /// Mode page. + iscsi_scsi_mode_sense_mode_page_data_packet mode_page; - /// Flags. - int8_t flags; + /// Flags. + int8_t flags; - /// Method Of Reporting Informational Exceptions (MRIE) flags. - uint8_t mrie; + /// Method Of Reporting Informational Exceptions (MRIE) flags. + uint8_t mrie; - /// Interval timer. - uint32_t interval_timer; + /// Interval timer. + uint32_t interval_timer; - /// Report count. - uint32_t report_cnt; + /// 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 @@ -4954,86 +3292,80 @@ typedef struct __attribute__((packed)) iscsi_scsi_pr_reserve_in_parameter_data_p * direction is indicated by the R and/or W bit). */ typedef struct __attribute__((packed)) iscsi_scsi_cmd_packet { - /// Always 1 according to the iSCSI specification. - uint8_t opcode; - - /// Flags and Task Attributes. - int8_t flags_task; - - /// Reserved for future usage, MUST always be 0. - uint16_t reserved; - - /// Total length of AHS. - uint8_t total_ahs_len; - - /// Length of DataSegment. - uint8_t ds_len[3]; - - /// SCSI LUN bit mask. - uint64_t lun; - - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; - - /** - * @brief Expected Data Transfer Length. - * - * For unidirectional operations, the Expected Data Transfer Length - * field contains the number of bytes of data involved in this SCSI - * operation. For a unidirectional write operation (W flag set to 1 and - * R flag set to 0), the initiator uses this field to specify the number - * of bytes of data it expects to transfer for this operation. For a - * unidirectional read operation (W flag set to 0 and R flag set to 1), - * the initiator uses this field to specify the number of bytes of data - * it expects the target to transfer to the initiator. It corresponds - * to the SAM-2 byte count.\n - * For bidirectional operations (both R and W flags are set to 1), this - * field contains the number of data bytes involved in the write - * transfer. For bidirectional operations, an additional header segment - * MUST be present in the header sequence that indicates the - * Bidirectional Read Expected Data Transfer Length. The Expected Data - * Transfer Length field and the Bidirectional Read Expected Data - * Transfer Length field correspond to the SAM-2 byte count. - * If the Expected Data Transfer Length for a write and the length of - * the immediate data part that follows the command (if any) are the - * same, then no more data PDUs are expected to follow. In this case, - * the F bit MUST be set to 1.\n - * If the Expected Data Transfer Length is higher than the - * FirstBurstLength (the negotiated maximum amount of unsolicited data - * the target will accept), the initiator MUST send the maximum amount - * of unsolicited data OR ONLY the immediate data, if any. - * Upon completion of a data transfer, the target informs the initiator - * (through residual counts) of how many bytes were actually processed - * (sent and/or received) by the target. - */ - uint32_t exp_xfer_len; - - /// The CmdSN enables ordered delivery across multiple connections in a single session. - uint32_t cmd_sn; - - /// Command responses up to ExpStatSN - 1 (modulo 2**32) have been received (acknowledges status) on the connection. - uint32_t exp_stat_sn; - - /** - * @brief SCSI Command Descriptor Block (CDB). - * - * 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. - */ - iscsi_scsi_cdb scsi_cdb; - - /// Optional AHS packet data. - iscsi_ahs_packet ahs; - - /// Optional header digest. - iscsi_header_digest hdr_digest; - - /// Optional data segment, command data. - iscsi_scsi_ds_cmd_data ds_cmd_data; - - /// Optional data digest. - iscsi_data_digest data_digest; + /// Always 1 according to the iSCSI specification. + uint8_t opcode; + + /// Flags and Task Attributes. + int8_t flags_task; + + /// Reserved for future usage, MUST always be 0. + uint16_t reserved; + + /// Total length of AHS. + uint8_t total_ahs_len; + + /// Length of DataSegment. + uint8_t ds_len[3]; + + /// SCSI LUN bit mask. + uint64_t lun; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /** + * @brief Expected Data Transfer Length. + * + * For unidirectional operations, the Expected Data Transfer Length + * field contains the number of bytes of data involved in this SCSI + * operation. For a unidirectional write operation (W flag set to 1 and + * R flag set to 0), the initiator uses this field to specify the number + * of bytes of data it expects to transfer for this operation. For a + * unidirectional read operation (W flag set to 0 and R flag set to 1), + * the initiator uses this field to specify the number of bytes of data + * it expects the target to transfer to the initiator. It corresponds + * to the SAM-2 byte count.\n + * For bidirectional operations (both R and W flags are set to 1), this + * field contains the number of data bytes involved in the write + * transfer. For bidirectional operations, an additional header segment + * MUST be present in the header sequence that indicates the + * Bidirectional Read Expected Data Transfer Length. The Expected Data + * Transfer Length field and the Bidirectional Read Expected Data + * Transfer Length field correspond to the SAM-2 byte count. + * If the Expected Data Transfer Length for a write and the length of + * the immediate data part that follows the command (if any) are the + * same, then no more data PDUs are expected to follow. In this case, + * the F bit MUST be set to 1.\n + * If the Expected Data Transfer Length is higher than the + * FirstBurstLength (the negotiated maximum amount of unsolicited data + * the target will accept), the initiator MUST send the maximum amount + * of unsolicited data OR ONLY the immediate data, if any. + * Upon completion of a data transfer, the target informs the initiator + * (through residual counts) of how many bytes were actually processed + * (sent and/or received) by the target. + */ + uint32_t exp_xfer_len; + + /// The CmdSN enables ordered delivery across multiple connections in a single session. + uint32_t cmd_sn; + + /// Command responses up to ExpStatSN - 1 (modulo 2**32) have been received (acknowledges status) on the connection. + uint32_t exp_stat_sn; + + /** + * @brief SCSI Command Descriptor Block (CDB). + * + * 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. + */ + iscsi_scsi_cdb scsi_cdb; + + /// Optional AHS packet data. + iscsi_ahs_packet ahs; + + /// Optional data segment, command data. + iscsi_scsi_ds_cmd_data ds_cmd_data; } iscsi_scsi_cmd_packet; @@ -5234,496 +3566,132 @@ typedef struct __attribute__((packed)) iscsi_scsi_cmd_packet { * terms, response value 0x00 maps to the SCSI service response (see */ typedef struct __attribute__((packed)) iscsi_scsi_response_packet { - /// Always 0x21 according to specification. - uint8_t opcode; - - /// Flags. - int8_t flags; - - /// This field contains the iSCSI service response. - uint8_t response; - - /// The Status field is used to report the SCSI status of the command (as specified in SAM2) and is only valid if the response code is Command Completed at Target. - uint8_t status; - - /// Total AHS length. - uint8_t total_ahs_len; - - /// Data segment length. - uint8_t ds_len[3]; - - /// Reserved for future usage. Always MUST be 0. - uint64_t reserved; - - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; - - /** - * @brief Copy of the last accepted Selective Negative / Sequence Number Acknowledgment (SNACK) tag. - * - * This field contains a copy of the SNACK Tag of the last SNACK Tag - * accepted by the target on the same connection and for the command for - * which the response is issued. Otherwise, it is reserved and should - * be set to 0.\n - * After issuing a R-Data SNACK, the initiator must discard any SCSI - * status unless contained in a SCSI Response PDU carrying the same - * SNACK Tag as the last issued R-Data SNACK for the SCSI command on the - * current connection. - */ - uint32_t snack_tag; - - /** - * @brief StatSN - Status Sequence Number. - * - * The StatSN is a sequence number that the target iSCSI layer generates - * per connection and that in turn enables the initiator to acknowledge - * status reception. The StatSN is incremented by 1 for every - * response/status sent on a connection, except for responses sent as a - * result of a retry or SNACK. In the case of responses sent due to a - * retransmission request, the StatSN MUST be the same as the first time - * the PDU was sent, unless the connection has since been restarted. - */ - uint32_t stat_sn; - - /** - * @brief ExpCmdSN - Next Expected CmdSN from This Initiator. - * - * The ExpCmdSN is a sequence number that the target iSCSI returns to - * the initiator to acknowledge command reception. It is used to update - * a local variable with the same name. An ExpCmdSN equal to - * MaxCmdSN + 1 indicates that the target cannot accept new commands. - */ - uint32_t exp_cmd_sn; - - /** - * @brief MaxCmdSN - Maximum CmdSN from This Initiator. - * - * The MaxCmdSN is a sequence number that the target iSCSI returns to - * the initiator to indicate the maximum CmdSN the initiator can send. - * It is used to update a local variable with the same name. If the - * MaxCmdSN is equal to ExpCmdSN - 1, this indicates to the initiator - * that the target cannot receive any additional commands. When the - * MaxCmdSN changes at the target while the target has no pending PDUs - * to convey this information to the initiator, it MUST generate a - * NOP-In to carry the new MaxCmdSN. - */ - uint32_t max_cmd_sn; - - /** - * @brief ExpDataSN or Reserved. - * - * This field indicates the number of Data-In (read) PDUs the target has - * sent for the command.\n - * This field MUST be 0 if the response code is not Command Completed at - * Target or the target sent no Data-In PDUs for the command. - */ - uint32_t exp_data_sn; - - /** - * @brief Bidirectional Read Residual Count or Reserved. - * - * The Bidirectional Read Residual Count field MUST be valid in the case - * where either the u bit or the o bit is set. If neither bit is set, - * the Bidirectional Read Residual Count field is reserved. Targets may - * set the Bidirectional Read Residual Count, and initiators may use it - * when the response code is Command Completed at Target. If the o bit - * is set, the Bidirectional Read Residual Count indicates the number of - * bytes that were not transferred to the initiator because the - * initiator's Bidirectional Read Expected Data Transfer Length was not - * sufficient. If the u bit is set, the Bidirectional Read Residual - * Count indicates the number of bytes that were not transferred to the - * initiator out of the number of bytes expected to be transferred. - */ - uint32_t bidi_read_res_cnt; - - /** - * @brief Residual Count or Reserved. - * - * The Residual Count field MUST be valid in the case where either the U - * bit or the O bit is set. If neither bit is set, the Residual Count - * field MUST be ignored on reception and SHOULD be set to 0 when - * sending. Targets may set the residual count, and initiators may use - * it when the response code is Command Completed at Target (even if the - * status returned is not GOOD). If the O bit is set, the Residual - * Count indicates the number of bytes that were not transferred because - * the initiator's Expected Data Transfer Length was not sufficient. If - * the U bit is set, the Residual Count indicates the number of bytes - * that were not transferred out of the number of bytes expected to be - * transferred. - */ - uint32_t res_cnt; - - /// Optional header digest. - iscsi_header_digest hdr_digest; - - /// Optional data segment, command data. - iscsi_scsi_ds_cmd_data ds_cmd_data; - - /// Optional data digest. - iscsi_data_digest data_digest; + /// Always 0x21 according to specification. + uint8_t opcode; + + /// Flags. + int8_t flags; + + /// This field contains the iSCSI service response. + uint8_t response; + + /// The Status field is used to report the SCSI status of the command (as specified in SAM2) and is only valid if the response code is Command Completed at Target. + uint8_t status; + + /// Total AHS length. + uint8_t total_ahs_len; + + /// Data segment length. + uint8_t ds_len[3]; + + /// Reserved for future usage. Always MUST be 0. + uint64_t reserved; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /** + * @brief Copy of the last accepted Selective Negative / Sequence Number Acknowledgment (SNACK) tag. + * + * This field contains a copy of the SNACK Tag of the last SNACK Tag + * accepted by the target on the same connection and for the command for + * which the response is issued. Otherwise, it is reserved and should + * be set to 0.\n + * After issuing a R-Data SNACK, the initiator must discard any SCSI + * status unless contained in a SCSI Response PDU carrying the same + * SNACK Tag as the last issued R-Data SNACK for the SCSI command on the + * current connection. + */ + uint32_t snack_tag; + + /** + * @brief StatSN - Status Sequence Number. + * + * The StatSN is a sequence number that the target iSCSI layer generates + * per connection and that in turn enables the initiator to acknowledge + * status reception. The StatSN is incremented by 1 for every + * response/status sent on a connection, except for responses sent as a + * result of a retry or SNACK. In the case of responses sent due to a + * retransmission request, the StatSN MUST be the same as the first time + * the PDU was sent, unless the connection has since been restarted. + */ + uint32_t stat_sn; + + /** + * @brief ExpCmdSN - Next Expected CmdSN from This Initiator. + * + * The ExpCmdSN is a sequence number that the target iSCSI returns to + * the initiator to acknowledge command reception. It is used to update + * a local variable with the same name. An ExpCmdSN equal to + * MaxCmdSN + 1 indicates that the target cannot accept new commands. + */ + uint32_t exp_cmd_sn; + + /** + * @brief MaxCmdSN - Maximum CmdSN from This Initiator. + * + * The MaxCmdSN is a sequence number that the target iSCSI returns to + * the initiator to indicate the maximum CmdSN the initiator can send. + * It is used to update a local variable with the same name. If the + * MaxCmdSN is equal to ExpCmdSN - 1, this indicates to the initiator + * that the target cannot receive any additional commands. When the + * MaxCmdSN changes at the target while the target has no pending PDUs + * to convey this information to the initiator, it MUST generate a + * NOP-In to carry the new MaxCmdSN. + */ + uint32_t max_cmd_sn; + + /** + * @brief ExpDataSN or Reserved. + * + * This field indicates the number of Data-In (read) PDUs the target has + * sent for the command.\n + * This field MUST be 0 if the response code is not Command Completed at + * Target or the target sent no Data-In PDUs for the command. + */ + uint32_t exp_data_sn; + + /** + * @brief Bidirectional Read Residual Count or Reserved. + * + * The Bidirectional Read Residual Count field MUST be valid in the case + * where either the u bit or the o bit is set. If neither bit is set, + * the Bidirectional Read Residual Count field is reserved. Targets may + * set the Bidirectional Read Residual Count, and initiators may use it + * when the response code is Command Completed at Target. If the o bit + * is set, the Bidirectional Read Residual Count indicates the number of + * bytes that were not transferred to the initiator because the + * initiator's Bidirectional Read Expected Data Transfer Length was not + * sufficient. If the u bit is set, the Bidirectional Read Residual + * Count indicates the number of bytes that were not transferred to the + * initiator out of the number of bytes expected to be transferred. + */ + uint32_t bidi_read_res_cnt; + + /** + * @brief Residual Count or Reserved. + * + * The Residual Count field MUST be valid in the case where either the U + * bit or the O bit is set. If neither bit is set, the Residual Count + * field MUST be ignored on reception and SHOULD be set to 0 when + * sending. Targets may set the residual count, and initiators may use + * it when the response code is Command Completed at Target (even if the + * status returned is not GOOD). If the O bit is set, the Residual + * Count indicates the number of bytes that were not transferred because + * the initiator's Expected Data Transfer Length was not sufficient. If + * the U bit is set, the Residual Count indicates the number of bytes + * that were not transferred out of the number of bytes expected to be + * transferred. + */ + uint32_t res_cnt; + + /// Optional data segment, command data. + iscsi_scsi_ds_cmd_data ds_cmd_data; } iscsi_scsi_response_packet; - -/// Task management request function: ABORT TASK: aborts the task identified by the Referenced Task Tag field. -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_ABORT_TASK 0x01 - -/// Task management request function: ABORT TASK SET: aborts all tasks issued via this session on the LU. -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_ABORT_TASK_SET 0x02 - -/// Task management request function: CLEAR ACA - clears the Auto Contingent Allegiance condition. -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_CLEAR_ACA 0x03 - -/// Task management request function: CLEAR TASK SET - aborts all tasks in the appropriate task set as defined by the TST field in the Control mode page (see SPC3). -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_CLEAR_TASK_SET 0x04 - -/// Task management request function: LOGICAL UNIT RESET. -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_LOGICAL_UNIT_RESET 0x05 - -/// Task management request function: TARGET WARM RESET. -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_TARGET_WARM_RESET 0x06 - -/// Task management request function: TARGET COLD RESET. -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_TARGET_COLD_RESET 0x07 - -/// Task management request function: TASK REASSIGN - reassigns connection allegiance for the task identified by the Initiator Task Tag field to this connection, thus resuming the iSCSI exchanges for the task. -#define ISCSI_TASK_MGMT_FUNC_REQ_FUNC_TASK_REASSIGN 0x08 - - -/** - * @brief iSCSI Task Management Function Request packet data. - * - * This structure is used to explicity control the execution of one - * or more tasks (iSCSI and SCSI). - */ -typedef struct __attribute__((packed)) iscsi_task_mgmt_func_req_packet { - /// Always 2 according to iSCSI specification. - uint8_t opcode; - - /** - * @brief Function. - * - * The task management functions provide an initiator with a way to - * explicitly control the execution of one or more tasks (SCSI and iSCSI - * tasks). The task management function codes are listed below. For a - * more detailed description of SCSI task management, see SAM2. - */ - int8_t func; - - /// Reserved fot future usage, always MUST be 0. - uint16_t reserved; - - /// TotalAHSLength (MUST be 0 for this PDU). - uint8_t total_ahs_len; - - /// DataSegmentLength (MUST be 0 for this PDU). - uint8_t ds_len[3]; - - /** - * @brief Logical Unit Number (LUN) or Reserved. - * - * This field is required for functions that address a specific LU - * (ABORT TASK, CLEAR TASK SET, ABORT TASK SET, CLEAR ACA, LOGICAL UNIT - * RESET) and is reserved in all others - */ - uint64_t lun; - - /** - * @brief Initiator Task Tag (ITT). - * - * This is the Initiator Task Tag of the task to be aborted for the - * ABORT TASK function or reassigned for the TASK REASSIGN function. - * For all the other functions, this field MUST be set to the reserved - * value 0xFFFFFFFF. - */ - uint32_t init_task_tag; - - /// Referenced task tag or 0xFFFFFFFF. - uint32_t ref_task_tag; - - /// CmdSN. - uint32_t cmd_sn; - - /// ExpStatSN - uint32_t exp_stat_sn; - - /** - * @brief RefCmdSN or Reserved. - * - * If an ABORT TASK is issued for a task created by an immediate - * command, then the RefCmdSN MUST be that of the task management - * request itself (i.e., the CmdSN and RefCmdSN are equal).\n - * For an ABORT TASK of a task created by a non-immediate command, the - * RefCmdSN MUST be set to the CmdSN of the task identified by the - * Referenced Task Tag field. Targets must use this field when the task - * identified by the Referenced Task Tag field is not with the target. - * Otherwise, this field is reserved. - */ - uint32_t ref_cmd_sn; - - /** - * @brief ExpDataSN or Reserved. - * - * For recovery purposes, the iSCSI target and initiator maintain a data - * acknowledgment reference number - the first input DataSN number - * unacknowledged by the initiator. When issuing a new command, this - * number is set to 0. If the function is TASK REASSIGN, which - * establishes a new connection allegiance for a previously issued read - * or bidirectional command, the ExpDataSN will contain an updated data - * acknowledgment reference number or the value 0; the latter indicates - * that the data acknowledgment reference number is unchanged. The - * initiator MUST discard any data PDUs from the previous execution that - * it did not acknowledge, and the target MUST transmit all Data-In PDUs - * (if any) starting with the data acknowledgment reference number. The - * number of retransmitted PDUs may or may not be the same as the - * original transmission, depending on if there was a change in - * MaxRecvDataSegmentLength in the reassignment. The target MAY also - * send no more Data-In PDUs if all data has been acknowledged. - * The value of ExpDataSN MUST be 0 or higher than the DataSN of the - * last acknowledged Data-In PDU, but not larger than DataSN + 1 of the - * last Data-IN PDU sent by the target. Any other value MUST be ignored - * by the target. - * For other functions, this field is reserved - */ - uint32_t exp_data_sn; - - /// Reserved for future usage, always MUST be 0. - uint64_t reserved2; - - /// Optional header digest. - iscsi_header_digest hdr_digest; -} iscsi_task_mgmt_func_req_packet; - - -/// Task management function response: Function complete. -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_COMPLETE 0x00 - -/// Task management function response: Task does not exist. -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_NO_EXIST 0x01 - -/// Task management function response: LUN does not exist. -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_LUN_NO_EXIST 0x02 - -/// Task management function response: Task still allegiant. -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_ALLEGIANT 0x03 - -/// Task management function response: Task allegiance reassignment not supported. -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_UNSUPPORTED_ALLEGIANCE 0x04 - -/// Task management function response: Task management function not supported. -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_TASK_UNSUPPORTED_MGMT 0x05 - -/// Task management function response: Function authorization failed. -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_AUTH_FAILED 0x06 - -/// Task management function response: Function rejected. -#define ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_REJECTED 0xFF - - -/** - * @brief iSCSI Task Management Function Response packet data. - * - * For the functions ABORT TASK, ABORT TASK SET, CLEAR ACA, CLEAR TASK - * SET, LOGICAL UNIT RESET, TARGET COLD RESET, TARGET WARM RESET, and - * TASK REASSIGN, the target performs the requested task management - * function and sends a task management response back to the initiator. - * For TASK REASSIGN, the new connection allegiance MUST ONLY become - * effective at the target after the target issues the task management - * response. - */ -typedef struct __attribute__((packed)) iscsi_task_mgmt_func_response_packet { - /// Always 0x22 according to specification. - uint8_t opcode; - - /// Reserved for future usage (always MUST be 0x80 for now). - int8_t flags; - - /** - * @brief Function response. - * - * For the TARGET COLD RESET and TARGET WARM RESET functions, the target - * cancels all pending operations across all LUs known to the issuing - * initiator. For the TARGET COLD RESET function, the target MUST then - * close all of its TCP connections to all initiators (terminates all - * sessions).\n - * The mapping of the response code into a SCSI service response code - * value, if needed, is outside the scope of this document. However, in - * symbolic terms, Response values 0 and 1 map to the SCSI service - * response of FUNCTION COMPLETE. Response value 2 maps to the SCSI - * service response of INCORRECT LOGICAL UNIT NUMBER. All other - * Response values map to the SCSI service response of FUNCTION - * REJECTED. If a Task Management Function Response PDU does not arrive - * before the session is terminated, the SCSI service response is - * SERVICE DELIVERY OR TARGET FAILURE.\n - * The response to ABORT TASK SET and CLEAR TASK SET MUST only be issued - * by the target after all of the commands affected have been received - * by the target, the corresponding task management functions have been - * executed by the SCSI target, and the delivery of all responses - * delivered until the task management function completion has been - * confirmed (acknowledged through the ExpStatSN) by the initiator on - * all connections of this session.\n - * For the ABORT TASK function,\n - * -# if the Referenced Task Tag identifies a valid task leading to a - * successful termination, then targets must return the "Function - * complete" response. - * -# if the Referenced Task Tag does not identify an existing task - * but the CmdSN indicated by the RefCmdSN field in the Task - * Management Function Request is within the valid CmdSN window - * and less than the CmdSN of the Task Management Function Request - * itself, then targets must consider the CmdSN as received and - * return the "Function complete" response. - * -# if the Referenced Task Tag does not identify an existing task - * and the CmdSN indicated by the RefCmdSN field in the Task - * Management Function Request is outside the valid CmdSN window, - * then targets must return the "Task does not exist" response - */ - uint8_t response; - - /// Reserved for future usage, always MUST be 0. - uint8_t reserved; - - /// TotalAHSLength (MUST be 0 for this PDU). - uint8_t total_ahs_len; - - /// DataSegmentLength (MUST be 0 for this PDU). - uint8_t ds_len[3]; - - /// Reserved for future usage, always MUST be 0. - uint64_t reserved2; - - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; - - /// Reserved for future usage, always MUST be 0. - uint32_t reserved3; - - /// StatSN. - uint32_t stat_sn; - - /// ExpCmdSN. - uint32_t exp_cmd_sn; - - /// MaxCmdSN. - uint32_t max_cmd_sn; - - /// Reserved for future usage, always MUST be 0. - uint32_t reserved4; - - /// Reserved for future usage, always MUST be 0. - uint64_t reserved5; - - /// Optional header digest. - iscsi_header_digest hdr_digest; -} iscsi_task_mgmt_func_response_packet; - /// SCSI data out / in flags: Immediately process transfer. #define ISCSI_SCSI_DATA_OUT_DATA_IN_FLAGS_IMMEDIATE (1 << 7) -/** - * @brief iSCSI SCSI Data Out request packet data. - * - * THis structure is used by iSCSI for SCSI data output - * requests, i.e. write operations. - */ -typedef struct __attribute__((packed)) iscsi_scsi_data_out_req_packet { - /// Always 2 according to iSCSI specification. - uint8_t opcode; - - /// Flags. - int8_t flags; - - /// Reserved for future usage, always MUST be 0. - uint16_t reserved; - - /// TotalAHSLength. - uint8_t total_ahs_len; - - /** - * @brief DataSegmentLength. - * - * This is the data payload length of a SCSI Data-In or SCSI Data-Out - * PDU. The sending of 0-length data segments should be avoided, but - * initiators and targets MUST be able to properly receive 0-length data - * segments.\n - * The data segments of Data-In and Data-Out PDUs SHOULD be filled to - * the integer number of 4-byte words (real payload), unless the F bit - * is set to 1. - */ - uint8_t ds_len[3]; - - /** - * @brief Logical Unit Number (LUN) or Reserved. - * - * If the Target Transfer Tag is provided, then the LUN field MUST hold a - * valid value and be consistent with whatever was specified with the command; - * otherwise, the LUN field is reserved. - */ - uint64_t lun; - - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; - - /** - * @brief Target Transfer Tag or 0xFFFFFFFF. - * - * On outgoing data, the Target Transfer Tag is provided to the target - * if the transfer is honoring an R2T. In this case, the Target - * Transfer Tag field is a replica of the Target Transfer Tag provided - * with the R2T.\n - * The Target Transfer Tag values are not specified by this protocol, - * except that the value 0xFFFFFFFF is reserved and means that the - * Target Transfer Tag is not supplied. - */ - uint32_t target_xfer_tag; - - /// Reserved for future usage, always MUST be 0. - uint32_t reserved2; - - /// ExpStatSN. - uint32_t exp_stat_sn; - - /// Reserved for future usage, always MUST be 0. - uint32_t reserved3; - - /** - * @brief DataSN. - * - * For output (write) data PDUs, the DataSN is the Data-Out PDU number - * within the current output sequence. Either the current output - * sequence is identified by the Initiator Task Tag (for unsolicited - * data) or it is a data sequence generated for one R2T (for data - * solicited through R2T). - */ - uint32_t data_sn; - - /** - * @brief Buffer Offset. - * - * The Buffer Offset field contains the offset of this PDU payload data - * within the complete data transfer. The sum of the buffer offset and - * length should not exceed the expected transfer length for the - * command.\n - * The order of data PDUs within a sequence is determined by - * DataPDUInOrder. When set to Yes, it means that PDUs have to be in - * increasing buffer offset order and overlays are forbidden.\n - * The ordering between sequences is determined by DataSequenceInOrder. - * When set to Yes, it means that sequences have to be in increasing - * buffer offset order and overlays are forbidden. - */ - uint32_t buf_offset; - - /// Reserved for future usage, always MUST be 0. - uint32_t reserved4; - - /// Optional header digest. - iscsi_header_digest hdr_digest; - - /// Data segment. - iscsi_scsi_ds_cmd_data ds_cmd_data; - - /// Optional data digest. - iscsi_data_digest data_digest; -} iscsi_scsi_data_out_req_packet; - /** * @brief SCSI Data In reponse flags: Status. * @@ -5822,436 +3790,114 @@ typedef struct __attribute__((packed)) iscsi_scsi_data_out_req_packet { * responses, i.e. read operations. */ typedef struct __attribute__((packed)) iscsi_scsi_data_in_response_packet { - /// Always 0x25 according to iSCSI specification. - uint8_t opcode; - - /// Incoming data flags. The fields StatSN, Status, and Residual Count only have meaningful content if the S bit is set to 1. - int8_t flags; - - /// Rserved for future usage, always MUST be 0. - uint8_t reserved; - - /** - * @brief Status or Reserved. - * - * Status can accompany the last Data-In PDU if the command did not end - * with an exception (i.e., the status is "good status" - GOOD, - * CONDITION MET, or INTERMEDIATE-CONDITION MET). The presence of - * status (and of a residual count) is signaled via the S flag bit. - * Although targets MAY choose to send even non-exception status in - * separate responses, initiators MUST support non-exception status in - * Data-In PDUs. - */ - uint8_t status; - - /// TotalAHSLength. - uint8_t total_ahs_len; - - /** - * @brief DataSegmentLength. - * - * This is the data payload length of a SCSI Data-In or SCSI Data-Out - * PDU. The sending of 0-length data segments should be avoided, but - * initiators and targets MUST be able to properly receive 0-length data - * segments.\n - * The data segments of Data-In and Data-Out PDUs SHOULD be filled to - * the integer number of 4-byte words (real payload), unless the F bit - * is set to 1. - */ - uint8_t ds_len[3]; - - /** - * @brief Logical Unit Number (LUN) or Reserved. - * - * If the Target Transfer Tag is provided, then the LUN field MUST hold a - * valid value and be consistent with whatever was specified with the command; - * otherwise, the LUN field is reserved. - */ - uint64_t lun; - - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; - - /** - * @brief Target Transfer Tag or 0xFFFFFFFF. - * - * On incoming data, the Target Transfer Tag and LUN MUST be provided by - * the target if the A bit is set to 1; otherwise, they are reserved. - * The Target Transfer Tag and LUN are copied by the initiator into the - * SNACK of type DataACK that it issues as a result of receiving a SCSI - * Data-In PDU with the A bit set to 1.\n - * The Target Transfer Tag values are not specified by this protocol, - * except that the value 0xFFFFFFFF is reserved and means that the - * Target Transfer Tag is not supplied. - */ - uint32_t target_xfer_tag; - - /// StatSN. - uint32_t stat_sn; - - /// ExpCmdSN. - - uint32_t exp_cmd_sn; - - /// MaxCmdSN. - uint32_t max_cmd_sn; - - /** - * @brief DataSN. - * - * For input (read) or bidirectional Data-In PDUs, the DataSN is the - * input PDU number within the data transfer for the command identified - * by the Initiator Task Tag.\n - * R2T and Data-In PDUs, in the context of bidirectional commands, share - * the numbering sequence. - */ - uint32_t data_sn; - - /** - * @brief Buffer Offset. - * - * The Buffer Offset field contains the offset of this PDU payload data - * within the complete data transfer. The sum of the buffer offset and - * length should not exceed the expected transfer length for the - * command.\n - * The order of data PDUs within a sequence is determined by - * DataPDUInOrder. When set to Yes, it means that PDUs have to be in - * increasing buffer offset order and overlays are forbidden.\n - * The ordering between sequences is determined by DataSequenceInOrder. - * When set to Yes, it means that sequences have to be in increasing - * buffer offset order and overlays are forbidden. - */ - uint32_t buf_offset; - - /// Residual Count or Reserved. - uint32_t res_cnt; - - /// Optional header digest. - iscsi_header_digest hdr_digest; - - /// Data segment. - iscsi_scsi_ds_cmd_data ds_cmd_data; - - /// Optional data digest. - iscsi_data_digest data_digest; + /// Always 0x25 according to iSCSI specification. + uint8_t opcode; + + /// Incoming data flags. The fields StatSN, Status, and Residual Count only have meaningful content if the S bit is set to 1. + int8_t flags; + + /// Rserved for future usage, always MUST be 0. + uint8_t reserved; + + /** + * @brief Status or Reserved. + * + * Status can accompany the last Data-In PDU if the command did not end + * with an exception (i.e., the status is "good status" - GOOD, + * CONDITION MET, or INTERMEDIATE-CONDITION MET). The presence of + * status (and of a residual count) is signaled via the S flag bit. + * Although targets MAY choose to send even non-exception status in + * separate responses, initiators MUST support non-exception status in + * Data-In PDUs. + */ + uint8_t status; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /** + * @brief DataSegmentLength. + * + * This is the data payload length of a SCSI Data-In or SCSI Data-Out + * PDU. The sending of 0-length data segments should be avoided, but + * initiators and targets MUST be able to properly receive 0-length data + * segments.\n + * The data segments of Data-In and Data-Out PDUs SHOULD be filled to + * the integer number of 4-byte words (real payload), unless the F bit + * is set to 1. + */ + uint8_t ds_len[3]; + + /** + * @brief Logical Unit Number (LUN) or Reserved. + * + * If the Target Transfer Tag is provided, then the LUN field MUST hold a + * valid value and be consistent with whatever was specified with the command; + * otherwise, the LUN field is reserved. + */ + uint64_t lun; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /** + * @brief Target Transfer Tag or 0xFFFFFFFF. + * + * On incoming data, the Target Transfer Tag and LUN MUST be provided by + * the target if the A bit is set to 1; otherwise, they are reserved. + * The Target Transfer Tag and LUN are copied by the initiator into the + * SNACK of type DataACK that it issues as a result of receiving a SCSI + * Data-In PDU with the A bit set to 1.\n + * The Target Transfer Tag values are not specified by this protocol, + * except that the value 0xFFFFFFFF is reserved and means that the + * Target Transfer Tag is not supplied. + */ + uint32_t target_xfer_tag; + + /// StatSN. + uint32_t stat_sn; + + /// ExpCmdSN. + + uint32_t exp_cmd_sn; + + /// MaxCmdSN. + uint32_t max_cmd_sn; + + /** + * @brief DataSN. + * + * For input (read) or bidirectional Data-In PDUs, the DataSN is the + * input PDU number within the data transfer for the command identified + * by the Initiator Task Tag.\n + * R2T and Data-In PDUs, in the context of bidirectional commands, share + * the numbering sequence. + */ + uint32_t data_sn; + + /** + * @brief Buffer Offset. + * + * The Buffer Offset field contains the offset of this PDU payload data + * within the complete data transfer. The sum of the buffer offset and + * length should not exceed the expected transfer length for the + * command.\n + * The order of data PDUs within a sequence is determined by + * DataPDUInOrder. When set to Yes, it means that PDUs have to be in + * increasing buffer offset order and overlays are forbidden.\n + * The ordering between sequences is determined by DataSequenceInOrder. + * When set to Yes, it means that sequences have to be in increasing + * buffer offset order and overlays are forbidden. + */ + uint32_t buf_offset; + + /// Residual Count or Reserved. + uint32_t res_cnt; + + /// Data segment. + iscsi_scsi_ds_cmd_data ds_cmd_data; } iscsi_scsi_data_in_response_packet; -/** - * @brief iSCSI Ready To Transfer packet data. - * - * When an initiator has submitted a SCSI command with data that passes - * from the initiator to the target (write), the target may specify - * which blocks of data it is ready to receive. The target may request - * that the data blocks be delivered in whichever order is convenient - * for the target at that particular instant. This information is - * passed from the target to the initiator in the Ready To Transfer - * (R2T) PDU. - * - * In order to allow write operations without an explicit initial R2T, - * the initiator and target MUST have negotiated the key InitialR2T to - * No during login. - * - * An R2T MAY be answered with one or more SCSI Data-Out PDUs with a - * matching Target Transfer Tag. If an R2T is answered with a single - * Data-Out PDU, the buffer offset in the data PDU MUST be the same as - * the one specified by the R2T, and the data length of the data PDU - * MUST be the same as the Desired Data Transfer Length specified in the - * R2T. If the R2T is answered with a sequence of data PDUs, the buffer - * offset and length MUST be within the range of those specified by the - * R2T, and the last PDU MUST have the F bit set to 1. If the last PDU - * (marked with the F bit) is received before the Desired Data Transfer - * Length is transferred, a target MAY choose to reject that PDU with - * the "Protocol Error" reason code. DataPDUInOrder governs the - * Data-Out PDU ordering. If DataPDUInOrder is set to Yes, the buffer - * offsets and lengths for consecutive PDUs MUST form a continuous - * non-overlapping range, and the PDUs MUST be sent in increasing offset - * order. - * - * The target may send several R2T PDUs. It therefore can have a number - * of pending data transfers. The number of outstanding R2T PDUs is - * limited by the value of the negotiated key MaxOutstandingR2T. Within - * a task, outstanding R2Ts MUST be fulfilled by the initiator in the - * order in which they were received. - * - * R2T PDUs MAY also be used to recover Data-Out PDUs. Such an R2T - * (Recovery-R2T) is generated by a target upon detecting the loss of - * one or more Data-Out PDUs due to: - * - * - Digest error - * - * - Sequence error - * - * - Sequence reception timeout - * - * A Recovery-R2T carries the next unused R2TSN but requests part of or - * the entire data burst that an earlier R2T (with a lower R2TSN) had - * already requested. - * - * DataSequenceInOrder governs the buffer offset ordering in consecutive - * R2Ts. If DataSequenceInOrder is Yes, then consecutive R2Ts MUST - * refer to continuous non-overlapping ranges, except for Recovery-R2Ts. - */ -typedef struct __attribute__((packed)) iscsi_r2t_packet { - /// Always 0x31 according to iSCSI specification. - uint8_t opcode; - - /// Reserved for future usage (always MUST be 0x80 for now). - int8_t flags; - - /// Reserved for future usage, always MUST be 0 for now. - uint16_t reserved; - - /// TotalAHSLength, MUST be 0 for this PDU. - uint8_t total_ahs_len; - - /// DataSegmentLength, MUST be 0 0 for this PDU. - uint8_t ds_len[3]; - - /// Logical Unit Number (LUN) or Reserved. - uint64_t lun; - - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; - - /// Target Transfer Tag (TTT). - uint32_t target_xfer_tag; - - /// The StatSN field will contain the next StatSN. The StatSN for this connection is not advanced after this PDU is sent. - uint32_t stat_sn; - - /// ExpCmdSN. - uint32_t exp_cmd_sn; - - /// MaxCmdSN. - uint32_t max_cmd_sn; - - /// DataSN. - uint32_t data_sn; - - /// Ready To Transfer Sequence Number (R2TSN) is the R2T PDU input PDU number within the command identified by the Initiator Task Tag. For bidirectional commands, R2T and Data-In PDUs share the input PDU numbering sequence. - uint32_t r2t_sn; - - /** - * @brief Buffer Offset. - * - * The target therefore also specifies a buffer offset that indicates - * the point at which the data transfer should begin, relative to the - * beginning of the total data transfer. - */ - uint32_t buf_offset; - - /** - * @brief Desired Data Transfer Length. - * - * The target specifies how many bytes it wants the initiator to send - * because of this R2T PDU. The target may request the data from the - * initiator in several chunks, not necessarily in the original order of - * the data. The Desired Data Transfer Length MUST NOT be 0 and MUST NOT - * exceed MaxBurstLength. - */ - uint32_t des_data_xfer_len; - - /// Optional header digest. - iscsi_header_digest hdr_digest; -} iscsi_r2t_packet; - - -/** - * @brief SCSI Asynchronous Message Event: SCSI Async Event. - * - * A SCSI asynchronous event is reported in the sense data. - * Sense Data that accompanies the report, in the data - * segment, identifies the condition. The sending of a - * SCSI event ("asynchronous event reporting" in SCSI - * terminology) is dependent on the target support for SCSI - * asynchronous event reporting as indicated in the - * standard INQUIRY data. Its use may be enabled by - * parameters in the SCSI Control mode page. - */ -#define ISCSI_ASYNC_MSG_EVENT_SCSI_ASYNC_EVENT 0x00 - -/** - * @brief SCSI Asynchronous Message Event: Logout Request. - * - * The target requests Logout. This Async Message MUST - * be sent on the same connection as the one requesting - * to be logged out. The initiator MUST honor this request - * by issuing a Logout as early as possible but no later - * than Parameter3 seconds. The initiator MUST send a Logout - * with a reason code of "close the connection" OR "close the - * session" to close all the connections. Once this message is - * received, the initiator SHOULD NOT issue new iSCSI commands on - * the connection to be logged out. The target MAY reject any - * new I/O requests that it receives after this message with the - * reason code "Waiting for Logout". If the initiator does not - * log out in Parameter3 seconds, the target should send an Async - * PDU with iSCSI event code "Dropped the connection" if possible - * or simply terminate the transport connection. Parameter1 and - * Parameter2 are reserved. - */ -#define ISCSI_ASYNC_MSG_EVENT_LOGOUT_REQUEST 0x01 - -/** - * @brief SCSI Asynchronous Message Event: Connection Drop Notification. - * - * The target indicates that it will drop the connection. - * The Parameter1 field indicates the CID of the connection that - * is going to be dropped.\n - * The Parameter2 field (Time2Wait) indicates, in seconds, the - * minimum time to wait before attempting to reconnect or - * reassign.\n - * The Parameter3 field (Time2Retain) indicates the maximum time - * allowed to reassign commands after the initial wait (in - * Parameter2).\n - * If the initiator does not attempt to reconnect and/or reassign - * the outstanding commands within the time specified by - * Parameter3, or if Parameter3 is 0, the target will terminate - * all outstanding commands on this connection. In this case, no - * other responses should be expected from the target for the - * outstanding commands on this connection.\n - * A value of 0 for Parameter2 indicates that reconnect can be - * attempted immediately. - */ -#define ISCSI_ASYNC_MSG_EVENT_CONNECT_DROP_NOTIFY 0x02 - -/** - * @brief SCSI Asynchronous Message Event: Session Drop Notification. - * - * The target indicates that it will drop all the connections - * of this session.\n - * The Parameter1 field is reserved.\n - * The Parameter2 field (Time2Wait) indicates, in seconds, the - * minimum time to wait before attempting to reconnect.\n - * The Parameter3 field (Time2Retain) indicates the maximum time - * allowed to reassign commands after the initial wait (in - * Parameter2).\n - * If the initiator does not attempt to reconnect and/or reassign - * the outstanding commands within the time specified by - * Parameter3, or if Parameter3 is 0, the session is terminated.\n - * In this case, the target will terminate all outstanding - * commands in this session; no other responses should be - * expected from the target for the outstanding commands in this - * session. A value of 0 for Parameter2 indicates that reconnect - * can be attempted immediately. - */ -#define ISCSI_ASYNC_MSG_EVENT_SESSION_DROP_NOTIFY 0x03 - -/** - * @brief SCSI Asynchronous Message Event: Negotiation Request. - * - * The target requests parameter negotiation on this connection. - * The initiator MUST honor this request by issuing a Text - * Request (that can be empty) on the same connection as early - * as possible, but no later than Parameter3 seconds, unless a - * Text Request is already pending on the connection, or by - * issuing a Logout Request. If the initiator does not issue a - * Text Request, the target may reissue the Asynchronous Message - * requesting parameter negotiation. - */ -#define ISCSI_ASYNC_MSG_EVENT_NEGOTIATION_REQUEST 0x04 - -/** - * @brief SCSI Asynchronous Message Event: Task Termination. - * - * All active tasks for a LU with a matching LUN field in the - * Async Message PDU are being terminated. The receiving - * initiator iSCSI layer MUST respond to this message by - * taking the following steps, in order: - * - Stop Data-Out transfers on that connection for all active - * TTTs for the affected LUN quoted in the Async Message PDU. - * - Acknowledge the StatSN of the Async Message PDU via a - * NOP-Out PDU with ITT=0xFFFFFFFF (i.e., non-ping flavor), - * while copying the LUN field from the Async Message to - * NOP-Out. - * This value of AsyncEvent, however, MUST NOT be used on an - * iSCSI session unless the new TaskReporting text key was - * negotiated to FastAbort on the session. - */ -#define ISCSI_ASYNC_MSG_EVENT_TASK_TERMINATION 0x05 - -/// SCSI Asynchronous Message Event: First vendor-specific iSCSI event. The AsyncVCode details the vendor code, and data MAY accompany the report. -#define ISCSI_ASYNC_MSG_EVENT_VENDOR_FIRST 0xF8 - -/// SCSI Asynchronous Message Event: Last vendor-specific iSCSI event. The AsyncVCode details the vendor code, and data MAY accompany the report. -#define ISCSI_ASYNC_MSG_EVENT_VENDOR_LAST 0xFF - -/** - * @brief iSCSI Asynchronous Message packet data. - * - * An Asynchronous Message may be sent from the target to the initiator - * without corresponding to a particular command. The target specifies - * the reason for the event and sense data.\n - * Some Asynchronous Messages are strictly related to iSCSI, while - * others are related to SCSI - */ -typedef struct __attribute__((packed)) iscsi_async_msg_packet { - /// Always 0x32 according to iSCSI specification. - uint8_t opcode; - - /// Reserved for future usage (always MUST be 0x80 for now). - int8_t flags; - - /// Reserved for future usage, always MUST be 0. - uint16_t reserved; - - /// TotalAHSLength, MUST be 0 for this PDU. - uint8_t total_ahs_len; - - /// DataSegmentLength, MUST be 0 0 for this PDU. - uint8_t ds_len[3]; - - /// The LUN field MUST be valid if AsyncEvent is 0. Otherwise, this field is reserved. - uint64_t lun; - - /// Tag (always 0xFFFFFFFF for now). - uint32_t tag; - - /// Reserved for future usage, always MUST be 0. - uint32_t reserved2; - - /** - * @brief StatSN. - * - * The StatSN counts this PDU as an acknowledgeable event (the StatSN is - * advanced), which allows for initiator and target state synchronization. - */ - uint32_t stat_sn; - - /// ExpCmdSN. - uint32_t exp_cmd_sn; - - /// MaxCmdSN. - uint32_t max_cmd_sn; - - /// AsyncEvent. - uint8_t async_event; - - /// AsyncVCode is a vendor-specific detail code that is only valid if the AsyncEvent field indicates a vendor-specific event. Otherwise, it is reserved. - uint8_t async_vcode; - - /// Parameter1 or Reserved. - uint16_t param_1; - - /// Parameter2 or Reserved. - uint16_t param_2; - - /// Parameter3 or Reserved. - uint16_t param_3; - - /// Reserved for future usage, always MUST be 0. - uint32_t reserved3; - - /// Optional header digest. - iscsi_header_digest hdr_digest; - - /// Data segment. - iscsi_scsi_ds_cmd_data ds_cmd_data; - - /// Optional data digest. - iscsi_data_digest data_digest; -} iscsi_async_msg_packet; - - /** * @brief Text Request flags: Continue. * @@ -6287,106 +3933,100 @@ typedef struct __attribute__((packed)) iscsi_async_msg_packet { * implicitly terminated by the target. */ typedef struct __attribute__((packed)) iscsi_text_req_packet { - /// Always 0x04 according to iSCSI specification. - uint8_t opcode; - - /// Text request flags. - int8_t flags; - - /// Reserved for future usage, always MUST be 0. - uint16_t reserved; - - /// TotalAHSLength. - uint8_t total_ahs_len; - - /// DataSegmentLength. - uint8_t ds_len[3]; - - /// Logical Unit Number (LUN) or Reserved. - uint64_t lun; - - /** - * @brief Initiator Task Tag (ITT). - * - * This is the initiator-assigned identifier for this Text Request. If - * the command is sent as part of a sequence of Text Requests and - * responses, the Initiator Task Tag MUST be the same for all the - * requests within the sequence (similar to linked SCSI commands). The - * I bit for all requests in a sequence also MUST be the same. - */ - uint32_t init_task_tag; - - /** - * @brief Target Transfer Tag (TTT). - * - * When the Target Transfer Tag is set to the reserved value 0xFFFFFFFF, - * it tells the target that this is a new request, and the target resets - * any internal state associated with the Initiator Task Tag (resets the - * current negotiation state).\n - * The target sets the Target Transfer Tag in a Text Response to a value - * other than the reserved value 0xFFFFFFFF whenever it indicates that - * it has more data to send or more operations to perform that are - * associated with the specified Initiator Task Tag. It MUST do so - * whenever it sets the F bit to 0 in the response. By copying the - * Target Transfer Tag from the response to the next Text Request, the - * initiator tells the target to continue the operation for the specific - * Initiator Task Tag. The initiator MUST ignore the Target Transfer - * Tag in the Text Response when the F bit is set to 1.\n - * This mechanism allows the initiator and target to transfer a large - * amount of textual data over a sequence of text-command/text-response - * exchanges or to perform extended negotiation sequences.\n - * If the Target Transfer Tag is not 0xFFFFFFFF, the LUN field MUST be - * sent by the target in the Text Response.\n - * A target MAY reset its internal negotiation state if an exchange is - * stalled by the initiator for a long time or if it is running out of - * resources.\n - * Long Text Responses are handled as shown in the following example:\n - * @verbatim - * I->T Text SendTargets=All (F = 1, TTT = 0xFFFFFFFF) - * T->I Text (F = 0, TTT = 0x12345678) - * I->T Text (F = 1, TTT = 0x12345678) - * T->I Text (F = 0, TTT = 0x12345678) - * I->T Text (F = 1, TTT = 0x12345678) - * ... - * T->I Text (F = 1, TTT = 0xFFFFFFFF) - * @endverbatim - */ - uint32_t target_xfer_tag; - - /// CmdSN. - uint32_t cmd_sn; - - /// ExpStatSN. - uint32_t exp_stat_sn; - - /// Reserved for future usage, always MUST be 0. - uint64_t reserved2[2]; - - /// Optional header digest. - iscsi_header_digest hdr_digest; - - /** - * @brief Data segment. - * - * The data lengths of a Text Request MUST NOT exceed the iSCSI target - * MaxRecvDataSegmentLength (a parameter that is negotiated per - * connection and per direction).\n - * A key=value pair can span Text Request or Text Response boundaries. - * A key=value pair can start in one PDU and continue on the next. In - * other words, the end of a PDU does not necessarily signal the end of - * a key=value pair.\n - * The target responds by sending its response back to the initiator. - * The response text format is similar to the request text format. The - * Text Response MAY refer to key=value pairs presented in an earlier - * Text Request, and the text in the request may refer to earlier - * responses.\n - * Text operations are usually meant for parameter setting/negotiations - * but can also be used to perform some long-lasting operations. - */ - iscsi_scsi_ds_cmd_data ds_cmd_data; - - /// Optional data digest. - iscsi_data_digest data_digest; + /// Always 0x04 according to iSCSI specification. + uint8_t opcode; + + /// Text request flags. + int8_t flags; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// Logical Unit Number (LUN) or Reserved. + uint64_t lun; + + /** + * @brief Initiator Task Tag (ITT). + * + * This is the initiator-assigned identifier for this Text Request. If + * the command is sent as part of a sequence of Text Requests and + * responses, the Initiator Task Tag MUST be the same for all the + * requests within the sequence (similar to linked SCSI commands). The + * I bit for all requests in a sequence also MUST be the same. + */ + uint32_t init_task_tag; + + /** + * @brief Target Transfer Tag (TTT). + * + * When the Target Transfer Tag is set to the reserved value 0xFFFFFFFF, + * it tells the target that this is a new request, and the target resets + * any internal state associated with the Initiator Task Tag (resets the + * current negotiation state).\n + * The target sets the Target Transfer Tag in a Text Response to a value + * other than the reserved value 0xFFFFFFFF whenever it indicates that + * it has more data to send or more operations to perform that are + * associated with the specified Initiator Task Tag. It MUST do so + * whenever it sets the F bit to 0 in the response. By copying the + * Target Transfer Tag from the response to the next Text Request, the + * initiator tells the target to continue the operation for the specific + * Initiator Task Tag. The initiator MUST ignore the Target Transfer + * Tag in the Text Response when the F bit is set to 1.\n + * This mechanism allows the initiator and target to transfer a large + * amount of textual data over a sequence of text-command/text-response + * exchanges or to perform extended negotiation sequences.\n + * If the Target Transfer Tag is not 0xFFFFFFFF, the LUN field MUST be + * sent by the target in the Text Response.\n + * A target MAY reset its internal negotiation state if an exchange is + * stalled by the initiator for a long time or if it is running out of + * resources.\n + * Long Text Responses are handled as shown in the following example:\n + * @verbatim + * I->T Text SendTargets=All (F = 1, TTT = 0xFFFFFFFF) + * T->I Text (F = 0, TTT = 0x12345678) + * I->T Text (F = 1, TTT = 0x12345678) + * T->I Text (F = 0, TTT = 0x12345678) + * I->T Text (F = 1, TTT = 0x12345678) + * ... + * T->I Text (F = 1, TTT = 0xFFFFFFFF) + * @endverbatim + */ + uint32_t target_xfer_tag; + + /// CmdSN. + uint32_t cmd_sn; + + /// ExpStatSN. + uint32_t exp_stat_sn; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2[2]; + + /** + * @brief Data segment. + * + * The data lengths of a Text Request MUST NOT exceed the iSCSI target + * MaxRecvDataSegmentLength (a parameter that is negotiated per + * connection and per direction).\n + * A key=value pair can span Text Request or Text Response boundaries. + * A key=value pair can start in one PDU and continue on the next. In + * other words, the end of a PDU does not necessarily signal the end of + * a key=value pair.\n + * The target responds by sending its response back to the initiator. + * The response text format is similar to the request text format. The + * Text Response MAY refer to key=value pairs presented in an earlier + * Text Request, and the text in the request may refer to earlier + * responses.\n + * Text operations are usually meant for parameter setting/negotiations + * but can also be used to perform some long-lasting operations. + */ + iscsi_scsi_ds_cmd_data ds_cmd_data; } iscsi_text_req_packet; @@ -6428,87 +4068,81 @@ typedef struct __attribute__((packed)) iscsi_text_req_packet { * of the Text Request. */ typedef struct __attribute__((packed)) iscsi_text_response_packet { - /// Always 0x24 according to iSCSI specification. - uint8_t opcode; - - /// Text response flags. - int8_t flags; - - /// Reserved for future usage, always MUST be 0. - uint16_t reserved; - - /// TotalAHSLength. - uint8_t total_ahs_len; - - /// DataSegmentLength. - uint8_t ds_len[3]; - - /// Logical Unit Number (LUN) or Reserved. - uint64_t lun; - - /// The Initiator Task Tag matches the tag used in the initial Text Request. - uint32_t init_task_tag; - - /** - * @brief Target Transfer Tag (TTT). - * - * When a target has more work to do (e.g., cannot transfer all the - * remaining text data in a single Text Response or has to continue the - * negotiation) and has enough resources to proceed, it MUST set the - * Target Transfer Tag to a value other than the reserved value - * 0xFFFFFFFF. Otherwise, the Target Transfer Tag MUST be set to - * 0xFFFFFFFF.\n - * When the Target Transfer Tag is not 0xFFFFFFFF, the LUN field may be - * significant.\n - * The initiator MUST copy the Target Transfer Tag and LUN in its next - * request to indicate that it wants the rest of the data.\n - * When the target receives a Text Request with the Target Transfer Tag - * set to the reserved value 0xFFFFFFFF, it resets its internal - * information (resets state) associated with the given Initiator Task - * Tag (restarts the negotiation).\n - * When a target cannot finish the operation in a single Text Response - * and does not have enough resources to continue, it rejects the Text - * Request with the appropriate Reject code.\n - * A target may reset its internal state associated with an Initiator - * Task Tag (the current negotiation state) as expressed through the - * Target Transfer Tag if the initiator fails to continue the exchange - * for some time. The target may reject subsequent Text Requests with - * the Target Transfer Tag set to the "stale" value. - */ - uint32_t target_xfer_tag; - - /// StatSN. The target StatSN variable is advanced by each Text Response sent. - uint32_t stat_sn; - - /// ExpCmdSN. - uint32_t exp_cmd_sn; - - /// MaxCmdSN. - uint32_t max_cmd_sn; - - /// Reserved for future usage, always MUST be 0. - uint64_t reserved2[2]; - - /// Optional header digest. - iscsi_header_digest hdr_digest; - - /** - * @brief Data segment. - * - * The data lengths of a Text Response MUST NOT exceed the iSCSI - * initiator MaxRecvDataSegmentLength (a parameter that is negotiated - * per connection and per direction).\n - * The text in the Text Response Data is governed by the same rules as - * the text in the Text Request Data.\n - * Although the initiator is the requesting party and controls the - * request-response initiation and termination, the target can offer - * key=value pairs of its own as part of a sequence and not only in - * response to the initiator. - */ - iscsi_scsi_ds_cmd_data ds_cmd_data; - - /// Optional data digest. - iscsi_data_digest data_digest; + /// Always 0x24 according to iSCSI specification. + uint8_t opcode; + + /// Text response flags. + int8_t flags; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// Logical Unit Number (LUN) or Reserved. + uint64_t lun; + + /// The Initiator Task Tag matches the tag used in the initial Text Request. + uint32_t init_task_tag; + + /** + * @brief Target Transfer Tag (TTT). + * + * When a target has more work to do (e.g., cannot transfer all the + * remaining text data in a single Text Response or has to continue the + * negotiation) and has enough resources to proceed, it MUST set the + * Target Transfer Tag to a value other than the reserved value + * 0xFFFFFFFF. Otherwise, the Target Transfer Tag MUST be set to + * 0xFFFFFFFF.\n + * When the Target Transfer Tag is not 0xFFFFFFFF, the LUN field may be + * significant.\n + * The initiator MUST copy the Target Transfer Tag and LUN in its next + * request to indicate that it wants the rest of the data.\n + * When the target receives a Text Request with the Target Transfer Tag + * set to the reserved value 0xFFFFFFFF, it resets its internal + * information (resets state) associated with the given Initiator Task + * Tag (restarts the negotiation).\n + * When a target cannot finish the operation in a single Text Response + * and does not have enough resources to continue, it rejects the Text + * Request with the appropriate Reject code.\n + * A target may reset its internal state associated with an Initiator + * Task Tag (the current negotiation state) as expressed through the + * Target Transfer Tag if the initiator fails to continue the exchange + * for some time. The target may reject subsequent Text Requests with + * the Target Transfer Tag set to the "stale" value. + */ + uint32_t target_xfer_tag; + + /// StatSN. The target StatSN variable is advanced by each Text Response sent. + uint32_t stat_sn; + + /// ExpCmdSN. + uint32_t exp_cmd_sn; + + /// MaxCmdSN. + uint32_t max_cmd_sn; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2[2]; + + /** + * @brief Data segment. + * + * The data lengths of a Text Response MUST NOT exceed the iSCSI + * initiator MaxRecvDataSegmentLength (a parameter that is negotiated + * per connection and per direction).\n + * The text in the Text Response Data is governed by the same rules as + * the text in the Text Request Data.\n + * Although the initiator is the requesting party and controls the + * request-response initiation and termination, the target can offer + * key=value pairs of its own as part of a sequence and not only in + * response to the initiator. + */ + iscsi_scsi_ds_cmd_data ds_cmd_data; } iscsi_text_response_packet; @@ -6585,1735 +4219,20 @@ typedef struct __attribute__((packed)) iscsi_text_response_packet { * MUST also be persistent over power cycles, reboot, card swap, etc. */ typedef struct __attribute__((packed)) iscsi_isid { - /// Meaning depends on T bit, either 22-bit OUI or reserved. - uint8_t a; + /// Meaning depends on T bit, either 22-bit OUI or reserved. + uint8_t a; - /// Meaning depends on T bit, either 22-bit OUI, EN (IANA Enterprise Number) or random. - uint16_t b; + /// Meaning depends on T bit, either 22-bit OUI, EN (IANA Enterprise Number) or random. + uint16_t b; - /// Meaning depends on T bit, either 24-bit Qualifier, EN (IANA Enterprise Number) or random. - uint8_t c; + /// Meaning depends on T bit, either 24-bit Qualifier, EN (IANA Enterprise Number) or random. + uint8_t c; - /// Meaning depends on T bit, either 24-bit Qualifier or Qualifier. - uint16_t d; + /// Meaning depends on T bit, either 24-bit Qualifier or Qualifier. + uint16_t d; } iscsi_isid; -/** - * @brief Key used during SecurityNegotiation stage of Login Phase: Session type. - * - * @verbatim - * Use: LO, Declarative, Any-Stage - * Senders: Initiator - * Scope: SW - * SessionType= - * Default is Normal. - * @endverbatim - * The initiator indicates the type of session it wants to create. The - * target can either accept it or reject it.\n - * A Discovery session indicates to the target that the only purpose of - * this session is discovery. The only requests a target accepts in - * this type of session are a Text Request with a SendTargets key and a - * Logout Request with reason "close the session".\n - * The Discovery session implies MaxConnections = 1 and overrides both - * the default and an explicit setting. ErrorRecoveryLevel MUST be 0 - * (zero) for Discovery sessions.\n - * Depending on the type of session, a target may decide on resources to - * allocate, the security to enforce, etc., for the session. If the - * 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 ((const uint8_t *) "SessionType\0\0\0\0") - -/** - * @brief Key used during SecurityNegotiation stage of Login Phase: Initiator name. - * - * @verbatim - * Use: IO, Declarative, Any-Stage - * Senders: Initiator - * Scope: SW - * InitiatorName= - * Examples: - * InitiatorName=iqn.1992-04.de.uni-freiburg.bwlehrpool:qcow2.5003 - * InitiatorName=iqn.2001-02.de.uni-freiburg.matrix:basty.eduroam - * InitiatorName=naa.52004567BA64678D - * @endverbatim - * The initiator of the TCP connection MUST provide this key to the - * remote endpoint at the first login of the Login Phase for every - * connection. The InitiatorName key enables the initiator to identify - * 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 ((const uint8_t *) "InitiatorName\0\0") - -/** - * @brief Key used during SecurityNegotiation stage of Login Phase: Target name. - * - * @verbatim - * Use: IO by initiator, FFPO by target - only as response to a - * SendTargets, Declarative, Any-Stage - * Senders: Initiator and target - * Scope: SW - * TargetName= - * Examples: - * TargetName=iqn.1993-11.de.uni-freiburg:diskarrays.sn.5003 - * TargetName=eui.020000023B040506 - * TargetName=naa.62004567BA64678D0123456789ABCDEF - * @endverbatim - * The initiator of the TCP connection MUST provide this key to the - * remote endpoint in the first Login Request if the initiator is not - * establishing a Discovery session. The iSCSI Target Name specifies - * the worldwide unique name of the target.\n - * The TargetName key may also be returned by the SendTargets Text - * 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 ((const uint8_t *) "TargetName\0\0\0\0\0") - -/** - * @brief Key used during SecurityNegotiation stage of Login Phase: Target address. - * - * @verbatim - * Use: ALL, Declarative, Any-Stage - * Senders: Target - * Scope: SW - * TargetAddress=domainname[:port][,portal-group-tag] - * @endverbatim - * The domainname can be specified as either a DNS host name, a dotted- - * decimal IPv4 address, or a bracketed IPv6 address as specified in - * RFC3986.\n - * If the TCP port is not specified, it is assumed to be the IANA- - * assigned default port for iSCSI.\n - * If the TargetAddress is returned as the result of a redirect status - * in a Login Response, the comma and portal-group-tag MUST be omitted. - * If the TargetAddress is returned within a SendTargets response, the - * portal-group-tag MUST be included.\n - * @verbatim - * Examples: - * TargetAddress=10.0.0.1:5003,1 - * TargetAddress=[1080:0:0:0:8:800:200C:417A],65 - * TargetAddress=[1080::8:800:200C:417A]:5003,1 - * TargetAddress=gitlab.uni-freiburg.de,443 - * @endverbatim - * 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 ((const uint8_t *) "TargetAddress\0\0") - -/** - * @brief Key used during SecurityNegotiation stage of Login Phase: Initiator alias. - * - * @verbatim - * Use: ALL, Declarative, Any-Stage - * Senders: Initiator - * Scope: SW - * InitiatorAlias= - * Examples: - * InitiatorAlias=Web Server 5 - * InitiatorAlias=matrix.uni-freiburg.de - * InitiatorAlias=Matrix Server - * @endverbatim - * If an initiator has been configured with a human-readable name or - * description, it SHOULD be communicated to the target during a Login - * Request PDU. If not, the host name can be used instead. This string - * is not used as an identifier, nor is it meant to be used for - * authentication or authorization decisions. It can be displayed by - * the target's user interface in a list of initiators to which it is - * connected. - */ -#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. - * - * @verbatim - * Use: ALL, Declarative, Any-Stage - * Senders: Target - * Scope: SW - * TargetAlias= - * Examples: - * TargetAlias=Bob-s Disk - * TargetAlias=Database Server 1 Log Disk - * TargetAlias=Web Server 3 Disk 20 - * @endverbatim - * If a target has been configured with a human-readable name or - * description, this name SHOULD be communicated to the initiator during - * a Login Response PDU if SessionType=Normal. This string is not used - * as an identifier, nor is it meant to be used for authentication or - * 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 ((const uint8_t *) "TargetAlias\0\0\0\0") - -/** - * @brief Key used during SecurityNegotiation stage of Login Phase: Target portal group tag. - * - * @verbatim - * Use: IO by target, Declarative, Any-Stage - * Senders: Target - * Scope: SW - * TargetPortalGroupTag=<16-bit-binary-value> - * Example: - * TargetPortalGroupTag=1 - * @endverbatim - * The TargetPortalGroupTag key is a 16-bit binary-value that uniquely - * identifies a portal group within an iSCSI target node. This key - * carries the value of the tag of the portal group that is servicing - * the Login Request. The iSCSI target returns this key to the - * initiator in the Login Response PDU to the first Login Request PDU - * that has the C bit set to 0 when TargetName is given by the - * initiator.\n - * SAM2 notes in its informative text that the TPGT value should be - * non-zero; note that this is incorrect. A zero value is allowed as a - * legal value for the TPGT. This discrepancy currently stands - * corrected in SAM4. - */ -#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. - * - * @verbatim - * Use: During Login - Security Negotiation - * Senders: Initiator and target - * Scope: connection - * AuthMethod = - * @endverbatim - * The main item of security negotiation is the authentication method - * (AuthMethod).\n - * The authentication methods that can be used (appear in the list-of- - * values) are either vendor-unique methods or those listed in the - * following table: - * Name | Description - * :--- | :--------------------------------------------------------------- - * KRB5 | Kerberos V5 - defined in RFC4120 - * SRP | Secure Remote Password - defined in RFC2945 - * CHAP | Challenge Handshake Authentication Protocol - defined in RFC1994 - * None | No authentication - * - * The AuthMethod selection is followed by an "authentication exchange" - * specific to the authentication method selected.\n - * The authentication method proposal may be made by either the - * initiator or the target. However, the initiator MUST make the first - * step specific to the selected authentication method as soon as it is - * selected. It follows that if the target makes the authentication - * method proposal, the initiator sends the first key(s) of the exchange - * together with its authentication method selection.\n - * The authentication exchange authenticates the initiator to the target - * and, optionally, the target to the initiator. Authentication is - * OPTIONAL to use but MUST be supported by the target and initiator. - * The initiator and target MUST implement CHAP. All other - * authentication methods are OPTIONAL.\n - * Private or public extension algorithms MAY also be negotiated for - * authentication methods. Whenever a private or public extension - * algorithm is part of the default offer (the offer made in the absence - * of explicit administrative action), the implementer MUST ensure that - * CHAP is listed as an alternative in the default offer and "None" is - * not part of the default offer.\n - * Extension authentication methods MUST be named using one of the - * following two formats: - * -# Z-reversed.vendor.dns_name.do_something= - * -# New public key with no name prefix constraints - * - * Authentication methods named using the Z- format are used as private - * extensions. New public keys must be registered with IANA using the - * IETF Review process RFC5226. New public extensions for - * authentication methods MUST NOT use the Z# name prefix.\n - * For all of the public or private extension authentication methods, - * the method-specific keys MUST conform to the format specified for - * standard-label.\n - * To identify the vendor for private extension authentication methods, - * we suggest using the reversed DNS-name as a prefix to the proper - * digest names.\n - * The part of digest-name following Z- MUST conform to the format for - * standard-label.\n - * Support for public or private extension authentication methods is - * OPTIONAL. - */ -#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD ((const uint8_t *) "AuthMethod\0\0\0\0\0") - - -/** - * @brief Key used during SecurityNegotiation stage of Login Phase: Kerberos V5 (KRB5): KRB_AP_REQ. - * - * For KRB5 (Kerberos V5) (see RFC4120 and RFC1964), the initiator MUST use: - * @verbatim - * KRB_AP_REQ= - * @endverbatim - * where KRB_AP_REQ is the client message as defined in RFC4120. - * The default principal name assumed by an iSCSI initiator or target - * (prior to any administrative configuration action) MUST be the iSCSI - * Initiator Name or iSCSI Target Name, respectively, prefixed by the - * string "iscsi/".\n - * If the initiator authentication fails, the target MUST respond with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator has selected the mutual authentication option (by setting - * MUTUAL-REQUIRED in the ap-options field of the KRB_AP_REQ), the - * target MUST reply with: - * @verbatim - * KRB_AP_REP= - * @endverbatim - * where KRB_AP_REP is the server's response message as defined in - * RFC4120.\n - * If mutual authentication was selected and target authentication - * fails, the initiator MUST close the connection.\n - * KRB_AP_REQ and KRB_AP_REP are binary-values, and their binary length - * (not the length of the character string that represents them in - * 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 ((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. - * - * For KRB5 (Kerberos V5) (see RFC4120 and RFC1964), the initiator MUST use: - * @verbatim - * KRB_AP_REQ= - * @endverbatim - * where KRB_AP_REQ is the client message as defined in RFC4120. - * The default principal name assumed by an iSCSI initiator or target - * (prior to any administrative configuration action) MUST be the iSCSI - * Initiator Name or iSCSI Target Name, respectively, prefixed by the - * string "iscsi/".\n - * If the initiator authentication fails, the target MUST respond with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator has selected the mutual authentication option (by setting - * MUTUAL-REQUIRED in the ap-options field of the KRB_AP_REQ), the - * target MUST reply with: - * @verbatim - * KRB_AP_REP= - * @endverbatim - * where KRB_AP_REP is the server's response message as defined in - * RFC4120.\n - * If mutual authentication was selected and target authentication - * fails, the initiator MUST close the connection.\n - * KRB_AP_REQ and KRB_AP_REP are binary-values, and their binary length - * (not the length of the character string that represents them in - * 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 ((const uint8_t *) "KRB_AP_REP\0\0\0\0\0") - - -/** - * @brief Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_U. - * - * For SRP RFC2945, the initiator MUST use: - * @verbatim - * SRP_U= TargetAuth=Yes or TargetAuth=No - * @endverbatim - * The target MUST answer with a Login reject with the "Authorization - * Failure" status or reply with: - * @verbatim - * SRP_GROUP= SRP_s= - * @endverbatim - * where G1,G2... are proposed groups, in order of preference. - * The initiator MUST either close the connection or continue with: - * @verbatim - * SRP_A= - * SRP_GROUP= - * @endverbatim - * where G is one of G1,G2... that were proposed by the target. - * The target MUST answer with a Login reject with the "Authentication - * Failure" status or reply with: - * @verbatim - * SRP_B= - * @endverbatim - * The initiator MUST close the connection or continue with: - * @verbatim - * SRP_M= - * @endverbatim - * If the initiator authentication fails, the target MUST answer with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator sent TargetAuth=Yes in the first message (requiring target - * authentication), the target MUST reply with: - * @verbatim - * SRP_HM= - * @endverbatim - * If the target authentication fails, the initiator MUST close the - * connection:\n - * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using - * the SHA1 hash function, such as SRP-SHA1) and - * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups - * specified in RFC3723.\n - * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are - * binary-values. The length of s,A,B,M and H(A | M | K) in binary form - * (not the length of the character string that represents them in - * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may - * be used for s,A,B,M and H(A | M | K).\n - * For the SRP_GROUP, all the groups specified in RFC3723 up to - * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be - * 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 ((const uint8_t *) "SRP_U\0\0") - -/** - * @brief Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_GROUP. - * - * For SRP RFC2945, the initiator MUST use: - * @verbatim - * SRP_U= TargetAuth=Yes or TargetAuth=No - * @endverbatim - * The target MUST answer with a Login reject with the "Authorization - * Failure" status or reply with: - * @verbatim - * SRP_GROUP= SRP_s= - * @endverbatim - * where G1,G2... are proposed groups, in order of preference. - * The initiator MUST either close the connection or continue with: - * @verbatim - * SRP_A= - * SRP_GROUP= - * @endverbatim - * where G is one of G1,G2... that were proposed by the target. - * The target MUST answer with a Login reject with the "Authentication - * Failure" status or reply with: - * @verbatim - * SRP_B= - * @endverbatim - * The initiator MUST close the connection or continue with: - * @verbatim - * SRP_M= - * @endverbatim - * If the initiator authentication fails, the target MUST answer with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator sent TargetAuth=Yes in the first message (requiring target - * authentication), the target MUST reply with: - * @verbatim - * SRP_HM= - * @endverbatim - * If the target authentication fails, the initiator MUST close the - * connection:\n - * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using - * the SHA1 hash function, such as SRP-SHA1) and - * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups - * specified in RFC3723.\n - * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are - * binary-values. The length of s,A,B,M and H(A | M | K) in binary form - * (not the length of the character string that represents them in - * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may - * be used for s,A,B,M and H(A | M | K).\n - * For the SRP_GROUP, all the groups specified in RFC3723 up to - * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be - * 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 ((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. - * - * For SRP RFC2945, the initiator MUST use: - * @verbatim - * SRP_U= TargetAuth=Yes or TargetAuth=No - * @endverbatim - * The target MUST answer with a Login reject with the "Authorization - * Failure" status or reply with: - * @verbatim - * SRP_GROUP= SRP_s= - * @endverbatim - * where G1,G2... are proposed groups, in order of preference. - * The initiator MUST either close the connection or continue with: - * @verbatim - * SRP_A= - * SRP_GROUP= - * @endverbatim - * where G is one of G1,G2... that were proposed by the target. - * The target MUST answer with a Login reject with the "Authentication - * Failure" status or reply with: - * @verbatim - * SRP_B= - * @endverbatim - * The initiator MUST close the connection or continue with: - * @verbatim - * SRP_M= - * @endverbatim - * If the initiator authentication fails, the target MUST answer with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator sent TargetAuth=Yes in the first message (requiring target - * authentication), the target MUST reply with: - * @verbatim - * SRP_HM= - * @endverbatim - * If the target authentication fails, the initiator MUST close the - * connection:\n - * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using - * the SHA1 hash function, such as SRP-SHA1) and - * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups - * specified in RFC3723.\n - * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are - * binary-values. The length of s,A,B,M and H(A | M | K) in binary form - * (not the length of the character string that represents them in - * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may - * be used for s,A,B,M and H(A | M | K).\n - * For the SRP_GROUP, all the groups specified in RFC3723 up to - * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be - * 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 ((const uint8_t *) "SRP_A\0\0") - -/** - * @brief Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_B. - * - * For SRP RFC2945, the initiator MUST use: - * @verbatim - * SRP_U= TargetAuth=Yes or TargetAuth=No - * @endverbatim - * The target MUST answer with a Login reject with the "Authorization - * Failure" status or reply with: - * @verbatim - * SRP_GROUP= SRP_s= - * @endverbatim - * where G1,G2... are proposed groups, in order of preference. - * The initiator MUST either close the connection or continue with: - * @verbatim - * SRP_A= - * SRP_GROUP= - * @endverbatim - * where G is one of G1,G2... that were proposed by the target. - * The target MUST answer with a Login reject with the "Authentication - * Failure" status or reply with: - * @verbatim - * SRP_B= - * @endverbatim - * The initiator MUST close the connection or continue with: - * @verbatim - * SRP_M= - * @endverbatim - * If the initiator authentication fails, the target MUST answer with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator sent TargetAuth=Yes in the first message (requiring target - * authentication), the target MUST reply with: - * @verbatim - * SRP_HM= - * @endverbatim - * If the target authentication fails, the initiator MUST close the - * connection:\n - * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using - * the SHA1 hash function, such as SRP-SHA1) and - * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups - * specified in RFC3723.\n - * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are - * binary-values. The length of s,A,B,M and H(A | M | K) in binary form - * (not the length of the character string that represents them in - * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may - * be used for s,A,B,M and H(A | M | K).\n - * For the SRP_GROUP, all the groups specified in RFC3723 up to - * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be - * 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 ((const uint8_t *) "SRP_B\0\0") - -/** - * @brief Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_M. - * - * For SRP RFC2945, the initiator MUST use: - * @verbatim - * SRP_U= TargetAuth=Yes or TargetAuth=No - * @endverbatim - * The target MUST answer with a Login reject with the "Authorization - * Failure" status or reply with: - * @verbatim - * SRP_GROUP= SRP_s= - * @endverbatim - * where G1,G2... are proposed groups, in order of preference. - * The initiator MUST either close the connection or continue with: - * @verbatim - * SRP_A= - * SRP_GROUP= - * @endverbatim - * where G is one of G1,G2... that were proposed by the target. - * The target MUST answer with a Login reject with the "Authentication - * Failure" status or reply with: - * @verbatim - * SRP_B= - * @endverbatim - * The initiator MUST close the connection or continue with: - * @verbatim - * SRP_M= - * @endverbatim - * If the initiator authentication fails, the target MUST answer with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator sent TargetAuth=Yes in the first message (requiring target - * authentication), the target MUST reply with: - * @verbatim - * SRP_HM= - * @endverbatim - * If the target authentication fails, the initiator MUST close the - * connection:\n - * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using - * the SHA1 hash function, such as SRP-SHA1) and - * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups - * specified in RFC3723.\n - * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are - * binary-values. The length of s,A,B,M and H(A | M | K) in binary form - * (not the length of the character string that represents them in - * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may - * be used for s,A,B,M and H(A | M | K).\n - * For the SRP_GROUP, all the groups specified in RFC3723 up to - * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be - * 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 ((const uint8_t *) "SRP_M\0\0") - -/** - * @brief Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_HM. - * - * For SRP RFC2945, the initiator MUST use: - * @verbatim - * SRP_U= TargetAuth=Yes or TargetAuth=No - * @endverbatim - * The target MUST answer with a Login reject with the "Authorization - * Failure" status or reply with: - * @verbatim - * SRP_GROUP= SRP_s= - * @endverbatim - * where G1,G2... are proposed groups, in order of preference. - * The initiator MUST either close the connection or continue with: - * @verbatim - * SRP_A= - * SRP_GROUP= - * @endverbatim - * where G is one of G1,G2... that were proposed by the target. - * The target MUST answer with a Login reject with the "Authentication - * Failure" status or reply with: - * @verbatim - * SRP_B= - * @endverbatim - * The initiator MUST close the connection or continue with: - * @verbatim - * SRP_M= - * @endverbatim - * If the initiator authentication fails, the target MUST answer with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator sent TargetAuth=Yes in the first message (requiring target - * authentication), the target MUST reply with: - * @verbatim - * SRP_HM= - * @endverbatim - * If the target authentication fails, the initiator MUST close the - * connection:\n - * where U, s, A, B, M, and H(A | M | K) are defined in RFC2945 (using - * the SHA1 hash function, such as SRP-SHA1) and - * G,Gn ("Gn" stands for G1,G2...) are identifiers of SRP groups - * specified in RFC3723.\n - * G, Gn, and U are text strings; s,A,B,M, and H(A | M | K) are - * binary-values. The length of s,A,B,M and H(A | M | K) in binary form - * (not the length of the character string that represents them in - * encoded form) MUST NOT exceed 1024 bytes. Hex or Base64 encoding may - * be used for s,A,B,M and H(A | M | K).\n - * For the SRP_GROUP, all the groups specified in RFC3723 up to - * 1536 bits (i.e. SRP-768, SRP-1024, SRP-1280, SRP-1536) must be - * 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 ((const uint8_t *) "SRP_HM\0") - - -/** - * @brief Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_A. - * - * For CHAP RFC1994, the initiator MUST use: - * @verbatim - * CHAP_A= - * @endverbatim - * where A1,A2... are proposed algorithms, in order of preference. - * The target MUST answer with a Login reject with the "Authentication - * Failure" status or reply with: - * @verbatim - * CHAP_A= - * CHAP_I= - * CHAP_C= - * @endverbatim - * where A is one of A1,A2... that were proposed by the initiator. - * The initiator MUST continue with: - * @verbatim - * CHAP_N= - * CHAP_R= - * @endverbatim - * or, if it requires target authentication, with: - * @verbatim - * CHAP_N= - * CHAP_R= - * CHAP_I= - * CHAP_C= - * @endverbatim - * If the initiator authentication fails, the target MUST answer with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator required target authentication, the target MUST either - * answer with a Login reject with "Authentication Failure" or reply - * with: - * @verbatim - * CHAP_N= - * CHAP_R= - * @endverbatim - * If the target authentication fails, the initiator MUST close the - * connection:\n - * where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, - * Algorithm, Identifier, Challenge, and Response as defined in - * RFC1994.\n - * N is a text string; A,A1,A2, and I are numbers; C and R are - * binary-values. Their binary length (not the length of the character - * string that represents them in encoded form) MUST NOT exceed - * 1024 bytes. Hex or Base64 encoding may be used for C and R.\n - * For the Algorithm, as stated in [RFC1994], one value is required to - * be implemented: - * @verbatim - * 5 (CHAP with MD5) - * @endverbatim - * 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 ((const uint8_t *) "CHAP_A\0") - -/** - * @brief Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_I. - * - * For CHAP RFC1994, the initiator MUST use: - * @verbatim - * CHAP_A= - * @endverbatim - * where A1,A2... are proposed algorithms, in order of preference. - * The target MUST answer with a Login reject with the "Authentication - * Failure" status or reply with: - * @verbatim - * CHAP_A= - * CHAP_I= - * CHAP_C= - * @endverbatim - * where A is one of A1,A2... that were proposed by the initiator. - * The initiator MUST continue with: - * @verbatim - * CHAP_N= - * CHAP_R= - * @endverbatim - * or, if it requires target authentication, with: - * @verbatim - * CHAP_N= - * CHAP_R= - * CHAP_I= - * CHAP_C= - * @endverbatim - * If the initiator authentication fails, the target MUST answer with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator required target authentication, the target MUST either - * answer with a Login reject with "Authentication Failure" or reply - * with: - * @verbatim - * CHAP_N= - * CHAP_R= - * @endverbatim - * If the target authentication fails, the initiator MUST close the - * connection:\n - * where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, - * Algorithm, Identifier, Challenge, and Response as defined in - * RFC1994.\n - * N is a text string; A,A1,A2, and I are numbers; C and R are - * binary-values. Their binary length (not the length of the character - * string that represents them in encoded form) MUST NOT exceed - * 1024 bytes. Hex or Base64 encoding may be used for C and R.\n - * For the Algorithm, as stated in [RFC1994], one value is required to - * be implemented: - * @verbatim - * 5 (CHAP with MD5) - * @endverbatim - * 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 ((const uint8_t *) "CHAP_I\0") - -/** - * @brief Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_C. - * - * For CHAP RFC1994, the initiator MUST use: - * @verbatim - * CHAP_A= - * @endverbatim - * where A1,A2... are proposed algorithms, in order of preference. - * The target MUST answer with a Login reject with the "Authentication - * Failure" status or reply with: - * @verbatim - * CHAP_A= - * CHAP_I= - * CHAP_C= - * @endverbatim - * where A is one of A1,A2... that were proposed by the initiator. - * The initiator MUST continue with: - * @verbatim - * CHAP_N= - * CHAP_R= - * @endverbatim - * or, if it requires target authentication, with: - * @verbatim - * CHAP_N= - * CHAP_R= - * CHAP_I= - * CHAP_C= - * @endverbatim - * If the initiator authentication fails, the target MUST answer with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator required target authentication, the target MUST either - * answer with a Login reject with "Authentication Failure" or reply - * with: - * @verbatim - * CHAP_N= - * CHAP_R= - * @endverbatim - * If the target authentication fails, the initiator MUST close the - * connection:\n - * where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, - * Algorithm, Identifier, Challenge, and Response as defined in - * RFC1994.\n - * N is a text string; A,A1,A2, and I are numbers; C and R are - * binary-values. Their binary length (not the length of the character - * string that represents them in encoded form) MUST NOT exceed - * 1024 bytes. Hex or Base64 encoding may be used for C and R.\n - * For the Algorithm, as stated in [RFC1994], one value is required to - * be implemented: - * @verbatim - * 5 (CHAP with MD5) - * @endverbatim - * 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 ((const uint8_t *) "CHAP_C\0") - -/** - * @brief Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_N. - * - * For CHAP RFC1994, the initiator MUST use: - * @verbatim - * CHAP_A= - * @endverbatim - * where A1,A2... are proposed algorithms, in order of preference. - * The target MUST answer with a Login reject with the "Authentication - * Failure" status or reply with: - * @verbatim - * CHAP_A= - * CHAP_I= - * CHAP_C= - * @endverbatim - * where A is one of A1,A2... that were proposed by the initiator. - * The initiator MUST continue with: - * @verbatim - * CHAP_N= - * CHAP_R= - * @endverbatim - * or, if it requires target authentication, with: - * @verbatim - * CHAP_N= - * CHAP_R= - * CHAP_I= - * CHAP_C= - * @endverbatim - * If the initiator authentication fails, the target MUST answer with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator required target authentication, the target MUST either - * answer with a Login reject with "Authentication Failure" or reply - * with: - * @verbatim - * CHAP_N= - * CHAP_R= - * @endverbatim - * If the target authentication fails, the initiator MUST close the - * connection:\n - * where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, - * Algorithm, Identifier, Challenge, and Response as defined in - * RFC1994.\n - * N is a text string; A,A1,A2, and I are numbers; C and R are - * binary-values. Their binary length (not the length of the character - * string that represents them in encoded form) MUST NOT exceed - * 1024 bytes. Hex or Base64 encoding may be used for C and R.\n - * For the Algorithm, as stated in [RFC1994], one value is required to - * be implemented: - * @verbatim - * 5 (CHAP with MD5) - * @endverbatim - * 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 ((const uint8_t *) "CHAP_N\0") - -/** - * @brief Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_R. - * - * For CHAP RFC1994, the initiator MUST use: - * @verbatim - * CHAP_A= - * @endverbatim - * where A1,A2... are proposed algorithms, in order of preference. - * The target MUST answer with a Login reject with the "Authentication - * Failure" status or reply with: - * @verbatim - * CHAP_A= - * CHAP_I= - * CHAP_C= - * @endverbatim - * where A is one of A1,A2... that were proposed by the initiator. - * The initiator MUST continue with: - * @verbatim - * CHAP_N= - * CHAP_R= - * @endverbatim - * or, if it requires target authentication, with: - * @verbatim - * CHAP_N= - * CHAP_R= - * CHAP_I= - * CHAP_C= - * @endverbatim - * If the initiator authentication fails, the target MUST answer with a - * Login reject with "Authentication Failure" status. Otherwise, if the - * initiator required target authentication, the target MUST either - * answer with a Login reject with "Authentication Failure" or reply - * with: - * @verbatim - * CHAP_N= - * CHAP_R= - * @endverbatim - * If the target authentication fails, the initiator MUST close the - * connection:\n - * where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, - * Algorithm, Identifier, Challenge, and Response as defined in - * RFC1994.\n - * N is a text string; A,A1,A2, and I are numbers; C and R are - * binary-values. Their binary length (not the length of the character - * string that represents them in encoded form) MUST NOT exceed - * 1024 bytes. Hex or Base64 encoding may be used for C and R.\n - * For the Algorithm, as stated in [RFC1994], one value is required to - * be implemented: - * @verbatim - * 5 (CHAP with MD5) - * @endverbatim - * 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 ((const uint8_t *) "CHAP_R\0") - -/* Login/Text Operational Text Keys - - Some session-specific parameters MUST only be carried on the leading - connection and cannot be changed after the leading connection login - (e.g., MaxConnections - the maximum number of connections). This - holds for a single connection session with regard to connection - restart. The keys that fall into this category have the "use: LO" - (Leading Only). - - Keys that can only be used during login have the "use: IO" - (Initialize Only), while those that can be used in both the Login - Phase and Full Feature Phase have the "use: ALL". - - Keys that can only be used during the Full Feature Phase use FFPO - (Full Feature Phase Only). - - Keys marked as Any-Stage may also appear in the SecurityNegotiation - stage, while all other keys described in this section are - operational keys. - - Keys that do not require an answer are marked as Declarative. - - Key scope is indicated as session-wide (SW) or connection-only (CO). - - "Result function", wherever mentioned, states the function that can - be applied to check the validity of the responder selection. - "Minimum" means that the selected value cannot exceed the offered - value. "Maximum" means that the selected value cannot be lower than - the offered value. "AND" means that the selected value must be a - possible result of a Boolean "and" function with an arbitrary Boolean - value (e.g., if the offered value is No the selected value must be - No). "OR" means that the selected value must be a possible result of - a Boolean "or" function with an arbitrary Boolean value (e.g., if the - offered value is Yes the selected value must be Yes). -*/ - -/** - * @brief Login/Text Operational Session Text Key: Header digest. - * - * @verbatim - * Use: IO - * Senders: Initiator and target - * Scope: CO - * HeaderDigest = - * Default is None for HeaderDigest. - * @endverbatim - * Digests enable the checking of end-to-end, non-cryptographic data - * integrity beyond the integrity checks provided by the link layers and - * the covering of the whole communication path, including all elements - * that may change the network-level PDUs, such as routers, switches, - * and proxies.\n - * The following table lists cyclic integrity checksums that can be - * negotiated for the digests and MUST be implemented by every iSCSI - * initiator and target. These digest options only have error detection - * significance. - * Name | Description | Generator - * :----- | :---------- | :---------- - * CRC32C | 32-bit CRC | 0x11EDC6F41 - * None | no digest || - * - * The generator polynomial G(x) for this digest is given in hexadecimal - * notation (e.g. "0x3b" stands for 0011 1011, and the polynomial is - * x**5 + x**4 + x**3 + x + 1).\n - * When the initiator and target agree on a digest, this digest MUST be - * used for every PDU in the Full Feature Phase.\n - * Padding bytes, when present in a segment covered by a CRC, SHOULD be - * set to 0 and are included in the CRC.\n - * The CRC MUST be calculated by a method that produces the same results - * as the following process: - * - The PDU bits are considered as the coefficients of a polynomial - * M(x) of degree n - 1; bit 7 of the lowest numbered byte is - * considered the most significant bit (x**n - 1), followed by bit 6 - * of the lowest numbered byte through bit 0 of the highest numbered - * byte (x**0). - * - The most significant 32 bits are complemented. - * - The polynomial is multiplied by x**32, then divided by G(x). The - * generator polynomial produces a remainder R(x) of degree <= 31. - * - The coefficients of R(x) are formed into a 32-bit sequence. - * - The bit sequence is complemented, and the result is the CRC. - * - The CRC bits are mapped into the digest word. The x**31 - * coefficient is mapped to bit 7 of the lowest numbered byte of the - * digest, and the mapping continues with successive coefficients and - * bits so that the x**24 coefficient is mapped to bit 0 of the lowest - * numbered byte. The mapping continues further with the x**23 - * coefficient mapped to bit 7 of the next byte in the digest until - * the x**0 coefficient is mapped to bit 0 of the highest numbered - * byte of the digest. - * - Computing the CRC over any segment (data or header) extended to - * include the CRC built using the generator 0x11edc6f41 will always - * get the value 0x1c2d19ed as its final remainder (R(x)). This value - * is given here in its polynomial form (i.e., not mapped as the - * digest word). - * - * For a discussion about selection criteria for the CRC, see RFC3385.\n - * For a detailed analysis of the iSCSI polynomial, see Castagnoli93.\n - * Private or public extension algorithms MAY also be negotiated for - * digests. Whenever a private or public digest extension algorithm is - * part of the default offer (the offer made in the absence of explicit - * administrative action), the implementer MUST ensure that CRC32C is - * listed as an alternative in the default offer and "None" is not part - * of the default offer.\n - * Extension digest algorithms MUST be named using one of the following - * two formats: - * 1. Y-reversed.vendor.dns_name.do_something= - * 2. New public key with no name prefix constraints - * - * Digests named using the Y- format are used for private purposes - * (unregistered). New public keys must be registered with IANA using - * the IETF Review process (RFC5226). New public extensions for - * digests MUST NOT use the Y# name prefix.\n - * For private extension digests, to identify the vendor we suggest - * using the reversed DNS-name as a prefix to the proper digest names.\n - * The part of digest-name following Y- MUST conform to the format for - * standard-label specified.\n - * Support for public or private extension digests is OPTIONAL. - */ -#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. - * - * @verbatim - * Use: IO - * Senders: Initiator and target - * Scope: CO - * DataDigest = - * Default is None for DataDigest. - * @endverbatim - * Digests enable the checking of end-to-end, non-cryptographic data - * integrity beyond the integrity checks provided by the link layers and - * the covering of the whole communication path, including all elements - * that may change the network-level PDUs, such as routers, switches, - * and proxies.\n - * The following table lists cyclic integrity checksums that can be - * negotiated for the digests and MUST be implemented by every iSCSI - * initiator and target. These digest options only have error detection - * significance. - * Name | Description | Generator - * :----- | :---------- | :---------- - * CRC32C | 32-bit CRC | 0x11EDC6F41 - * None | no digest || - * - * The generator polynomial G(x) for this digest is given in hexadecimal - * notation (e.g. "0x3b" stands for 0011 1011, and the polynomial is - * x**5 + x**4 + x**3 + x + 1).\n - * When the initiator and target agree on a digest, this digest MUST be - * used for every PDU in the Full Feature Phase.\n - * Padding bytes, when present in a segment covered by a CRC, SHOULD be - * set to 0 and are included in the CRC.\n - * The CRC MUST be calculated by a method that produces the same results - * as the following process: - * - The PDU bits are considered as the coefficients of a polynomial - * M(x) of degree n - 1; bit 7 of the lowest numbered byte is - * considered the most significant bit (x**n - 1), followed by bit 6 - * of the lowest numbered byte through bit 0 of the highest numbered - * byte (x**0). - * - The most significant 32 bits are complemented. - * - The polynomial is multiplied by x**32, then divided by G(x). The - * generator polynomial produces a remainder R(x) of degree <= 31. - * - The coefficients of R(x) are formed into a 32-bit sequence. - * - The bit sequence is complemented, and the result is the CRC. - * - The CRC bits are mapped into the digest word. The x**31 - * coefficient is mapped to bit 7 of the lowest numbered byte of the - * digest, and the mapping continues with successive coefficients and - * bits so that the x**24 coefficient is mapped to bit 0 of the lowest - * numbered byte. The mapping continues further with the x**23 - * coefficient mapped to bit 7 of the next byte in the digest until - * the x**0 coefficient is mapped to bit 0 of the highest numbered - * byte of the digest. - * - Computing the CRC over any segment (data or header) extended to - * include the CRC built using the generator 0x11edc6f41 will always - * get the value 0x1c2d19ed as its final remainder (R(x)). This value - * is given here in its polynomial form (i.e., not mapped as the - * digest word). - * - * For a discussion about selection criteria for the CRC, see RFC3385.\n - * For a detailed analysis of the iSCSI polynomial, see Castagnoli93.\n - * Private or public extension algorithms MAY also be negotiated for - * digests. Whenever a private or public digest extension algorithm is - * part of the default offer (the offer made in the absence of explicit - * administrative action), the implementer MUST ensure that CRC32C is - * listed as an alternative in the default offer and "None" is not part - * of the default offer.\n - * Extension digest algorithms MUST be named using one of the following - * two formats: - * 1. Y-reversed.vendor.dns_name.do_something= - * 2. New public key with no name prefix constraints - * - * Digests named using the Y- format are used for private purposes - * (unregistered). New public keys must be registered with IANA using - * the IETF Review process (RFC5226). New public extensions for - * digests MUST NOT use the Y# name prefix.\n - * For private extension digests, to identify the vendor we suggest - * using the reversed DNS-name as a prefix to the proper digest names.\n - * The part of digest-name following Y- MUST conform to the format for - * standard-label specified.\n - * Support for public or private extension digests is OPTIONAL. - */ -#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. - * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * Irrelevant when: SessionType=Discovery - * MaxConnections= - * Default is 1. - * @endverbatim - * Result function is Minimum.\n - * The initiator and target negotiate the maximum number of connections - * requested/acceptable. - */ -#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS ((const uint8_t *) "MaxConnections\0") - -/** - * @brief Login/Text Operational Session Text Key: Send targets. - * - * @verbatim - * Use: FFPO - * Senders: Initiator - * Scope: SW - * @endverbatim - * The text in this appendix is a normative part of this document.\n - * To reduce the amount of configuration required on an initiator, iSCSI - * provides the SendTargets Text Request. The initiator uses the - * SendTargets request to get a list of targets to which it may have - * access, as well as the list of addresses (IP address and TCP port) on - * which these targets may be accessed.\n - * To make use of SendTargets, an initiator must first establish one of - * two types of sessions. If the initiator establishes the session - * using the key "SessionType=Discovery", the session is a Discovery - * session, and a target name does not need to be specified. Otherwise, - * the session is a Normal operational session. The SendTargets command - * MUST only be sent during the Full Feature Phase of a Normal or - * Discovery session.\n - * A system that contains targets MUST support Discovery sessions on - * each of its iSCSI IP address-port pairs and MUST support the - * SendTargets command on the Discovery session. In a Discovery - * session, a target MUST return all path information (IP address-port - * pairs and Target Portal Group Tags) for the targets on the target - * Network Entity that the requesting initiator is authorized to access.\n - * A target MUST support the SendTargets command on operational - * sessions; these will only return path information about the target to - * which the session is connected and do not need to return information - * about other target names that may be defined in the responding - * system.\n - * An initiator MAY make use of the SendTargets command as it sees fit.\n - * A SendTargets command consists of a single Text Request PDU. This - * PDU contains exactly one text key and value. The text key MUST be - * SendTargets. The expected response depends upon the value, as well - * as whether the session is a Discovery session or an operational - * session.\n - * The value must be one of: - * @verbatim - * All - * The initiator is requesting that information on all relevant - * targets known to the implementation be returned. This value - * MUST be supported on a Discovery session and MUST NOT be - * supported on an operational session. - * - * If an iSCSI Target Name is specified, the session should - * respond with addresses for only the named target, if possible. - * This value MUST be supported on Discovery sessions. A - * Discovery session MUST be capable of returning addresses for - * those targets that would have been returned had value=All been - * designated. - * - * The session should only respond with addresses for the target - * to which the session is logged in. This MUST be supported on - * operational sessions and MUST NOT return targets other than the - * one to which the session is logged in. - * @endverbatim - * The response to this command is a Text Response that contains a list - * of zero or more targets and, optionally, their addresses. Each - * target is returned as a target record. A target record begins with - * the TargetName text key, followed by a list of TargetAddress text - * keys, and bounded by the end of the Text Response or the next - * TargetName key, which begins a new record. No text keys other than - * TargetName and TargetAddress are permitted within a SendTargets - * response.\n - * A Discovery session MAY respond to a SendTargets request with its - * complete list of targets, or with a list of targets that is based on - * the name of the initiator logged in to the session.\n - * A SendTargets response MUST NOT contain target names if there are no - * targets for the requesting initiator to access.\n - * Each target record returned includes zero or more TargetAddress - * fields.\n - * Each target record starts with one text key of the form: - * @verbatim - * TargetName= - * @endverbatim - * followed by zero or more address keys of the form: - * @verbatim - * TargetAddress=[:], - * - * @endverbatim - * The hostname-or-ipaddress contains a domain name, IPv4 address, or - * IPv6 address (RFC4291), as specified for the TargetAddress key.\n - * A hostname-or-ipaddress duplicated in TargetAddress responses for a - * given node (the port is absent or equal) would probably indicate that - * multiple address families are in use at once (IPv6 and IPv4).\n - * Each TargetAddress belongs to a portal group, identified by its - * numeric Target Portal Group Tag. The iSCSI Target Name, together with - * this tag, constitutes the SCSI port identifier; the tag only needs to - * be unique within a given target's name list of addresses.\n - * Multiple-connection sessions can span iSCSI addresses that belong to - * the same portal group.\n - * Multiple-connection sessions cannot span iSCSI addresses that belong - * to different portal groups.\n - * If a SendTargets response reports an iSCSI address for a target, it - * SHOULD also report all other addresses in its portal group in the - * same response.\n - * A SendTargets Text Response can be longer than a single Text Response - * PDU and makes use of the long Text Responses as specified.\n - * After obtaining a list of targets from the Discovery session, an - * iSCSI initiator may initiate new sessions to log in to the discovered - * targets for full operation. The initiator MAY keep the Discovery - * session open and MAY send subsequent SendTargets commands to discover - * new targets.\n - * Examples:\n - * This example is the SendTargets response from a single target that - * has no other interface ports.\n - * The initiator sends a Text Request that contains: - * @verbatim - * SendTargets=All - * @endverbatim - * The target sends a Text Response that contains: - * @verbatim - * TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.8675309 - * @endverbatim - * All the target had to return in this simple case was the target name.\n - * It is assumed by the initiator that the IP address and TCP port for - * this target are the same as those used on the current connection to - * the default iSCSI target.\n - * The next example has two internal iSCSI targets, each accessible via - * two different ports with different IP addresses. The following is - * the Text Response: - * @verbatim - * TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.8675309 - * TargetAddress=10.1.0.45:5300,1 - * TargetAddress=10.1.1.45:5300,2 - * TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.1234567 - * TargetAddress=10.1.0.45:5300,1 - * TargetAddress=10.1.1.45:5300,2 - * @endverbatim - * Both targets share both addresses; the multiple addresses are likely - * used to provide multi-path support. The initiator may connect to - * either target name on either address. Each of the addresses has its - * own Target Portal Group Tag; they do not support spanning multiple- - * connection sessions with each other. Keep in mind that the Target - * Portal Group Tags for the two named targets are independent of one - * another; portal group "1" on the first target is not necessarily the - * same as portal group "1" on the second target.\n - * In the above example, a DNS host name or an IPv6 address could have - * been returned instead of an IPv4 address.\n - * The next Text Response shows a target that supports spanning sessions - * across multiple addresses and further illustrates the use of the - * Target Portal Group Tags: - * @verbatim - * TargetName=iqn.1993-11.de.uni-freiburg:diskarray.sn.8675309 - * TargetAddress=10.1.0.45:5300,1 - * TargetAddress=10.1.1.46:5300,1 - * TargetAddress=10.1.0.47:5300,2 - * TargetAddress=10.1.1.48:5300,2 - * TargetAddress=10.1.1.49:5300,3 - * @endverbatim - * In this example, any of the target addresses can be used to reach the - * same target. A single-connection session can be established to any - * of these TCP addresses. A multiple-connection session could span - * addresses .45 and .46 or .47 and .48 but cannot span any other - * combination. A TargetAddress with its own tag (.49) cannot be - * combined with any other address within the same session.\n - * This SendTargets response does not indicate whether .49 supports - * 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 ((const uint8_t *) "SendTargets\0\0\0\0") - -/** - * @brief Login/Text Operational Session Text Key: Initial Ready To Transfer. - * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * Irrelevant when: SessionType=Discovery - * InitialR2T= - * Examples: - * I->InitialR2T=No - * T->InitialR2T=No - * Default is Yes. - * @endverbatim - * Result function is OR.\n - * The InitialR2T key is used to turn off the default use of R2T for - * unidirectional operations and the output part of bidirectional - * commands, thus allowing an initiator to start sending data to a - * target as if it has received an initial R2T with Buffer - * Offset=Immediate Data Length and Desired Data Transfer - * Length=(min(FirstBurstLength, Expected Data Transfer Length) - - * Received Immediate Data Length).\n - * The default action is that R2T is required, unless both the initiator - * and the target send this key-pair attribute specifying InitialR2T=No. - * 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 ((const uint8_t *) "InitialR2T\0\0\0\0\0") - -/** - * @brief Login/Text Operational Session Text Key: Immediate data. - * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * Irrelevant when: SessionType=Discovery - * ImmediateData= - * Default is Yes. - * @endverbatim - * Result function is AND.\n - * 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 - * If ImmediateData is set to Yes and InitialR2T is set to Yes - * (default), then only immediate data are accepted in the first burst. - * If ImmediateData is set to No and InitialR2T is set to Yes, then the - * initiator MUST NOT send unsolicited data and the target MUST reject - * unsolicited data with the corresponding response code.\n - * If ImmediateData is set to No and InitialR2T is set to No, then the - * initiator MUST NOT send unsolicited immediate data but MAY send one - * unsolicited burst of Data-OUT PDUs.\n - * If ImmediateData is set to Yes and InitialR2T is set to No, then the - * initiator MAY send unsolicited immediate data and/or one unsolicited - * burst of Data-OUT PDUs.\n - * The following table is a summary of unsolicited data options: - * InitialR2T | ImmediateData | Unsolicited Data-Out PDUs | ImmediateData - * :--------- | :------------ | :------------------------ | :------------ - * | No | No | Yes | No | - * | No | Yes | Yes | Yes | - * | Yes | No | No | No | - * | Yes | Yes | No | Yes | - */ -#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. - * - * @verbatim - * Use: ALL, Declarative - * Senders: Initiator and target - * Scope: CO - * MaxRecvDataSegmentLength= - * Default is 8192 bytes. - * @endverbatim - * The initiator or target declares the maximum data segment length in - * bytes it can receive in an iSCSI PDU.\n - * The transmitter (initiator or target) is required to send PDUs with a - * data segment that does not exceed MaxRecvDataSegmentLength of the - * receiver.\n - * A target receiver is additionally limited by MaxBurstLength for - * solicited data and FirstBurstLength for unsolicited dataAn - * initiator MUST NOT send solicited PDUs exceeding MaxBurstLength nor - * 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 ((const uint8_t *) "MaxRecvDataSegmentLength\0\0\0\0\0\0\0") - -/** - * @brief Login/Text Operational Session Text Key: Maximum burst length. - * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * Irrelevant when: SessionType=Discovery - * MaxBurstLength= - * Default is 262144 (256 KB). - * @endverbatim - * Result function is Minimum.\n - * The initiator and target negotiate the maximum SCSI data payload in - * bytes in a Data-In or a solicited Data-Out iSCSI sequence. A - * 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 ((const uint8_t *) "MaxBurstLength\0") - -/** - * @brief Login/Text Operational Session Text Key: First burst length. - * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * Irrelevant when: SessionType=Discovery - * Irrelevant when: ( InitialR2T=Yes and ImmediateData=No ) - * FirstBurstLength= - * Default is 65536 (64 KB). - * @endverbatim - * Result function is Minimum.\n - * The initiator and target negotiate the maximum amount in bytes of - * unsolicited data an iSCSI initiator may send to the target during the - * execution of a single SCSI command. This covers the immediate data - * (if any) and the sequence of unsolicited Data-Out PDUs (if any) that - * follow the command.\n - * FirstBurstLength MUST NOT exceed MaxBurstLength. - */ -#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. - * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * DefaultTime2Wait= - * Default is 2. - * @endverbatim - * Result function is Maximum.\n - * The initiator and target negotiate the minimum time, in seconds, to - * wait before attempting an explicit/implicit logout or an active task - * reassignment after an unexpected connection termination or a - * connection reset.\n - * 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 ((const uint8_t *) "DefaultTime2Wait\0\0\0\0\0\0\0") - -/** - * @brief Login/Text Operational Session Text Key: Default time to retain. - * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * DefaultTime2Retain= - * Default is 20. - * @endverbatim - * Result function is Minimum.\n - * The initiator and target negotiate the maximum time, in seconds, - * after an initial wait (Time2Wait), before which an active task - * reassignment is still possible after an unexpected connection - * termination or a connection reset.\n - * This value is also the session state timeout if the connection in - * question is the last LOGGED_IN connection in the session.\n - * 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 ((const uint8_t *) "DefaultTime2Retain\0\0\0\0\0") - -/** - * @brief Login/Text Operational Session Text Key: Maximum outstanding Ready To Transfer. - * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * MaxOutstandingR2T= - * Irrelevant when: SessionType=Discovery - * Default is 1. - * @endverbatim - * Result function is Minimum.\n - * The initiator and target negotiate the maximum number of outstanding - * R2Ts per task, excluding any implied initial R2T that might be part - * of that task. An R2T is considered outstanding until the last data - * 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 ((const uint8_t *) "MaxOutstandingR2T\0\0\0\0\0\0") - -/** - * @brief Login/Text Operational Session Text Key: Data Protocol Data Unit (PDU) in order. - * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * Irrelevant when: SessionType=Discovery - * DataPDUInOrder= - * Default is Yes. - * @endverbatim - * Result function is OR.\n - * "No" is used by iSCSI to indicate that the data PDUs within sequences - * can be in any order. "Yes" is used to indicate that data PDUs within - * sequences have to be at continuously increasing addresses and - * overlays are forbidden. - */ -#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. - * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * Irrelevant when: SessionType=Discovery - * DataSequenceInOrder= - * Default is Yes. - * @endverbatim - * Result function is OR.\n - * A data sequence is a sequence of Data-In or Data-Out PDUs that end - * with a Data-In or Data-Out PDU with the F bit set to 1. A Data-Out - * sequence is sent either unsolicited or in response to an R2T.\n - * Sequences cover an offset-range.\n - * If DataSequenceInOrder is set to No, data PDU sequences may be - * transferred in any order.\n - * If DataSequenceInOrder is set to Yes, data sequences MUST be - * transferred using continuously non-decreasing sequence offsets (R2T - * buffer offset for writes, or the smallest SCSI Data-In buffer offset - * within a read data sequence).\n - * If DataSequenceInOrder is set to Yes, a target may retry at most the - * last R2T, and an initiator may at most request retransmission for the - * last read data sequence. For this reason, if ErrorRecoveryLevel is - * 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 ((const uint8_t *) "DataSequenceInOrder\0\0\0\0") - -/** - * @brief Login/Text Operational Session Text Key: Error recovery level. - * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * ErrorRecoveryLevel= - * Default is 0. - * @endverbatim - * Result function is Minimum.\n - * The initiator and target negotiate the recovery level supported. - * Recovery levels represent a combination of recovery capabilities. - * Each recovery level includes all the capabilities of the lower - * recovery levels and adds some new ones to them.\n - * In the description of recovery mechanisms, certain recovery classes - * are specified. - */ -#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. - * - * @verbatim - * Use: ALL - * Senders: Initiator and target - * Scope: specific key dependent - * X-reversed.vendor.dns_name.do_something= - * @endverbatim - * Keys with this format are used for private extension purposes. These - * keys always start with X- if unregistered with IANA (private). New - * public keys (if registered with IANA via an IETF Review RFC5226) no - * longer have an X# name prefix requirement; implementers may propose - * any intuitive unique name.\n - * For unregistered keys, to identify the vendor we suggest using the - * reversed DNS-name as a prefix to the key-proper.\n - * The part of key-name following X- MUST conform to the format for - * key-name.\n - * 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 ((const uint8_t *) "X-reversed.vendor\0\0\0\0\0\0") - -/** - * @brief Login/Text Operational Session Text Key: Task reporting. - * - * @verbatim - * Use: LO - * Senders: Initiator and target - * Scope: SW - * Irrelevant when: SessionType=Discovery - * TaskReporting= - * Default is RFC3720. - * @endverbatim - * This key is used to negotiate the task completion reporting semantics - * from the SCSI target. The following table describes the semantics - * that an iSCSI target MUST support for respective negotiated key - * values. Whenever this key is negotiated, at least the RFC3720 and - * ResponseFence values MUST be offered as options by the negotiation - * originator. - * Name | Description - * :-------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------ - * | RFC3720 | RFC 3720-compliant semantics. Response fencing is not guaranteed, and fast completion of multi-task aborting is not supported. - * | ResponseFence | Response Fence semantics MUST be supported in reporting task completions. - * | FastAbort | Updated fast multi-task abort semantics defined in MUST be supported. Support for the Response. Fence is implied - i.e., semantics MUST be supported as well. - * - * 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 ((const uint8_t *) "TaskReporting\0\0") - -/** - * @brief Login/Text Operational Session Text Key: X Node architecture. - * - * @verbatim - * Use: LO, Declarative - * Senders: Initiator and target - * Scope: SW - * X#NodeArchitecture= - * Default is None. - * Examples: - * X#NodeArchitecture=ExampleOS/v1234,ExampleInc_SW_Initiator/1.05a - * X#NodeArchitecture=ExampleInc_HW_Initiator/4010,Firmware/2.0.0.5 - * X#NodeArchitecture=ExampleInc_SW_Initiator/2.1,CPU_Arch/i686 - * @endverbatim - * This document does not define the structure or content of the list of - * values.\n - * The initiator or target declares the details of its iSCSI node - * architecture to the remote endpoint. These details may include, but - * are not limited to, iSCSI vendor software, firmware, or hardware - * versions; the OS version; or hardware architecture. This key may be - * declared on a Discovery session or a Normal session.\n - * The length of the key value (total length of the list-of-values) MUST - * NOT be greater than 255 bytes.\n - * X#NodeArchitecture MUST NOT be redeclared during the Login Phase.\n - * Functional behavior of the iSCSI node (this includes the iSCSI - * protocol logic - the SCSI, iSCSI, and TCP/IP protocols) MUST NOT - * depend on the presence, absence, or content of the X#NodeArchitecture - * key. The key MUST NOT be used by iSCSI nodes for interoperability or - * for exclusion of other nodes. To ensure proper use, key values - * SHOULD be set by the node itself, and there SHOULD NOT be provisions - * for the key values to contain user-defined text.\n - * Nodes implementing this key MUST choose one of the following - * implementation options:\n - * - only transmit the key, - * - only log the key values received from other nodes, or - * - both transmit and log the key values. - * - * Each node choosing to implement transmission of the key values MUST - * be prepared to handle the response of iSCSI nodes that do not - * understand the key.\n - * Nodes that implement transmission and/or logging of the key values - * may also implement administrative mechanisms that disable and/or - * change the logging and key transmission details.\n - * Thus, a valid behavior for this key may be that a node is completely - * 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 ((const uint8_t *) "X#NodeArchitecture\0\0\0\0\0") - -/** - * @brief Login/Text Operational Session Text Key: IFMarker (obseleted). - * - * This document obsoletes the following keys defined in RFC3720:\n - * IFMarker, OFMarker, OFMarkInt, and IFMarkInt. However, iSCSI - * mplementations compliant to this document may still receive these - * obsoleted keys - i.e., in a responder role - in a text negotiation.\n - * When an IFMarker or OFMarker key is received, a compliant iSCSI - * implementation SHOULD respond with the constant "Reject" value. The - * implementation MAY alternatively respond with a "No" value.\n - * However, the implementation MUST NOT respond with a "NotUnderstood" - * value for either of these keys.\n - * When an IFMarkInt or OFMarkInt key is received, a compliant iSCSI - * implementation MUST respond with the constant "Reject" value. The - * implementation MUST NOT respond with a "NotUnderstood" value for - * either of these keys. - */ -#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). - * - * This document obsoletes the following keys defined in RFC3720:\n - * IFMarker, OFMarker, OFMarkInt, and IFMarkInt. However, iSCSI - * mplementations compliant to this document may still receive these - * obsoleted keys - i.e., in a responder role - in a text negotiation.\n - * When an IFMarker or OFMarker key is received, a compliant iSCSI - * implementation SHOULD respond with the constant "Reject" value. The - * implementation MAY alternatively respond with a "No" value.\n - * However, the implementation MUST NOT respond with a "NotUnderstood" - * value for either of these keys.\n - * When an IFMarkInt or OFMarkInt key is received, a compliant iSCSI - * implementation MUST respond with the constant "Reject" value. The - * implementation MUST NOT respond with a "NotUnderstood" value for - * either of these keys. - */ -#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). - * - * This document obsoletes the following keys defined in RFC3720:\n - * IFMarker, OFMarker, OFMarkInt, and IFMarkInt. However, iSCSI - * mplementations compliant to this document may still receive these - * obsoleted keys - i.e., in a responder role - in a text negotiation.\n - * When an IFMarker or OFMarker key is received, a compliant iSCSI - * implementation SHOULD respond with the constant "Reject" value. The - * implementation MAY alternatively respond with a "No" value.\n - * However, the implementation MUST NOT respond with a "NotUnderstood" - * value for either of these keys.\n - * When an IFMarkInt or OFMarkInt key is received, a compliant iSCSI - * implementation MUST respond with the constant "Reject" value. The - * implementation MUST NOT respond with a "NotUnderstood" value for - * either of these keys. - */ -#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). - * - * This document obsoletes the following keys defined in RFC3720:\n - * IFMarker, OFMarker, OFMarkInt, and IFMarkInt. However, iSCSI - * mplementations compliant to this document may still receive these - * obsoleted keys - i.e., in a responder role - in a text negotiation.\n - * When an IFMarker or OFMarker key is received, a compliant iSCSI - * implementation SHOULD respond with the constant "Reject" value. The - * implementation MAY alternatively respond with a "No" value.\n - * However, the implementation MUST NOT respond with a "NotUnderstood" - * value for either of these keys.\n - * When an IFMarkInt or OFMarkInt key is received, a compliant iSCSI - * implementation MUST respond with the constant "Reject" value. The - * implementation MUST NOT respond with a "NotUnderstood" value for - * either of these keys. - */ -#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. #define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION 0x0 @@ -8436,119 +4355,119 @@ typedef struct __attribute__((packed)) iscsi_isid { * Login Requests are always considered as immediate. */ typedef struct __attribute__((packed)) iscsi_login_req_packet { - /// Always 0x03 according to iSCSI specification. - uint8_t opcode; - - /// Login request flags. - int8_t flags; - - /** - * @brief Version-max indicates the maximum version number supported. - * - * All Login Requests within the Login Phase MUST carry the same - * Version-max. Currently, this is always 0.\n - * The target MUST use the value presented with the first Login Request. - */ - uint8_t version_max; - - /** - * @brief Version-min indicates the minimum version number supported. - * - * All Login Requests within the Login Phase MUST carry the same - * Version-min. The target MUST use the value presented with the first - * Login Request. Always 0 for now. - */ - uint8_t version_min; - - /// TotalAHSLength. - uint8_t total_ahs_len; - - /// DataSegmentLength. - uint8_t ds_len[3]; - - /// Initiator Session ID (ISID). - iscsi_isid isid; - - /** - * @brief Target Session Identifying Handle (TSIH). - * - * The TSIH must be set in the first Login Request. The reserved value - * 0 MUST be used on the first connection for a new session. Otherwise, - * the TSIH sent by the target at the conclusion of the successful login - * of the first connection for this session MUST be used. The TSIH - * identifies to the target the associated existing session for this new - * connection.\n - * All Login Requests within a Login Phase MUST carry the same TSIH. - * The target MUST check the value presented with the first Login - * Request. - */ - uint16_t tsih; - - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; - - /** - * @brief Connection ID (CID). - * - * The CID provides a unique ID for this connection within the session.\n - * All Login Requests within the Login Phase MUST carry the same CID. - * The target MUST use the value presented with the first Login Request.\n - * A Login Request with a non-zero TSIH and a CID equal to that of an - * existing connection implies a logout of the connection followed by a - * login. - */ - uint16_t cid; - - /// Reserved for future usage, always MUST be 0. - uint16_t reserved; - - /** - * @brief CmdSN. - * - * The CmdSN is either the initial command sequence number of a session - * (for the first Login Request of a session - the "leading" login) or - * the command sequence number in the command stream if the login is for - * a new connection in an existing session.\n - * Examples: - * - Login on a leading connection: If the leading login carries the - * CmdSN 123, all other Login Requests in the same Login Phase carry - * the CmdSN 123, and the first non-immediate command in the Full - * Feature Phase also carries the CmdSN 123. - * - Login on other than a leading connection: If the current CmdSN at - * the time the first login on the connection is issued is 500, then - * that PDU carries CmdSN=500. Subsequent Login Requests that are - * needed to complete this Login Phase may carry a CmdSN higher than - * 500 if non-immediate requests that were issued on other connections - * in the same session advance the CmdSN. - * - * If the Login Request is a leading Login Request, the target MUST use - * the value presented in the CmdSN as the target value for the - * ExpCmdSN. - */ - uint32_t cmd_sn; - - /** - * @brief ExpStatSN. - * - * For the first Login Request on a connection, this is the ExpStatSN - * for the old connection, and this field is only valid if the Login - * Request restarts a connection.\n - * For subsequent Login Requests, it is used to acknowledge the Login - * Responses with their increasing StatSN values. - */ - uint32_t exp_stat_sn; - - /// Reserved for future usage, always MUST be 0. - uint64_t reserved2[2]; - - /** - * @brief Data segment - Login Parameters in Text Request Format. - * - * The initiator MUST provide some basic parameters in order - * 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_scsi_ds_cmd_data ds_cmd_data; + /// Always 0x03 according to iSCSI specification. + uint8_t opcode; + + /// Login request flags. + int8_t flags; + + /** + * @brief Version-max indicates the maximum version number supported. + * + * All Login Requests within the Login Phase MUST carry the same + * Version-max. Currently, this is always 0.\n + * The target MUST use the value presented with the first Login Request. + */ + uint8_t version_max; + + /** + * @brief Version-min indicates the minimum version number supported. + * + * All Login Requests within the Login Phase MUST carry the same + * Version-min. The target MUST use the value presented with the first + * Login Request. Always 0 for now. + */ + uint8_t version_min; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// Initiator Session ID (ISID). + iscsi_isid isid; + + /** + * @brief Target Session Identifying Handle (TSIH). + * + * The TSIH must be set in the first Login Request. The reserved value + * 0 MUST be used on the first connection for a new session. Otherwise, + * the TSIH sent by the target at the conclusion of the successful login + * of the first connection for this session MUST be used. The TSIH + * identifies to the target the associated existing session for this new + * connection.\n + * All Login Requests within a Login Phase MUST carry the same TSIH. + * The target MUST check the value presented with the first Login + * Request. + */ + uint16_t tsih; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /** + * @brief Connection ID (CID). + * + * The CID provides a unique ID for this connection within the session.\n + * All Login Requests within the Login Phase MUST carry the same CID. + * The target MUST use the value presented with the first Login Request.\n + * A Login Request with a non-zero TSIH and a CID equal to that of an + * existing connection implies a logout of the connection followed by a + * login. + */ + uint16_t cid; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /** + * @brief CmdSN. + * + * The CmdSN is either the initial command sequence number of a session + * (for the first Login Request of a session - the "leading" login) or + * the command sequence number in the command stream if the login is for + * a new connection in an existing session.\n + * Examples: + * - Login on a leading connection: If the leading login carries the + * CmdSN 123, all other Login Requests in the same Login Phase carry + * the CmdSN 123, and the first non-immediate command in the Full + * Feature Phase also carries the CmdSN 123. + * - Login on other than a leading connection: If the current CmdSN at + * the time the first login on the connection is issued is 500, then + * that PDU carries CmdSN=500. Subsequent Login Requests that are + * needed to complete this Login Phase may carry a CmdSN higher than + * 500 if non-immediate requests that were issued on other connections + * in the same session advance the CmdSN. + * + * If the Login Request is a leading Login Request, the target MUST use + * the value presented in the CmdSN as the target value for the + * ExpCmdSN. + */ + uint32_t cmd_sn; + + /** + * @brief ExpStatSN. + * + * For the first Login Request on a connection, this is the ExpStatSN + * for the old connection, and this field is only valid if the Login + * Request restarts a connection.\n + * For subsequent Login Requests, it is used to acknowledge the Login + * Responses with their increasing StatSN values. + */ + uint32_t exp_stat_sn; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2[2]; + + /** + * @brief Data segment - Login Parameters in Text Request Format. + * + * The initiator MUST provide some basic parameters in order + * 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_scsi_ds_cmd_data ds_cmd_data; } iscsi_login_req_packet; @@ -8795,107 +4714,107 @@ typedef struct __attribute__((packed)) iscsi_login_req_packet { * Phase. */ typedef struct __attribute__((packed)) iscsi_login_response_packet { - /// Always 0x23 according to iSCSI specification. - uint8_t opcode; - - /// Login response flags. - int8_t flags; - - /** - * @brief This is the highest version number supported by the target. - * - * All Login Responses within the Login Phase MUST carry the same - * Version-max. - */ - uint8_t version_max; - - /** - * @brief Version-active indicates the highest version supported by the target and initiator. - * - * If the target does not support a version within the - * range specified by the initiator, the target rejects the login and - * this field indicates the lowest version supported by the target. - * All Login Responses within the Login Phase MUST carry the same - * Version-active.\n - * The initiator MUST use the value presented as a response to the first - * Login Request. - */ - uint8_t version_active; - - /// TotalAHSLength. - uint8_t total_ahs_len; - - /// DataSegmentLength. - uint8_t ds_len[3]; - - /// Initiator Session ID (ISID). - iscsi_isid isid; - - /** - * @brief Target Session Identifying Handle (TSIH). - * - * The TSIH is the target-assigned session-identifying handle. Its - * internal format and content are not defined by this protocol, except - * for the value 0, which is reserved. With the exception of the Login - * Final-Response in a new session, this field should be set to the TSIH - * provided by the initiator in the Login Request. For a new session, - * the target MUST generate a non-zero TSIH and ONLY return it in the - * Login Final-Response. - */ - uint16_t tsih; - - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; - - /// Reserved for future usage, always MUST be 0. - uint32_t reserved; - - /** - * @brief StatSN. - * - * For the first Login Response (the response to the first Login - * Request), this is the starting status sequence number for the - * connection. The next response of any kind - including the next - * Login Response, if any, in the same Login Phase - will carry this - * number + 1. This field is only valid if the Status-Class is 0. - */ - uint32_t stat_sn; - - /// ExpCmdSN. - uint32_t exp_cmd_sn; - - /// MaxCmdSN. - uint32_t max_cmd_sn; - - /** - * @brief Status-class. - * - * Status-class (see above for details). If the Status-Class is - * not 0, the initiator and target MUST close the TCP connection - * If the target wishes to reject the Login Request for more than one - * reason, it should return the primary reason for the rejection. - */ - uint8_t status_class; - - /// Status-detail. - uint8_t status_detail; - - /// Reserved for future usage, always MUST be 0. - uint16_t reserved2; - - /// Reserved for future usage, always MUST be 0. - uint64_t reserved3; - - /** - * @brief Data segment - Login Parameters in Text Request Format. - * - * The target MUST provide some basic parameters in order to enable the - * initiator to determine if it is connected to the correct port and the - * initial text parameters for the security exchange.\n - * All the rules specified for Text Responses also hold for Login - * Responses. - */ - iscsi_scsi_ds_cmd_data ds_cmd_data; + /// Always 0x23 according to iSCSI specification. + uint8_t opcode; + + /// Login response flags. + int8_t flags; + + /** + * @brief This is the highest version number supported by the target. + * + * All Login Responses within the Login Phase MUST carry the same + * Version-max. + */ + uint8_t version_max; + + /** + * @brief Version-active indicates the highest version supported by the target and initiator. + * + * If the target does not support a version within the + * range specified by the initiator, the target rejects the login and + * this field indicates the lowest version supported by the target. + * All Login Responses within the Login Phase MUST carry the same + * Version-active.\n + * The initiator MUST use the value presented as a response to the first + * Login Request. + */ + uint8_t version_active; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// Initiator Session ID (ISID). + iscsi_isid isid; + + /** + * @brief Target Session Identifying Handle (TSIH). + * + * The TSIH is the target-assigned session-identifying handle. Its + * internal format and content are not defined by this protocol, except + * for the value 0, which is reserved. With the exception of the Login + * Final-Response in a new session, this field should be set to the TSIH + * provided by the initiator in the Login Request. For a new session, + * the target MUST generate a non-zero TSIH and ONLY return it in the + * Login Final-Response. + */ + uint16_t tsih; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved; + + /** + * @brief StatSN. + * + * For the first Login Response (the response to the first Login + * Request), this is the starting status sequence number for the + * connection. The next response of any kind - including the next + * Login Response, if any, in the same Login Phase - will carry this + * number + 1. This field is only valid if the Status-Class is 0. + */ + uint32_t stat_sn; + + /// ExpCmdSN. + uint32_t exp_cmd_sn; + + /// MaxCmdSN. + uint32_t max_cmd_sn; + + /** + * @brief Status-class. + * + * Status-class (see above for details). If the Status-Class is + * not 0, the initiator and target MUST close the TCP connection + * If the target wishes to reject the Login Request for more than one + * reason, it should return the primary reason for the rejection. + */ + uint8_t status_class; + + /// Status-detail. + uint8_t status_detail; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved2; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved3; + + /** + * @brief Data segment - Login Parameters in Text Request Format. + * + * The target MUST provide some basic parameters in order to enable the + * initiator to determine if it is connected to the correct port and the + * initial text parameters for the security exchange.\n + * All the rules specified for Text Responses also hold for Login + * Responses. + */ + iscsi_scsi_ds_cmd_data ds_cmd_data; } iscsi_login_response_packet; @@ -9003,82 +4922,79 @@ typedef struct __attribute__((packed)) iscsi_login_response_packet { * recovery, unless the session is also closed. */ typedef struct __attribute__((packed)) iscsi_logout_req_packet { - /// Always 6 according to iSCSI specification. - uint8_t opcode; - - /** - * @brief Reason code. - * - * A target implicitly terminates the active tasks due to the iSCSI - * protocol in the following cases: - * -# When a connection is implicitly or explicitly logged out with - * the reason code "close the connection" and there are active - * tasks allegiant to that connection. - * -# When a connection fails and eventually the connection state - * times out and there are active tasks allegiant to that - * connection - * -# When a successful recovery Logout is performed while there are - * active tasks allegiant to that connection and those tasks - * eventually time out after the Time2Wait and Time2Retain periods - * without allegiance reassignment - * -# When a connection is implicitly or explicitly logged out with - * the reason code "close the session" and there are active tasks - * in that session - * - * If the tasks terminated in any of the above cases are SCSI tasks, - * they must be internally terminated as if with CHECK CONDITION status. - * This status is only meaningful for appropriately handling the - * internal SCSI state and SCSI side effects with respect to ordering, - * because this status is never communicated back as a terminating - * status to the initiator. However, additional actions may have to be - * taken at the SCSI level, depending on the SCSI context as defined by - * the SCSI standards (e.g., queued commands and ACA; UA for the next - * command on the I_T nexus in cases a), b), and c) above). After the - * tasks are terminated, the target MUST report a Unit Attention condition - * on the next command processed on any connection for each affected - * I_T_L nexus with the status of CHECK CONDITION, the ASC/ASCQ value - * of 0x47 / 0x7F ("SOME COMMANDS CLEARED BY ISCSI PROTOCOL EVENT"), etc. - */ - int8_t reason_code; - - /// Reserved for future usage, always MUST be 0. - uint16_t reserved; - - /// TotalAHSLength (MUST be 0 for this PDU). - uint8_t total_ahs_len; - - /// DataSegmentLength (MUST be 0 for this PDU). - uint8_t ds_len[3]; - - /// Reserved for future usage, always MUST be 0. - uint64_t reserved2; - - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; - - /** - * @brief Connection ID (CID). - * - * This is the connection ID of the connection to be closed (including - * closing the TCP stream). This field is only valid if the reason code - * is not "close the session". - */ - uint16_t cid; - - /// Reserved for future usage, always MUST be 0. - uint16_t reserved3; - - /// CmdSN. - uint32_t cmd_sn; - - /// This is the last ExpStatSN value for the connection to be closed. - uint32_t exp_stat_sn; - - /// Reserved for future usage, always MUST be 0. - uint64_t reserved4[2]; - - /// Optional header digest. - iscsi_header_digest hdr_digest; + /// Always 6 according to iSCSI specification. + uint8_t opcode; + + /** + * @brief Reason code. + * + * A target implicitly terminates the active tasks due to the iSCSI + * protocol in the following cases: + * -# When a connection is implicitly or explicitly logged out with + * the reason code "close the connection" and there are active + * tasks allegiant to that connection. + * -# When a connection fails and eventually the connection state + * times out and there are active tasks allegiant to that + * connection + * -# When a successful recovery Logout is performed while there are + * active tasks allegiant to that connection and those tasks + * eventually time out after the Time2Wait and Time2Retain periods + * without allegiance reassignment + * -# When a connection is implicitly or explicitly logged out with + * the reason code "close the session" and there are active tasks + * in that session + * + * If the tasks terminated in any of the above cases are SCSI tasks, + * they must be internally terminated as if with CHECK CONDITION status. + * This status is only meaningful for appropriately handling the + * internal SCSI state and SCSI side effects with respect to ordering, + * because this status is never communicated back as a terminating + * status to the initiator. However, additional actions may have to be + * taken at the SCSI level, depending on the SCSI context as defined by + * the SCSI standards (e.g., queued commands and ACA; UA for the next + * command on the I_T nexus in cases a), b), and c) above). After the + * tasks are terminated, the target MUST report a Unit Attention condition + * on the next command processed on any connection for each affected + * I_T_L nexus with the status of CHECK CONDITION, the ASC/ASCQ value + * of 0x47 / 0x7F ("SOME COMMANDS CLEARED BY ISCSI PROTOCOL EVENT"), etc. + */ + int8_t reason_code; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength (MUST be 0 for this PDU). + uint8_t total_ahs_len; + + /// DataSegmentLength (MUST be 0 for this PDU). + uint8_t ds_len[3]; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /** + * @brief Connection ID (CID). + * + * This is the connection ID of the connection to be closed (including + * closing the TCP stream). This field is only valid if the reason code + * is not "close the session". + */ + uint16_t cid; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved3; + + /// CmdSN. + uint32_t cmd_sn; + + /// This is the last ExpStatSN value for the connection to be closed. + uint32_t exp_stat_sn; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved4[2]; } iscsi_logout_req_packet; @@ -9105,272 +5021,90 @@ typedef struct __attribute__((packed)) iscsi_logout_req_packet { * was session close). */ typedef struct __attribute__((packed)) iscsi_logout_response_packet { - /// Always 0x26 according to iSCSI specification. - uint8_t opcode; - - /// Reserved for future usage (always MUST be 0x80 for now). - int8_t flags; - - /// Response. - uint8_t response; - - /// Reserved for future usage, always MUST be 0. - uint8_t reserved; - - /// TotalAHSLength (MUST be 0 for this PDU). - uint8_t total_ahs_len; - - /// DataSegmentLength (MUST be 0 for this PDU). - uint8_t ds_len[3]; - - /// Reserved for future usage, always MUST be 0. - uint64_t reserved2; - - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; - - /// Reserved for future usage, always MUST be 0. - uint32_t reserved3; - - /// StatSN. - uint32_t stat_sn; - - /// ExpCmdSN. - uint32_t exp_cmd_sn; - - /// MaxCmdSN. - uint32_t max_cmd_sn; - - /// Reserved for future usage, always MUST be 0. - uint32_t reserved4; - - /** - * @brief Time2Wait. - * - * If the Logout response code is 0 and the operational - * ErrorRecoveryLevel is 2, this is the minimum amount of time, in - * seconds, to wait before attempting task reassignment. If the Logout - * response code is 0 and the operational ErrorRecoveryLevel is less - * than 2, this field is to be ignored.\n - * This field is invalid if the Logout response code is 1.\n - * If the Logout response code is 2 or 3, this field specifies the - * minimum time to wait before attempting a new implicit or explicit - * logout.\n - * If Time2Wait is 0, the reassignment or a new Logout may be attempted - * immediately. - */ - uint16_t time_wait; - - /** - * @brief Time2Retain. - * - * If the Logout response code is 0 and the operational - * ErrorRecoveryLevel is 2, this is the maximum amount of time, in - * seconds, after the initial wait (Time2Wait) that the target waits for - * the allegiance reassignment for any active task, after which the task - * state is discarded. If the Logout response code is 0 and the - * operational ErrorRecoveryLevel is less than 2, this field is to be - * ignored.\n - * This field is invalid if the Logout response code is 1.\n - * If the Logout response code is 2 or 3, this field specifies the - * maximum amount of time, in seconds, after the initial wait - * (Time2Wait) that the target waits for a new implicit or explicit - * logout.\n - * If it is the last connection of a session, the whole session state is - * discarded after Time2Retain.\n - * If Time2Retain is 0, the target has already discarded the connection - * (and possibly the session) state along with the task states. No - * reassignment or Logout is required in this case. - */ - uint16_t time_retain; - - /// Reserved for future usage, always MUST be 0. - uint32_t reserved5; - - /// Optional header digest. - iscsi_header_digest hdr_digest; + /// Always 0x26 according to iSCSI specification. + uint8_t opcode; + + /// Reserved for future usage (must be always 0x80 for now). + int8_t flags; + + /// Response. + uint8_t response; + + /// Reserved for future usage, always MUST be 0. + uint8_t reserved; + + /// TotalAHSLength (MUST be 0 for this PDU). + uint8_t total_ahs_len; + + /// DataSegmentLength (MUST be 0 for this PDU). + uint8_t ds_len[3]; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved3; + + /// StatSN. + uint32_t stat_sn; + + /// ExpCmdSN. + uint32_t exp_cmd_sn; + + /// MaxCmdSN. + uint32_t max_cmd_sn; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved4; + + /** + * @brief Time2Wait. + * + * If the Logout response code is 0 and the operational + * ErrorRecoveryLevel is 2, this is the minimum amount of time, in + * seconds, to wait before attempting task reassignment. If the Logout + * response code is 0 and the operational ErrorRecoveryLevel is less + * than 2, this field is to be ignored.\n + * This field is invalid if the Logout response code is 1.\n + * If the Logout response code is 2 or 3, this field specifies the + * minimum time to wait before attempting a new implicit or explicit + * logout.\n + * If Time2Wait is 0, the reassignment or a new Logout may be attempted + * immediately. + */ + uint16_t time_wait; + + /** + * @brief Time2Retain. + * + * If the Logout response code is 0 and the operational + * ErrorRecoveryLevel is 2, this is the maximum amount of time, in + * seconds, after the initial wait (Time2Wait) that the target waits for + * the allegiance reassignment for any active task, after which the task + * state is discarded. If the Logout response code is 0 and the + * operational ErrorRecoveryLevel is less than 2, this field is to be + * ignored.\n + * This field is invalid if the Logout response code is 1.\n + * If the Logout response code is 2 or 3, this field specifies the + * maximum amount of time, in seconds, after the initial wait + * (Time2Wait) that the target waits for a new implicit or explicit + * logout.\n + * If it is the last connection of a session, the whole session state is + * discarded after Time2Retain.\n + * If Time2Retain is 0, the target has already discarded the connection + * (and possibly the session) state along with the task states. No + * reassignment or Logout is required in this case. + */ + uint16_t time_retain; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved5; } iscsi_logout_response_packet; -/// Selective Negative / Sequence Number Acknowledgment (SNACK) request: Data/R2T SNACK: requesting retransmission of one or more Data-In or R2T PDUs. -#define ISCSI_SNACK_REQ_TYPE_DATA_R2T_SNACK 0x00 - -/// Selective Negative / Sequence Number Acknowledgment (SNACK) request: -#define ISCSI_SNACK_REQ_TYPE_STATUS_SNACK 0x01 // Status SNACK: requesting retransmission of one or more - // numbered responses - -/** - * @brief Selective Negative / Sequence Number Acknowledgment (SNACK) request: DataACK: positively acknowledges Data-In PDUs. - * - * If an initiator operates at ErrorRecoveryLevel 1 or higher, it MUST - * issue a SNACK of type DataACK after receiving a Data-In PDU with the - * A bit set to 1. However, if the initiator has detected holes in the - * input sequence, it MUST postpone issuing the SNACK of type DataACK - * until the holes are filled. An initiator MAY ignore the A bit if it - * deems that the bit is being set aggressively by the target (i.e., - * before the MaxBurstLength limit is reached).\n - * The DataACK is used to free resources at the target and not to - * request or imply data retransmission.\n - * An initiator MUST NOT request retransmission for any data it had - * already acknowledged - */ -#define ISCSI_SNACK_REQ_TYPE_DATA_ACK 0x02 - -/** - * @brief Selective Negative / Sequence Number Acknowledgment (SNACK) request: R-Data SNACK: requesting retransmission of Data-In PDUs with possible resegmentation and status tagging. - * - * If the initiator MaxRecvDataSegmentLength changed between the - * original transmission and the time the initiator requests - * retransmission, the initiator MUST issue a R-Data SNACK.\n - * With R-Data SNACK, the initiator indicates that it discards all the - * unacknowledged data and expects the target to resend it. It also - * expects resegmentation. In this case, the retransmitted Data-In PDUs - * MAY be different from the ones originally sent in order to reflect - * changes in MaxRecvDataSegmentLength. Their DataSN starts with the - * BegRun of the last DataACK received by the target if any was received; - * otherwise, it starts with 0 and is increased by 1 for each resent - * Data-In PDU.\n - * A target that has received a R-Data SNACK MUST return a SCSI Response - * that contains a copy of the SNACK Tag field from the R-Data SNACK in - * the SCSI Response SNACK Tag field as its last or only Response. For - * example, if it has already sent a response containing another value - * in the SNACK Tag field or had the status included in the last Data-In - * PDU, it must send a new SCSI Response PDU. If a target sends more - * than one SCSI Response PDU due to this rule, all SCSI Response PDUs - * must carry the same StatSN. If an initiator attempts to recover a lost - * SCSI Response when more than one response has been sent, the - * target will send the SCSI Response with the latest content known to - * the target, including the last SNACK Tag for the command.\n - * For considerations in allegiance reassignment of a task to a - * connection with a different MaxRecvDataSegmentLength. - */ -#define ISCSI_SNACK_REQ_TYPE_R_DATA_SNACK 0x03 - - -/** - * @brief iSCSI SNACK Request packet data. - * - * If the implementation supports ErrorRecoveryLevel greater than zero, - * it MUST support all SNACK types. - * - * The SNACK is used by the initiator to request the retransmission of - * numbered responses, data, or R2T PDUs from the target. The SNACK - * Request indicates the numbered responses or data "runs" whose - * retransmission is requested, where the run starts with the first - * StatSN, DataSN, or R2TSN whose retransmission is requested and - * indicates the number of Status, Data, or R2T PDUs requested, - * including the first. 0 has special meaning when used as a starting - * number and length: - * - * - When used in RunLength, it means all PDUs starting with the - * initial. - * - * - When used in both BegRun and RunLength, it means all - * unacknowledged PDUs. - * - * The numbered response(s) or R2T(s) requested by a SNACK MUST be - * delivered as exact replicas of the ones that the target transmitted - * originally, except for the fields ExpCmdSN, MaxCmdSN, and ExpDataSN, - * which MUST carry the current values. R2T(s)requested by SNACK MUST - * also carry the current value of the StatSN. - * - * The numbered Data-In PDUs requested by a Data SNACK MUST be delivered - * as exact replicas of the ones that the target transmitted originally, - * except for the fields ExpCmdSN and MaxCmdSN, which MUST carry the - * current values; and except for resegmentation. - * - * Any SNACK that requests a numbered response, data, or R2T that was - * not sent by the target or was already acknowledged by the initiator - * MUST be rejected with a reason code of "Protocol Error". - */ -typedef struct __attribute__((packed)) iscsi_snack_req_packet { - /// Always 0x10 according to iSCSI specification. - uint8_t opcode; - - /** - * @brief Type. - * - * Data/R2T SNACK, Status SNACK, or R-Data SNACK for a command MUST - * precede status acknowledgment for the given command. - */ - int8_t type; - - /// Reserved for future usage, always MUST be 0. - uint16_t reserved; - - /// TotalAHSLength. - uint8_t total_ahs_len; - - /// DataSegmentLength. - uint8_t ds_len[3]; - - /// LUN or Reserved. - uint64_t lun; - - /** - * @brief Initiator Task Tag (ITT). - * - * For a Status SNACK and DataACK, the Initiator Task Tag MUST be set to - * the reserved value 0xFFFFFFFF. In all other cases, the Initiator - * Task Tag field MUST be set to the Initiator Task Tag of the - * referenced command. - */ - uint32_t init_task_tag; - - /** - * @brief Target Transfer Tag (TTT). - * - * For a R-Data SNACK, this field MUST contain a value that is different - * from 0 or 0xFFFFFFFF and is unique for the task (identified by the - * Initiator Task Tag). This value MUST be copied by the iSCSI target - * in the last or only SCSI Response PDU it issues for the command.\n - * For DataACK, the Target Transfer Tag MUST contain a copy of the - * Target Transfer Tag and LUN provided with the SCSI Data-In PDU with - * the A bit set to 1.\n - * In all other cases, the Target Transfer Tag field MUST be set to the - * reserved value 0xFFFFFFFF. - */ - uint32_t target_xfer_snack_tag; - - /// Reserved for future usage, always MUST be 0. - uint32_t reserved2; - - /// ExpStatSN. - uint32_t exp_stat_sn; - - /// Reserved for future usage, always MUST be 0. - uint32_t reserved3; - - /** - * @brief BegRun. - * - * This field indicates the DataSN, R2TSN, or StatSN of the first PDU - * whose retransmission is requested (Data/R2T and Status SNACK), or the - * next expected DataSN (DataACK SNACK).\n - * A BegRun of 0, when used in conjunction with a RunLength of 0, means - * "resend all unacknowledged Data-In, R2T or Response PDUs". - * BegRun MUST be 0 for a R-Data SNACK. - */ - uint32_t beg_run; - - /** - * @brief RunLength. - * - * This field indicates the number of PDUs whose retransmission is - * requested.\n - * A RunLength of 0 signals that all Data-In, R2T, or Response PDUs - * carrying the numbers equal to or greater than BegRun have to be - * resent.\n - * The RunLength MUST also be 0 for a DataACK SNACK in addition to a - * R-Data SNACK. - */ - uint32_t run_len; - - /// Optional header digest. - iscsi_header_digest hdr_digest; -} iscsi_snack_req_packet; - - /// iSCSI Reject packet data: Reserved, original PDU can't be resent. #define ISCSI_REJECT_REASON_RESERVED 0x01 @@ -9437,100 +5171,91 @@ typedef struct __attribute__((packed)) iscsi_snack_req_packet { * packet was rejected or has been rejected for some reason. */ typedef struct __attribute__((packed)) iscsi_reject_packet { - /// Always 0x3F according to iSCSI specification. - uint8_t opcode; - - /// Reserved for future usage (always MUST be 0x80 for now). - int8_t flags; - - /** - * @brief Reject reason. - * - * In all the cases in which a pre-instantiated SCSI task is terminated - * because of the reject, the target MUST issue a proper SCSI command - * response with CHECK CONDITION. In these cases in which a status for - * the SCSI task was already sent before the reject, no additional - * status is required. If the error is detected while data from the - * initiator is still expected (i.e., the command PDU did not contain - * all the data and the target has not received a Data-Out PDU with the - * Final bit set to 1 for the unsolicited data, if any, and all - * outstanding R2Ts, if any), the target MUST wait until it receives - * the last expected Data-Out PDUs with the F bit set to 1 before - * sending the Response PDU. - */ - uint8_t reason; - - /// Reserved for future usage, always MUST be 0. - uint8_t reserved; - - /// TotalAHSLength. - uint8_t total_ahs_len; - - /// DataSegmentLength. - uint8_t ds_len[3]; - - /// Reserved for future usage, always MUST be 0. - uint64_t reserved2; - - /// Always 0xFFFFFFFF for now. - uint32_t tag; - - /// Reserved for future usage, always MUST be 0. - uint32_t reserved3; - - /** - * @brief StatSN. - * - * This field carries its usual value and is not related to the - * rejected command. The StatSN is advanced after a Reject. - */ - uint32_t stat_sn; - - /** - * @brief ExpCmdSN. - * - * This field carries its usual value and is not related to the - * rejected command. - */ - uint32_t exp_cmd_sn; - - /** - * @brief MaxCmdSN. - * - * This field carries its usual value and is not related to the - * rejected command. - */ - uint32_t max_cmd_sn; - - /** - * @brief DataSN / Ready To Transfer Sequence Number (R2TSN) or Reserved. - * - * This field is only valid if the rejected PDU is a Data/R2T SNACK and - * the Reject reason code is "Protocol Error". The DataSN/R2TSN is the - * next Data/R2T sequence number that the target would send for the - * task, if any. - */ - uint32_t data_r2t_sn; - - /// Reserved for future usage, always MUST be 0. - uint64_t reserved4; - - /// Optional header digest. - iscsi_header_digest hdr_digest; - - /** - * @brief Complete Header of Bad PDU. - * - * The target returns the header (not including the digest) of the - * PDU in error as the data of the response. - */ - iscsi_bhs_packet bad_pdu_hdr; - - /// Vendor-specific data (if any). - uint8_t vendor_data[0]; - - /// Optional data digest. - iscsi_data_digest data_digest; + /// Always 0x3F according to iSCSI specification. + uint8_t opcode; + + /// Reserved for future usage (must be always 0x80 for now). + int8_t flags; + + /** + * @brief Reject reason. + * + * In all the cases in which a pre-instantiated SCSI task is terminated + * because of the reject, the target MUST issue a proper SCSI command + * response with CHECK CONDITION. In these cases in which a status for + * the SCSI task was already sent before the reject, no additional + * status is required. If the error is detected while data from the + * initiator is still expected (i.e., the command PDU did not contain + * all the data and the target has not received a Data-Out PDU with the + * Final bit set to 1 for the unsolicited data, if any, and all + * outstanding R2Ts, if any), the target MUST wait until it receives + * the last expected Data-Out PDUs with the F bit set to 1 before + * sending the Response PDU. + */ + uint8_t reason; + + /// Reserved for future usage, always MUST be 0. + uint8_t reserved; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2; + + /// Always 0xFFFFFFFF for now. + uint32_t tag; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved3; + + /** + * @brief StatSN. + * + * This field carries its usual value and is not related to the + * rejected command. The StatSN is advanced after a Reject. + */ + uint32_t stat_sn; + + /** + * @brief ExpCmdSN. + * + * This field carries its usual value and is not related to the + * rejected command. + */ + uint32_t exp_cmd_sn; + + /** + * @brief MaxCmdSN. + * + * This field carries its usual value and is not related to the + * rejected command. + */ + uint32_t max_cmd_sn; + + /** + * @brief DataSN / Ready To Transfer Sequence Number (R2TSN) or Reserved. + * + * This field is only valid if the rejected PDU is a Data/R2T SNACK and + * the Reject reason code is "Protocol Error". The DataSN/R2TSN is the + * next Data/R2T sequence number that the target would send for the + * task, if any. + */ + uint32_t data_r2t_sn; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved4; + + /** + * @brief Complete Header of Bad PDU. + * + * The target returns the header (not including the digest) of the + * PDU in error as the data of the response. + */ + iscsi_bhs_packet bad_pdu_hdr; } iscsi_reject_packet; /** @@ -9555,76 +5280,76 @@ typedef struct __attribute__((packed)) iscsi_reject_packet { * in response to each other. */ typedef struct __attribute__((packed)) iscsi_nop_out_packet { - /// Always 0x00 according to iSCSI specification. - uint8_t opcode; - - /// Reserved for future usage (always MUST be 0x80 for now). - int8_t flags; - - /// Reserved for future usage, always MUST be 0. - uint16_t reserved; - - /// TotalAHSLength. - uint8_t total_ahs_len; - - /// DataSegmentLength. - uint8_t ds_len[3]; - - /// LUN or Reserved. - uint64_t lun; - - /** - * @brief Initiator Task Tag (ITT). - * - * The NOP-Out MUST have the Initiator Task Tag set to a valid value - * only if a response in the form of a NOP-In is requested (i.e., the - * NOP-Out is used as a ping request). Otherwise, the Initiator Task - * Tag MUST be set to 0xFFFFFFFF.\n - * When a target receives the NOP-Out with a valid Initiator Task Tag, - * it MUST respond with a NOP-In Response.\n - * If the Initiator Task Tag contains 0xFFFFFFFF, the I bit MUST be set - * to 1, and the CmdSN is not advanced after this PDU is sent. - */ - uint32_t init_task_tag; - - /** - * @brief Target Transfer Tag (TTT). - * - * The Target Transfer Tag is a target-assigned identifier for the - * operation.\n - * The NOP-Out MUST only have the Target Transfer Tag set if it is - * issued in response to a NOP-In with a valid Target Transfer Tag. In - * this case, it copies the Target Transfer Tag from the NOP-In PDU.\n - * Otherwise, the Target Transfer Tag MUST be set to 0xFFFFFFFF.\n - * When the Target Transfer Tag is set to a value other than 0xFFFFFFFF, - * the LUN field MUST also be copied from the NOP-In. - */ - uint32_t target_xfer_tag; - - /// CmdSN. - uint32_t cmd_sn; - - /// ExpStatSN. - uint32_t exp_stat_sn; - - /// Reserved for future usage, always MUST be 0. - uint64_t reserved2[2]; - - /// Optional header digest. - iscsi_header_digest hdr_digest; - - /** - * @brief DataSegment - Ping Data (optional). - * - * Ping data is reflected in the NOP-In Response. The length of the - * reflected data is limited to MaxRecvDataSegmentLength. The length of - * ping data is indicated by the DataSegmentLength. 0 is a valid value - * for the DataSegmentLength and indicates the absence of ping data. - */ - iscsi_scsi_ds_cmd_data ds_ping_data; - - /// Optional data digest. - iscsi_data_digest data_digest; + /// Always 0x00 according to iSCSI specification. + uint8_t opcode; + + /// Reserved for future usage (must be always 0x80 for now). + int8_t flags; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength. + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// LUN or Reserved. + uint64_t lun; + + /** + * @brief Initiator Task Tag (ITT). + * + * The NOP-Out MUST have the Initiator Task Tag set to a valid value + * only if a response in the form of a NOP-In is requested (i.e., the + * NOP-Out is used as a ping request). Otherwise, the Initiator Task + * Tag MUST be set to 0xFFFFFFFF.\n + * When a target receives the NOP-Out with a valid Initiator Task Tag, + * it MUST respond with a NOP-In Response.\n + * If the Initiator Task Tag contains 0xFFFFFFFF, the I bit MUST be set + * to 1, and the CmdSN is not advanced after this PDU is sent. + */ + uint32_t init_task_tag; + + /** + * @brief Target Transfer Tag (TTT). + * + * The Target Transfer Tag is a target-assigned identifier for the + * operation.\n + * The NOP-Out MUST only have the Target Transfer Tag set if it is + * issued in response to a NOP-In with a valid Target Transfer Tag. In + * this case, it copies the Target Transfer Tag from the NOP-In PDU.\n + * Otherwise, the Target Transfer Tag MUST be set to 0xFFFFFFFF.\n + * When the Target Transfer Tag is set to a value other than 0xFFFFFFFF, + * the LUN field MUST also be copied from the NOP-In. + */ + uint32_t target_xfer_tag; + + /// CmdSN. + uint32_t cmd_sn; + + /// ExpStatSN. + uint32_t exp_stat_sn; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2[2]; + + /// Optional header digest. + uint32_t hdr_digest; + + /** + * @brief DataSegment - Ping Data (optional). + * + * Ping data is reflected in the NOP-In Response. The length of the + * reflected data is limited to MaxRecvDataSegmentLength. The length of + * ping data is indicated by the DataSegmentLength. 0 is a valid value + * for the DataSegmentLength and indicates the absence of ping data. + */ + iscsi_scsi_ds_cmd_data ds_ping_data; + + /// Optional data digest. + uint32_t data_digest; } iscsi_nop_out_packet; @@ -9653,70 +5378,70 @@ typedef struct __attribute__((packed)) iscsi_nop_out_packet { * (DataSegmentLength MUST be 0). */ typedef struct __attribute__((packed)) iscsi_nop_in_packet { - /// Always 0x20 according to iSCSI specification. - uint8_t opcode; - - /// Reserved for future usage (always MUST be 0x80 for now). - int8_t flags; - - /// Reserved for future usage, always MUST be 0. - uint16_t reserved; - - /// TotalAHSLength - uint8_t total_ahs_len; - - /// DataSegmentLength. - uint8_t ds_len[3]; - - /// A LUN MUST be set to a correct value when the Target Transfer Tag is valid (not the reserved value 0xFFFFFFFF). - uint64_t lun; - - /// Initiator Task Tag (ITT) or 0xFFFFFFFF. - uint32_t init_task_tag; - - /** - * @brief Target Transfer Tag (TTT). - * - * If the target is responding to a NOP-Out, this field is set to the - * reserved value 0xFFFFFFFF.\n - * If the target is sending a NOP-In as a ping (intending to receive a - * corresponding NOP-Out), this field is set to a valid value (not the - * reserved value 0xFFFFFFFF).\n - * If the target is initiating a NOP-In without wanting to receive a - * corresponding NOP-Out, this field MUST hold the reserved value - * 0xFFFFFFFF. - */ - uint32_t target_xfer_tag; - - /** - * @brief StatSN. - * - * The StatSN field will always contain the next StatSN. However, when - * the Initiator Task Tag is set to 0xFFFFFFFF, the StatSN for the - * connection is not advanced after this PDU is sent. - */ - uint32_t stat_sn; - - /// ExpCmdSN. - uint32_t exp_cmd_sn; - - /// MaxCmdSN. - uint32_t max_cmd_sn; - - /// Reserved for future usage, always MUST be 0. - 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_scsi_ds_cmd_data ds_ping_data; - - /// Optional data digest. - iscsi_data_digest data_digest; + /// Always 0x20 according to iSCSI specification. + uint8_t opcode; + + /// Reserved for future usage (must be always 0x80 for now). + int8_t flags; + + /// Reserved for future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength + uint8_t total_ahs_len; + + /// DataSegmentLength. + uint8_t ds_len[3]; + + /// A LUN MUST be set to a correct value when the Target Transfer Tag is valid (not the reserved value 0xFFFFFFFF). + uint64_t lun; + + /// Initiator Task Tag (ITT) or 0xFFFFFFFF. + uint32_t init_task_tag; + + /** + * @brief Target Transfer Tag (TTT). + * + * If the target is responding to a NOP-Out, this field is set to the + * reserved value 0xFFFFFFFF.\n + * If the target is sending a NOP-In as a ping (intending to receive a + * corresponding NOP-Out), this field is set to a valid value (not the + * reserved value 0xFFFFFFFF).\n + * If the target is initiating a NOP-In without wanting to receive a + * corresponding NOP-Out, this field MUST hold the reserved value + * 0xFFFFFFFF. + */ + uint32_t target_xfer_tag; + + /** + * @brief StatSN. + * + * The StatSN field will always contain the next StatSN. However, when + * the Initiator Task Tag is set to 0xFFFFFFFF, the StatSN for the + * connection is not advanced after this PDU is sent. + */ + uint32_t stat_sn; + + /// ExpCmdSN. + uint32_t exp_cmd_sn; // ExpCmdSN + + /// MaxCmdSN. + uint32_t max_cmd_sn; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved2; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved3; + + /// Optional header digest. + uint32_t hdr_digest; + + /// DataSegment - Return Ping Data. + iscsi_scsi_ds_cmd_data ds_ping_data; + + /// Optional data digest. + uint32_t data_digest; } iscsi_nop_in_packet; @@ -9764,17 +5489,17 @@ typedef struct __attribute__((packed)) iscsi_nop_in_packet { * identifier data. */ typedef struct __attribute__((packed)) iscsi_transport_id { - /// First 4 bits are protocol ID and last 2 bits are format. - uint8_t id; + /// First 4 bits are protocol ID and last 2 bits are format. + uint8_t id; - /// Reserved for future usage (always MUST be 0). - uint8_t reserved; + /// Reserved for future usage (always MUST be 0). + uint8_t reserved; - /// Additional length of name. - uint16_t add_len; + /// Additional length of name. + uint16_t add_len; - /// Name. - uint8_t name[0]; + /// Name. + uint8_t name[0]; } iscsi_transport_id; @@ -9854,20 +5579,20 @@ typedef struct __attribute__((packed)) iscsi_transport_id { * and the iSCSI connection lookup table. */ typedef struct iscsi_key_value_pair_lut_entry { - /// Name of key. - const uint8_t *key; + /// Name of key. + const uint8_t *key; - /// Default value of the key, always in string representation. - uint8_t *value; + /// Default value of the key, always in string representation. + uint8_t *value; - /// NUL separated list of allowed string values. If key type is numeric: NUL separated minimum and maximum integer range. End is marked with another NUL. - uint8_t *list_range; + /// NUL separated list of allowed string values. If key type is numeric: NUL separated minimum and maximum integer range. End is marked with another NUL. + uint8_t *list_range; - /// Type of key and value pair. - const int type; + /// Type of key and value pair. + const int type; - /// Flags indicating special key attributes. - const int flags; + /// Flags indicating special key attributes. + const int flags; } iscsi_key_value_pair_lut_entry; @@ -9879,555 +5604,62 @@ typedef struct iscsi_key_value_pair_lut_entry { * Text or Login packet data. */ typedef struct iscsi_key_value_pair { - /// Value of the key which is stored in the hash map. + /// Value of the key which is stored in the hash map. uint8_t *value; - /// NUL separated list of allowed string values. If key type is numeric: NUL separated minimum and maximum integer range. End is marked with another NUL. - uint8_t *list_range; + /// NUL separated list of allowed string values. If key type is numeric: NUL separated minimum and maximum integer range. End is marked with another NUL. + uint8_t *list_range; - /// Type of key and value pair. - int type; + /// Type of key and value pair. + int type; - /// Flags indicating special key attributes. - int flags; + /// Flags indicating special key attributes. + int flags; - /// State bit mask. + /// State bit mask. uint state_mask; } iscsi_key_value_pair; -typedef struct iscsi_connection iscsi_connection; - -/** - * @brief iSCSI Text / Login key=value packet data construction helper. - * - * This structure is used to store the key=value plus NUL terminator - * pairs for sending as DataSegment packet data to the client. - */ -typedef struct iscsi_key_value_pair_packet { - /// Associated iSCSI connection. - iscsi_connection *conn; - - /// Current text buffer containing multiple key=value + NUL terminator pairs. - uint8_t *buf; - - /// Position of output buffer for next write. - uint32_t pos; - - /// Current length of buffer including final NUL terminator without iSCSI zero padding. - uint32_t len; - - /// Discovery mode. - int discovery; -} iscsi_key_value_pair_packet; - -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 << 0) - -/// iSCSI main global data flags: CHAP authentication is disabled. -#define ISCSI_GLOBALS_FLAGS_CHAP_DISABLE (1 << 1) - -/// iSCSI main global data flags: CHAP authentication is required. -#define ISCSI_GLOBALS_FLAGS_CHAP_REQUIRE (1 << 2) - -/// iSCSI main global data flags: CHAP authentication is mutual. -#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 - - -/** - * @brief This is the main global iSCSI structure which manages all global data. - * - * All iSCSI portal groups, target nodes, sessions and - * connections are stored here for global access. - */ -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 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; - - -/// 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 -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. - * - * When redirecting logins, there are two portal group types: public and - * private.\n - * Public portal groups return their portals during discovery session. - * A redirection private portal may also be specified for non-discovery - * logins.\n - * Private portal groups instead do not return their portals during - * the discovery session. - */ -#define ISCSI_PORTAL_GROUP_PRIVATE (1 << 0) - -/// iSCSI portal group: CHAP authentication is disabled. -#define ISCSI_PORTAL_GROUP_CHAP_DISABLE (1 << 1) - -/// iSCSI portal group: CHAP authentication is required. -#define ISCSI_PORTAL_GROUP_CHAP_REQUIRE (1 << 2) - -/// iSCSI portal group: CHAP authentication is mutual. -#define ISCSI_PORTAL_GROUP_CHAP_MUTUAL (1 << 3) - +typedef struct iscsi_connection iscsi_connection; /** - * @brief iSCSI portal group. + * @brief iSCSI Text / Login key=value packet data construction helper. * - * Portal groups are either public or private and also are used - * by CHAP authentication. + * This structure is used to store the key=value plus NUL terminator + * pairs for sending as DataSegment packet data to the client. */ -typedef struct iscsi_portal_group { - /// Hash map containing all portals associated with this iSCSI group. - iscsi_hashmap *portals; - - /// Tag value for this portal group. - uint64_t tag; - - /// Reference count. - int ref_count; +typedef struct iscsi_key_value_pair_packet { + /// Associated iSCSI connection. + iscsi_connection *conn; - /// Portal group flags. - int flags; + /// Current text buffer containing multiple key=value + NUL terminator pairs. + uint8_t *buf; - /// CHAP group id. - int32_t chap_group; -} iscsi_portal_group; + /// Position of output buffer for next write. + uint32_t pos; + /// Current length of buffer including final NUL terminator without iSCSI zero padding. + uint32_t len; -/** - * @brief iSCSI portal. - * - * iSCSI portals manage the host / IP address and port, as well - * as the associated connections. - */ -typedef struct iscsi_portal { - /// Group this portal belongs to. - iscsi_portal_group *group; + /// Discovery mode. + int discovery; +} iscsi_key_value_pair_packet; - /// Hostname / IP address of the portal. - uint8_t *host; +/// iSCSI main global data flags: Allow duplicate ISIDs. +#define ISCSI_GLOBALS_FLAGS_ISID_ALLOW_DUPLICATES (1 << 0) - /// Port of the portal. - uint8_t *port; +/// iSCSI main global data flags: CHAP authentication is disabled. +#define ISCSI_GLOBALS_FLAGS_CHAP_DISABLE (1 << 1) - /// TCP/IP socket for the portal. - int sock; -} iscsi_portal; +/// iSCSI main global data flags: CHAP authentication is required. +#define ISCSI_GLOBALS_FLAGS_CHAP_REQUIRE (1 << 2) +/// iSCSI main global data flags: CHAP authentication is mutual. +#define ISCSI_GLOBALS_FLAGS_CHAP_MUTUAL (1 << 3) -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); +/// 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; /// iSCSI SCSI status code: Good. @@ -10531,711 +5763,277 @@ void iscsi_portal_destroy(iscsi_portal *portal); /// 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; - - /// Uplink read mutex for synchronization. - pthread_mutex_t uplink_mutex; - - /// Conditional to signal uplink read complete. - pthread_cond_t uplink_cond; - - /// 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 +/// iSCSI SCSI Additional Sense Code (ASC): Unrecovered read error. +#define ISCSI_SCSI_ASC_UNRECOVERED_READ_ERR 0x11 -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 +/// iSCSI SCSI Additional Sense Code (ASC): Miscompare during verify operation. +#define ISCSI_SCSI_ASC_MISCOMPARE_DURING_VERIFY_OPERATION 0x1D -int iscsi_scsi_emu_io_block_read(iscsi_scsi_task *scsi_task, 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 *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, 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 +/// iSCSI SCSI Additional Sense Code (ASC): Invalid command operation code. +#define ISCSI_SCSI_ASC_INVALID_COMMAND_OPERATION_CODE 0x20 -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 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 port flags: In use. -#define ISCSI_PORT_FLAGS_IN_USE (1 << 0) +/// 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 -/** - * @brief iSCSI port. - * - * This structure maintains the transport ID, - * name, identifiers and index of an ISCSI - * port. - */ -typedef struct iscsi_port { - /// Transport ID. - iscsi_transport_id *transport_id; +/// iSCSI SCSI Additional Sense Code (ASC): Write protected. +#define ISCSI_SCSI_ASC_WRITE_PROTECTED 0x27 - /// Name. - uint8_t *name; +/// iSCSI SCSI Additional Sense Code (ASC): Data has changed. +#define ISCSI_SCSI_ASC_CAPACITY_DATA_HAS_CHANGED 0x2A - /// Identifier. - uint64_t id; +/// iSCSI SCSI Additional Sense Code (ASC): Format command failed. +#define ISCSI_SCSI_ASC_FORMAT_COMMAND_FAIL 0x31 - /// Flags. - int flags; +/// iSCSI SCSI Additional Sense Code (ASC): Saving parameters not supported. +#define ISCSI_SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED 0x39 - /// Index. - uint16_t index; +/// iSCSI SCSI Additional Sense Code (ASC): Internal target failure. +#define ISCSI_SCSI_ASC_INTERNAL_TARGET_FAIL 0x44 - /// Transport ID length. - uint16_t transport_id_len; -} iscsi_port; +/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Cause not reportable. +#define ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE 0x00 -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 +/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Becoming ready. +#define ISCSI_SCSI_ASCQ_BECOMING_READY 0x01 -uint8_t *iscsi_port_get_name(const iscsi_port *port); // Retrieves the name of an iSCSI port +/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Format command failed. +#define ISCSI_SCSI_ASCQ_FORMAT_COMMAND_FAIL 0x01 -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 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 LUN flags: Removed. -#define ISCSI_SCSI_LUN_FLAGS_REMOVED (1 << 0) +/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): No access rights. +#define ISCSI_SCSI_ASCQ_NO_ACCESS_RIGHTS 0x02 -/// iSCSI SCSI LUN flags: Resizing. -#define ISCSI_SCSI_LUN_FLAGS_RESIZING (1 << 1) +/// 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 -typedef struct iscsi_device iscsi_device; +/// 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 -/** - * @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; +/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Capacity data has changed. +#define ISCSI_SCSI_ASCQ_CAPACITY_DATA_HAS_CHANGED 0x09 - /// 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; +/// iSCSI SCSI Persistent Reservation (PR) reservation type: Write exclusive. +#define ISCSI_SCSI_PR_RESERVATION_TYPE_WRITE_EXCLUSIVE 0x01 - /// Doubly linked list containing associated management tasks with this LUN. - iscsi_list tasks_mgmt; +/// iSCSI SCSI Persistent Reservation (PR) reservation type: Exclusive access. +#define ISCSI_SCSI_PR_RESERVATION_TYPE_EXCLUSIVE_ACCESS 0x03 - /// Mutex for accessing the associated management tasks through multiple threads. - pthread_mutex_t tasks_mgmt_mutex; +/// iSCSI SCSI Persistent Reservation (PR) reservation type: Write exclusive - registrants only. +#define ISCSI_SCSI_PR_RESERVATION_TYPE_WRITE_EXCLUSIVE_REGS_ONLY 0x05 - /// Doubly linked list containing associated management pending tasks with this LUN. - iscsi_list tasks_mgmt_pending; +/// iSCSI SCSI Persistent Reservation (PR) reservation type: Exclusive access - registrants only. +#define ISCSI_SCSI_PR_RESERVATION_TYPE_EXCLUSIVE_ACCESS_REGS_ONLY 0x06 - /// Mutex for accessing the associated management pending tasks through multiple threads. - pthread_mutex_t tasks_mgmt_pending_mutex; +/// iSCSI SCSI Persistent Reservation (PR) reservation type: Write exclusive - all registrants. +#define ISCSI_SCSI_PR_RESERVATION_TYPE_WRITE_EXCLUSIVE_ALL_REGS 0x07 - /// Doubly linked list containg Persistent Reservation (PR) registrant for I_T nexus. - iscsi_hashmap *pr_regs; +/// iSCSI SCSI Persistent Reservation (PR) reservation type: Exclusive access - all registrants. +#define ISCSI_SCSI_PR_RESERVATION_TYPE_EXCLUSIVE_ACCESS_ALL_REGS 0x08 - /// 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 SCSI Persistent Reservation (PR) reservation flags: SPC2 reserve. +#define ISCSI_SCSI_PR_RESERVATION_FLAGS_SPC2_RESERVE (1L << 0L) - /// iSCSI device which belongs to this LUN. - iscsi_device *device; - /// Assocated DNBD3 image for this LUN. - dnbd3_image_t *image; +/// iSCSI SCSI task run: Unknown. +#define ISCSI_SCSI_TASK_RUN_UNKNOWN -1 - /// LUN identifier (always MUST be between 0 and 7). - int id; +/// iSCSI SCSI task run: Completed. +#define ISCSI_SCSI_TASK_RUN_COMPLETE 0 - /// Flags. - int flags; +/// iSCSI SCSI task run: Pending. +#define ISCSI_SCSI_TASK_RUN_PENDING 1 - /// Persistent Reservation (PR) generation. - uint32_t pr_gen; -} iscsi_scsi_lun; +typedef struct iscsi_scsi_task iscsi_scsi_task; +typedef struct iscsi_scsi_lun iscsi_scsi_lun; -/// iSCSI device flags: Allocated. -#define ISCSI_DEVICE_FLAGS_ALLOCATED (1 << 0) +/// iSCSI SCSI task flags: Read. +#define ISCSI_SCSI_TASK_FLAGS_XFER_READ (1 << 0) -/// iSCSI device flags: Removed. -#define ISCSI_DEVICE_FLAGS_REMOVED (1 << 1) +/// iSCSI SCSI task flags: Write. +#define ISCSI_SCSI_TASK_FLAGS_XFER_WRITE (1 << 1) /** - * @brief iSCSI device. + * @brief iSCSI SCSI Task. * - * This structure managesw the SCSI - * devices and associates the - * disk image files with them. + * This structure is used for the iSCSI SCSI + * layer task management. */ -typedef struct iscsi_device { - /// Name of device. - uint8_t *name; +typedef struct iscsi_scsi_task { + /// Connection associated with this task. + iscsi_connection *connection; - /// LUNs associated with this device. - iscsi_hashmap *luns; + /// SCSI Command Descriptor Block (CDB). + iscsi_scsi_cdb *cdb; - /// 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; + /// SCSI sense data. + iscsi_scsi_sense_data_packet *sense_data; - /// Ports associated with this device. - iscsi_hashmap *ports; + /// Output buffer. + uint8_t *buf; - /// Device identifier. - int id; + /// Position of buffer in bytes. + uint32_t pos; - /// Flags. - int flags; + /// Length of buffer in bytes. + uint32_t len; - /// Number of active connections for this device. - uint32_t active_conns; + /// Unique identifier for this task. + uint64_t id; - /// Protocol identifier. - uint8_t protocol_id; -} iscsi_device; + /// Flags. + int flags; + /// Transfer position in bytes. + uint32_t xfer_pos; -/// iSCSI target node maximum length. -#define ISCSI_TARGET_NODE_MAX_NAME_LEN 223U + /// Transfer length in bytes. + uint32_t xfer_len; + /// Sense data length. + uint8_t sense_data_len; -/// iSCSI target node IQN identifier prefix string. -#define ISCSI_TARGET_NODE_NAME_IQN_PREFIX "iqn." + /// iSCSI SCSI status code. + uint8_t status; -/// iSCSI target node IEEE NAA identifier prefix string. -#define ISCSI_TARGET_NODE_NAME_NAA_PREFIX "naa." + /// Uplink read mutex for sync + pthread_mutex_t uplink_mutex; -/// iSCSI target node EUI identifier prefix string. -#define ISCSI_TARGET_NODE_NAME_EUI_PREFIX "eui." + /// Conditional to signal uplink read complete + pthread_cond_t uplink_cond; +} iscsi_scsi_task; -/// iSCSI target node WWN identifier prefix string. -#define ISCSI_TARGET_NODE_NAME_WWN_PREFIX "wwn-0x" +/// iSCSI SCSI emulation physical block size in bytes. +#define ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE DNBD3_BLOCK_SIZE +/// iSCSI SCSI emulation logical block size in bytes. +#define ISCSI_SCSI_EMU_BLOCK_SIZE (512) -/// iSCSI target node flags: CHAP authentication disabled. -#define ISCSI_TARGET_NODE_FLAGS_CHAP_DISABLE (1 << 0) +/// Block shift difference between dnbd3 (4k) and iSCSI (512b) +#define ISCSI_SCSI_EMU_BLOCK_DIFF_SHIFT (3) -/// iSCSI target node flags: CHAP authentication required. -#define ISCSI_TARGET_NODE_FLAGS_CHAP_REQUIRE (1 << 1) +/// iSCSI SCSI emulation I/O type: Removable. +#define ISCSI_SCSI_EMU_IO_TYPE_REMOVABLE (1 << 0) -/// iSCSI target node flags: CHAP authentication mutual. -#define ISCSI_TARGET_NODE_FLAGS_CHAP_MUTUAL (1 << 2) +/// iSCSI SCSI emulation I/O type: Unmap. +#define ISCSI_SCSI_EMU_IO_TYPE_UNMAP (1 << 1) -/// iSCSI target node flags: Destroyed. -#define ISCSI_TARGET_NODE_FLAGS_DESTROYED (1 << 3) +/// 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) -/** - * @brief iSCSI target node. - * - * This structure maintains the name, alias, - * associated device and connection data - * for a specific iSCSI target node. - */ -typedef struct iscsi_target_node { - /// Name of target node. - uint8_t *name; +/// iSCSI SCSI emulation I/O type: Device is (temporarily) write protected. +#define ISCSI_SCSI_EMU_IO_TYPE_WRITE_PROTECT (1 << 4) - /// Alias name of target node. - uint8_t *alias; - /// Associated iSCSI device. - iscsi_device *device; +/// 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) - /// Target node number. - uint num; - /// Queue depth. - uint queue_depth; - /// Flags. - int flags; +/// iSCSI target node WWN identifier prefix string. +#define ISCSI_TARGET_NODE_WWN_NAME_PREFIX "wwn-0x" - /// Header digest size (always MUST be 0 or 4 for now). - int header_digest; +/// iSCSI target node maximum length +#define ISCSI_TARGET_NODE_MAX_NAME_LEN 223U - /// Data digest size (always MUST be 0 or 4 for now). - int data_digest; - /// CHAP group ID. - int32_t chap_group; +/// iSCSI session type: Invalid. +#define ISCSI_SESSION_TYPE_INVALID 0 - /// Number of active connections for this target node. - uint32_t active_conns; -} iscsi_target_node; +/// iSCSI session type: Normal. +#define ISCSI_SESSION_TYPE_NORMAL 1 +/// iSCSI session type: Discovery. +#define ISCSI_SESSION_TYPE_DISCOVERY 2 /** - * @brief iSCSI target node search by name. - * - * This structure is used by iterating through - * all iSCSI target nodes finding by name. + * All mandatory fields in login process. + * Set to -1 or NULL if not sent by client. */ -typedef struct iscsi_target_node_find_name { - /// Found iSCSI target node is stored here, should be initialized to NULL. - iscsi_target_node *target; - - /// The name of the target node to search for. - uint8_t *name; -} iscsi_target_node_find_name; - - -/// iSCSI authentication CHAP phase: None. -#define ISCSI_AUTH_CHAP_PHASE_NONE 0 - -/// iSCSI authentication CHAP phase: Wait A. -#define ISCSI_AUTH_CHAP_PHASE_WAIT_A 1 +typedef struct iscsi_login_kvp +{ + /// Largest PDU client can receive. + int MaxRecvDataSegmentLength; -/// iSCSI authentication CHAP phase: Wait NR. -#define ISCSI_AUTH_CHAP_PHASE_WAIT_NR 2 + /// Maximum burst length client can receive. + int MaxBurstLength; -/// iSCSI authentication CHAP phase: End. -#define ISCSI_AUTH_CHAP_PHASE_END 3 + // Maximum unsolicited burst length client can receive. + int FirstBurstLength; + /// Maximum number of connections. + int MaxConnections; -/** - * @brief iSCSI CHAP authentication data structure. - * - * This structure maintains all data required for - * CHAP authentication method. - */ -typedef struct iscsi_auth_chap { - /// CHAP phase. - int phase; -} iscsi_auth_chap; + /// Error recovery level. + int ErrorRecoveryLevel; + /// The session type (Discovery, Normal). + const char *SessionType; -/// iSCSI session flags: Initial ready to transfer. -#define ISCSI_SESSION_FLAGS_INIT_R2T (1 << 0) + /// Desired auth method. + const char *AuthMethod; -/// iSCSI session flags: Immediate data. -#define ISCSI_SESSION_FLAGS_IMMEDIATE_DATA (1 << 1) + /// SendTargets command. + const char *SendTargets; -/// iSCSI session flags: Data PDU in order. -#define ISCSI_SESSION_FLAGS_DATA_PDU_IN_ORDER (1 << 2) + /// HeaderDigest requested by client. + const char *HeaderDigest; -/// iSCSI session flags: Data sequence in order. -#define ISCSI_SESSION_FLAGS_DATA_SEQ_IN_ORDER (1 << 3) + /// DataDigest requested by client. + const char *DataDigest; + const char *InitiatorName; -/// iSCSI session type: Invalid. -#define ISCSI_SESSION_TYPE_INVALID 0 + const char *TargetName; +} iscsi_negotiation_kvp; -/// iSCSI session type: Normal. -#define ISCSI_SESSION_TYPE_NORMAL 1 +/** + * Options/limits the client told us that + * are relevant for proper communication + */ +typedef struct iscsi_session_options +{ + /// Largest PDU client can receive. + int MaxRecvDataSegmentLength; -/// iSCSI session type: Discovery. -#define ISCSI_SESSION_TYPE_DISCOVERY 2 + /// Maximum burst length client can receive. + int MaxBurstLength; + // Maximum unsolicited burst length client can receive. + int FirstBurstLength; +} iscsi_session_options; /** * @brief iSCSI session. @@ -11245,68 +6043,29 @@ typedef struct iscsi_auth_chap { * login phase. */ typedef struct iscsi_session { - /// List of iSCSI connections associated with this session. - iscsi_list conn_list; - - /// Initiator port. - iscsi_port *init_port; - - /// 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. - uint64_t tag; - - /// Initiator Session ID (ISID). - uint64_t isid; - - /// Target Session Identifying Handle (TSIH). - uint64_t tsih; - - /// Flags (extracted from key and value pairs). - int flags; - - /// Queue depth. - uint queue_depth; - - /// iSCSI session type. - int type; - - /// Number of active connections linked to this session. - uint32_t conns; - - /// Maximum number of connections. - uint32_t max_conns; - - /// Ready to transfer maximum outstanding value. - uint32_t max_outstanding_r2t; - - /// Default time to wait. - uint32_t default_time_to_wait; + /// Initiator Session ID (ISID). + uint64_t isid; - /// Default time to retain. - uint32_t default_time_to_retain; + /// Target Session Identifying Handle (TSIH). + uint64_t tsih; - /// First burst length. - uint32_t first_burst_len; + /// Flags (extracted from key and value pairs). + int flags; - /// Maximum burst length. - uint32_t max_burst_len; + /// iSCSI session type. + int type; - /// Error recovery level. - uint32_t err_recovery_level; + /// ExpCmdSN. + uint32_t exp_cmd_sn; - /// ExpCmdSN. - uint32_t exp_cmd_sn; + /// MaxCmdSN. + uint32_t max_cmd_sn; - /// MaxCmdSN. - uint32_t max_cmd_sn; + /// Current text Initiator Task Tag (ITT). + uint32_t current_text_init_task_tag; - /// Current text Initiator Task Tag (ITT). - uint32_t current_text_init_task_tag; + /// Session options client sent in login request. + iscsi_session_options opts; } iscsi_session; @@ -11320,16 +6079,16 @@ typedef struct iscsi_pdu iscsi_pdu; #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 -1 +#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 -2 +#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 -3 +#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 -4 +#define ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER_XCHG_NOT_ONCE (-4) /// iSCSI connection flags: Stopped. @@ -11399,258 +6158,59 @@ typedef struct iscsi_pdu iscsi_pdu; * 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 login text key / value pairs associated to this connection. - iscsi_hashmap *key_value_pairs; - - /// 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; - - /// iSCSI initiator port. - iscsi_port *init_port; - - /// Initiator name. - uint8_t *init_name; - - //// Initiator IP address. - uint8_t *init_adr; - - /// iSCSI target node. - iscsi_target_node *target; - - /// iSCSI target port. - iscsi_port *target_port; - - /// iSCSI target short name. - uint8_t *target_name_short; - - /// iSCSI portal host name. - uint8_t *portal_host; - - /// iSCSI portal host port. - uint8_t *portal_port; - - /// Current PDU being processed. - iscsi_pdu *pdu_processing; - - /// Login response PDU. - iscsi_pdu *login_response_pdu; + /// iSCSI session associated with this connection. + iscsi_session *session; - /// Doubly linked list containing enqueued SCSI Data In tasks. - iscsi_list scsi_data_in_queued_tasks; + /// Associated dnbd3 client + dnbd3_client_t *client; - /// Doubly linked list containing writing PDU's associated with this connection. - iscsi_list pdus_write; + /// Current PDU being processed. + iscsi_pdu *pdu_processing; - /// Doubly linked list containing SNACK PDU's associated with this connection. - iscsi_list pdus_snack; + /// Login response PDU. + iscsi_pdu *login_response_pdu; - /// Doubly linked list containing active Ready To Transfer (R2T) tasks. - iscsi_list r2t_tasks_active; + /// Internal connection identifier + int id; - /// Doubly linked list containing queued Ready To Transfer (R2T) tasks. - iscsi_list r2t_tasks_queue; + /// iSCSI connection receiving state. + int pdu_recv_state; - /// iSCSI SendTargets total number of bytes completed. - uint target_send_total_size; + /// iSCSI connection flags. + int flags; - /// iSCSI SCSI Data In count. - uint scsi_data_in_cnt; + /// iSCSI connection state. + int state; - /// iSCSI SCSI Data Out count. - uint scsi_data_out_cnt; + /// iSCSI connection login phase. + int login_phase; - /// iSCSI tasks pending count. - uint task_cnt; + /// Initiator Session ID (ISID). + iscsi_isid isid; - /// Pending Ready To Transfer (R2T) tasks. - uint r2t_pending; + /// Target Session Identifying Handle (TSIH). + uint16_t tsih; - /// iSCSI connection contains a header digest (CRC32), always MUST be 0 or 4 for now. - int header_digest; + /// Connection ID (CID). + uint16_t cid; - /// iSCSI connection contains a data digest (CRC32), always MUST be 0 or 4 for now. - int data_digest; + /// Bit mask for connection state key negotiation. + uint16_t state_negotiated; - /// Internal connection identifier (key of iSCSI global vector hash map). - int id; + /// Bit mask for session state key negotiation. + uint32_t session_state_negotiated; - /// Connected TCP/IP socket. - int sock; + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; - /// iSCSI connection receiving state. - int pdu_recv_state; + /// Targer Transfer Tag (TTT). + uint32_t target_xfer_tag; - /// iSCSI connection flags. - int flags; - - /// iSCSI connection state. - int state; - - /// iSCSI connection login phase. - int login_phase; - - /// Maximum receive DataSegment length in bytes. - uint32_t max_recv_ds_len; - - /// Portal group tag. - uint64_t pg_tag; - - /// Initiator Session ID (ISID). - iscsi_isid isid; - - /// Target Session Identifying Handle (TSIH). - uint16_t tsih; - - /// Connection ID (CID). - uint16_t cid; - - /// Bit mask for connection state key negotiation. - uint16_t state_negotiated; - - /// Bit mask for session state key negotiation. - uint32_t session_state_negotiated; - - /// 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; - - /// CHAP group id. - int32_t chap_group; - - /// StatSN. - uint32_t stat_sn; - - /// 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; + /// StatSN. + uint32_t stat_sn; } 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; @@ -11666,77 +6226,38 @@ typedef struct iscsi_task iscsi_task; * 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; - - /// iSCSI Advanced Header Segment (AHS) packet data for fast access and is straight after BHS packet in memory. - iscsi_ahs_packet *ahs_pkt; + /// iSCSI Basic Header Segment (BHS) packet data. + iscsi_bhs_packet *bhs_pkt; - /// Header digest (CRC32C) packet data for fast access and is straight after BHS and AHS packet in memory. - iscsi_header_digest *header_digest; + /// iSCSI Advanced Header Segment (AHS) packet data for fast access and is straight after BHS packet in memory. + iscsi_ahs_packet *ahs_pkt; - /// iSCSI DataSegment (DS) packet data for fast access and is straight after BHS, AHS and header digest packet in memory. - iscsi_scsi_ds_cmd_data *ds_cmd_data; + /// iSCSI DataSegment (DS) packet data for fast access and is straight after BHS, AHS and header digest packet in memory. + 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; + /// iSCSI task handling this PDU. + iscsi_task *task; - /// iSCSI task handling this PDU. - iscsi_task *task; + /// Flags. + int flags; - /// Associated iSCSI connection. - iscsi_connection *conn; + /// Bytes of Basic Header Segment (BHS) already read. + uint bhs_pos; - /// Transfer complete callback function. - iscsi_connection_xfer_complete_callback xfer_complete_callback; + /// AHSLength. + uint ahs_len; - /// Transfer complete callback user data (arguments). - uint8_t *xfer_complete_user_data; + /// DataSegmentLength. + uint32_t ds_len; - /// Flags. - int flags; + /// DS Buffer write pos when filling buffer for sending. + uint32_t ds_write_pos; - /// Reference counter. - uint32_t ref; + /// Read position in AHS + DS buffer (recv). + uint32_t recv_pos; - /// 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; - - /// 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; - - /// Tasks referenced by this PDU counter. - uint task_ref_cnt; - - /// CmdSN. - uint32_t cmd_sn; + /// CmdSN. + uint32_t cmd_sn; } iscsi_pdu; @@ -11754,163 +6275,43 @@ typedef struct iscsi_pdu { * 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; - - /// Associated iSCSI PDU. - iscsi_pdu *pdu; + /// Underlying SCSI task structure. + iscsi_scsi_task scsi_task; - /// Buffer position in bytes. - uint32_t pos; + /// Associated iSCSI connection. + iscsi_connection *conn; - /// Buffer length in bytes. - uint32_t len; + /// Buffer position in bytes. + uint32_t pos; - /// Unique identifier for this task. - uint64_t id; + /// Buffer length in bytes. + uint32_t len; - /// Flags. - int flags; + /// Unique identifier for this task. + uint64_t id; - /// LUN identifier associated with this task (always MUST be between 0 and 7), used for hot removal tracking. - int lun_id; + /// Flags. + int flags; - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; + /// LUN identifier associated with this task (always MUST be between 0 and 7), used for hot removal tracking. + int lun_id; - /// Target Transfer Tag (TTT). - uint32_t target_xfer_tag; + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; - /// Desired number of bytes completed. - uint32_t des_data_xfer_pos; + /// Target Transfer Tag (TTT). + uint32_t target_xfer_tag; - /// Desired data transfer length. - uint32_t des_data_xfer_len; + /// Desired number of bytes completed. + uint32_t des_data_xfer_pos; - /// SCSI Data In Data Sequence Number (DataSN). - uint32_t data_sn; + /// Desired data transfer length. + uint32_t des_data_xfer_len; - /// 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; + /// SCSI Data In Data Sequence Number (DataSN). + uint32_t data_sn; } 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 - -uint8_t *iscsi_target_node_get_redirect(iscsi_connection *conn, iscsi_target_node *target); // Retrieves target node redirection address -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 - -iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock); // Creates data structure for an iSCSI connection from iSCSI portal and TCP/IP socket -int iscsi_connection_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // iSCSI connection destructor callback for hash map -void iscsi_connection_destroy(iscsi_connection *conn); // Deallocates all resources acquired by iscsi_connection_create - -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 - -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 -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 - -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 - -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 a279b02..48890df 100644 --- a/src/server/net.c +++ b/src/server/net.c @@ -33,6 +33,7 @@ #include #include +#include #ifdef __linux__ #include @@ -152,14 +153,43 @@ void net_init() mutex_init( &_clients_lock, LOCK_CLIENT_LIST ); } +void initClientStruct(dnbd3_client_t *client) +{ + mutex_init( &client->lock, LOCK_CLIENT ); + mutex_init( &client->sendMutex, LOCK_CLIENT_SEND ); + + mutex_lock( &client->lock ); + host_to_string( &client->host, client->hostName, HOSTNAMELEN ); + client->hostName[HOSTNAMELEN-1] = '\0'; + mutex_unlock( &client->lock ); + client->bytesSent = 0; + client->relayedCount = 0; +} + void* net_handleNewConnection(void *clientPtr) { dnbd3_client_t * const client = (dnbd3_client_t *)clientPtr; dnbd3_request_t request; + dnbd3_cache_map_t *cache = NULL; client->thread = pthread_self(); // Await data from client. Since this is a fresh connection, we expect data right away sock_setTimeout( client->sock, _clientTimeout ); + // NODELAY makes sense since we're sending a lot of data + int e2 = 1; + socklen_t optlen = sizeof(e2); + setsockopt( client->sock, IPPROTO_TCP, TCP_NODELAY, (void *)&e2, optlen ); + // Also increase send buffer + if ( getsockopt( client->sock, SOL_SOCKET, SO_SNDBUF, (void *)&e2, &optlen ) == 0 ) { +#ifdef __linux__ + // Linux doubles the value to account for overhead, get "real" value + e2 /= 2; +#endif + if ( e2 < SERVER_TCP_BUFFER_MIN_SIZE_PAYLOAD ) { + e2 = SERVER_TCP_BUFFER_MIN_SIZE_PAYLOAD; + setsockopt( client->sock, SOL_SOCKET, SO_SNDBUF, &e2, sizeof(e2) ); + } + } do { #ifdef DNBD3_SERVER_AFL const int ret = (int)recv( 0, &request, sizeof(request), MSG_WAITALL ); @@ -178,8 +208,16 @@ void* net_handleNewConnection(void *clientPtr) if ( ((char*)&request)[0] == 'G' || ((char*)&request)[0] == 'P' ) { // Close enough... rpc_sendStatsJson( client->sock, &client->host, &request, ret ); + } else if ( true /* check opcode ... */ ) { + initClientStruct( client ); + if ( !addToList( client ) ) { + freeClientStruct( client ); + logadd( LOG_WARNING, "Could not add new iSCSI client to list when connecting" ); + } else { + iscsi_connection_handle( client, &request, ret ); + goto exit_client_cleanup; + } } else { - iscsi_connection_handle( client, &request, ret ); logadd( LOG_DEBUG1, "Magic in client handshake incorrect" ); } goto fail_preadd; @@ -192,26 +230,17 @@ void* net_handleNewConnection(void *clientPtr) } } while (0); // Fully init client struct - mutex_init( &client->lock, LOCK_CLIENT ); - mutex_init( &client->sendMutex, LOCK_CLIENT_SEND ); - - mutex_lock( &client->lock ); - host_to_string( &client->host, client->hostName, HOSTNAMELEN ); - client->hostName[HOSTNAMELEN-1] = '\0'; - mutex_unlock( &client->lock ); - client->bytesSent = 0; - client->relayedCount = 0; + initClientStruct( client ); if ( !addToList( client ) ) { freeClientStruct( client ); - logadd( LOG_WARNING, "Could not add new client to list when connecting" ); - return NULL; + logadd( LOG_WARNING, "Could not add new DNBD3 client to list when connecting" ); + goto fail_preadd; } dnbd3_reply_t reply; dnbd3_image_t *image = NULL; - dnbd3_cache_map_t *cache = NULL; int image_file = -1; int num; @@ -518,11 +547,11 @@ exit_client_cleanup: ; removeFromList( client ); totalBytesSent += client->bytesSent; // Access time, but only if client didn't just probe - if ( image != NULL && client->bytesSent > DNBD3_BLOCK_SIZE * 10 ) { - mutex_lock( &image->lock ); - timing_get( &image->atime ); - image->accessed = true; - mutex_unlock( &image->lock ); + if ( client->image != NULL && client->bytesSent > DNBD3_BLOCK_SIZE * 10 ) { + mutex_lock( &client->image->lock ); + timing_get( &client->image->atime ); + client->image->accessed = true; + mutex_unlock( &client->image->lock ); } if ( cache != NULL ) { ref_put( &cache->reference ); @@ -688,7 +717,7 @@ static dnbd3_client_t* freeClientStruct(dnbd3_client_t *client) dnbd3_uplink_t *uplink = ref_get_uplink( &client->image->uplinkref ); if ( uplink != NULL ) { if ( client->relayedCount != 0 ) { - uplink_removeEntry( uplink, client, &uplinkCallback ); + uplink_removeEntry( uplink, client ); } ref_put( &uplink->reference ); } diff --git a/src/server/sendfile.c b/src/server/sendfile.c new file mode 100644 index 0000000..9e27238 --- /dev/null +++ b/src/server/sendfile.c @@ -0,0 +1,60 @@ +#include "sendfile.h" + +#if defined(__linux__) +#include +#elif defined(__FreeBSD__) +#include +#include +#include +#else +#error "What platform is this?" +#endif + +#include + +bool sendfile_all(const int fd, const int sock, off_t foffset, const size_t bytes) +{ + if ( bytes == 0 ) + return true; +#ifdef DNBD3_SERVER_AFL + errno = 0; + return true; +#elif defined(__linux__) + size_t done = 0; + int againCount = 0; + + while ( done < bytes ) { + const ssize_t sent = sendfile( sock, fd, &foffset, bytes - done ); + if ( sent == 0 ) // Probably EOF, like with send(), but manpage is not clear :-/ + return false; + if ( sent < 0 ) { + if ( errno == EAGAIN || errno == EINTR ) { + // Retry once, but give up otherwise - EAGAIN might just be the send timeout + if ( ++againCount > 1 ) + return false; + continue; + } + return false; + } + done += sent; + } +#elif defined(__FreeBSD__) + off_t sent; + size_t done = 0; + int againCount = 0; + + while ( done < bytes ) { + const int ret = sendfile( fd, sock, foffset + done, bytes - done, NULL, &sent, 0 ); + if ( ret == 0 || errno == EAGAIN || errno == EINTR ) { + // Retry once, but give up otherwise - EAGAIN might just be the send timeout + if ( sent == 0 && ++againCount > 1 ) + return false; + done += sent; + continue; + } + // Something else went wrong + return false; + } +#endif + return true; +} \ No newline at end of file diff --git a/src/server/sendfile.h b/src/server/sendfile.h new file mode 100644 index 0000000..e4cc5b7 --- /dev/null +++ b/src/server/sendfile.h @@ -0,0 +1,18 @@ +#ifndef SENDFILE_H_ +#define SENDFILE_H_ + +#include +#include +#include + +/** + * Platform-agnostic wrapper around sendfile, with retry logic. + * @param fd file to read from + * @param sock socket to write to + * @param foffset offset in file to start reading from + * @param bytes number of bytes to read/send + * @return true on success + */ +bool sendfile_all(int fd, int sock, off_t foffset, size_t bytes); + +#endif \ No newline at end of file diff --git a/src/server/server.c b/src/server/server.c index 0bbb417..b91e4ce 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -27,7 +27,6 @@ #include "net.h" #include "altservers.h" #include "integrity.h" -#include "iscsi.h" #include "threadpool.h" #include "rpc.h" #include "fuse.h" @@ -177,10 +176,6 @@ _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 ) { @@ -371,11 +366,6 @@ int main(int argc, char *argv[]) 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 ) ) { diff --git a/src/server/uplink.c b/src/server/uplink.c index 8a83124..e05d27c 100644 --- a/src/server/uplink.c +++ b/src/server/uplink.c @@ -228,12 +228,12 @@ static void freeUplinkStruct(ref *ref) * Remove given client from uplink request queue * Locks on: uplink.queueLock */ -void uplink_removeEntry(dnbd3_uplink_t *uplink, void *data, uplink_callback callback) +void uplink_removeEntry(dnbd3_uplink_t *uplink, void *data) { mutex_lock( &uplink->queueLock ); for ( dnbd3_queue_entry_t *it = uplink->queue; it != NULL; it = it->next ) { for ( dnbd3_queue_client_t **cit = &it->clients; *cit != NULL; ) { - if ( (**cit).data == data && (**cit).callback == callback ) { + if ( (**cit).data == data ) { (*(**cit).callback)( (**cit).data, (**cit).handle, 0, 0, NULL ); dnbd3_queue_client_t *entry = *cit; *cit = (**cit).next; diff --git a/src/server/uplink.h b/src/server/uplink.h index b6037d6..8d99695 100644 --- a/src/server/uplink.h +++ b/src/server/uplink.h @@ -10,7 +10,7 @@ uint64_t uplink_getTotalBytesReceived(); bool uplink_init(dnbd3_image_t *image, int sock, dnbd3_host_t *host, int version); -void uplink_removeEntry(dnbd3_uplink_t *uplink, void *data, uplink_callback callback); +void uplink_removeEntry(dnbd3_uplink_t *uplink, void *data); bool uplink_requestClient(dnbd3_client_t *client, uplink_callback callback, uint64_t handle, uint64_t start, uint32_t length, uint8_t hops); -- cgit v1.2.3-55-g7522 From 619502eea60436f50b63300ab3d4e73077f5194c Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 16 Oct 2025 14:50:30 +0200 Subject: [SERVER] iscsi: use sendfile() --- inc/dnbd3/shared/sockhelper.h | 5 ++ src/server/CMakeLists.txt | 2 + src/server/iscsi.c | 141 +++++++++++++++++++++++------------------- src/server/iscsi.h | 4 +- src/server/net.c | 109 +++++++------------------------- src/shared/sockhelper.c | 12 ++++ 6 files changed, 122 insertions(+), 151 deletions(-) (limited to 'src') diff --git a/inc/dnbd3/shared/sockhelper.h b/inc/dnbd3/shared/sockhelper.h index 5c7d903..aa482fa 100644 --- a/inc/dnbd3/shared/sockhelper.h +++ b/inc/dnbd3/shared/sockhelper.h @@ -117,4 +117,9 @@ ssize_t sock_sendAll(const int sock, const void *buffer, const size_t len, int m */ ssize_t sock_recv(const int sock, void *buffer, const size_t len); +/** + * Send a desired number of nullbytes to socket. + */ +bool sock_sendPadding(int fd, uint32_t bytes); + #endif /* SOCKHELPER_H_ */ diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index c66ace4..d53a778 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -54,6 +54,7 @@ set(DNBD3_SERVER_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/altservers.c ${CMAKE_CURRENT_SOURCE_DIR}/net.c ${CMAKE_CURRENT_SOURCE_DIR}/reference.c ${CMAKE_CURRENT_SOURCE_DIR}/rpc.c + ${CMAKE_CURRENT_SOURCE_DIR}/sendfile.c ${CMAKE_CURRENT_SOURCE_DIR}/server.c ${CMAKE_CURRENT_SOURCE_DIR}/threadpool.c ${CMAKE_CURRENT_SOURCE_DIR}/uplink.c @@ -72,6 +73,7 @@ set(DNBD3_SERVER_HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/altservers.h ${CMAKE_CURRENT_SOURCE_DIR}/reference.h ${CMAKE_CURRENT_SOURCE_DIR}/reftypes.h ${CMAKE_CURRENT_SOURCE_DIR}/rpc.h + ${CMAKE_CURRENT_SOURCE_DIR}/sendfile.h ${CMAKE_CURRENT_SOURCE_DIR}/server.h ${CMAKE_CURRENT_SOURCE_DIR}/threadpool.h ${CMAKE_CURRENT_SOURCE_DIR}/uplink.h diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 2b39955..30e4f7e 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -37,6 +37,7 @@ #include #include +#include "sendfile.h" #include "globals.h" #include "helper.h" #include "image.h" @@ -107,7 +108,7 @@ static void iscsi_connection_destroy(iscsi_connection *conn); // Deallocates all static 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 static void iscsi_connection_login_response_reject(iscsi_pdu *login_response_pdu, const iscsi_pdu *pdu); // Initializes a rejecting login response packet -static iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint32_t ds_len); +static iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint32_t ds_len, bool no_ds_alloc); static void iscsi_connection_pdu_destroy(iscsi_pdu *pdu); // Destroys an iSCSI PDU structure used by connections static iscsi_bhs_packet *iscsi_connection_pdu_resize(iscsi_pdu *pdu, const uint ahs_len, const uint32_t ds_len); // Appends packet data to an iSCSI PDU structure used by connections @@ -388,7 +389,7 @@ static void iscsi_task_destroy(iscsi_task *task) */ 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, bool immediate) { - iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, len ); + iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, len, true ); if ( response_pdu == NULL ) { logadd( LOG_ERROR, "iscsi_scsi_data_in_send: Out of memory while allocating iSCSI SCSI Data In response PDU" ); @@ -433,13 +434,42 @@ static uint32_t iscsi_scsi_data_in_send(iscsi_connection *conn, iscsi_task *task 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 ); - const uint32_t offset = (task->scsi_task.pos + pos); - iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->buf_offset, offset ); + iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->buf_offset, pos ); - memcpy( response_pdu->ds_cmd_data, (task->scsi_task.buf + pos), len ); + //memcpy( response_pdu->ds_cmd_data, (task->scsi_task.buf + pos), len ); iscsi_connection_pdu_write( conn, response_pdu ); + if ( task->scsi_task.buf != NULL ) { + if ( !sock_sendAll( conn->client->sock, (task->scsi_task.buf + pos), len, ISCSI_CONNECT_SOCKET_WRITE_RETRIES ) ) { + // Set error + return data_sn; + } + } else { + const off_t off = task->scsi_task.file_offset + pos; + size_t padding = 0; + size_t realBytes = len; + if ( off >= conn->client->image->realFilesize ) { + padding = len; + realBytes = 0; + } else if ( off + len > conn->client->image->realFilesize ) { + padding = ( off + len ) - conn->client->image->realFilesize; + realBytes -= padding; + } + bool ret = sendfile_all( conn->client->image->readFd, conn->client->sock, + off, realBytes ); + if ( !ret ) { + // Set error + return data_sn; + } + if ( padding > 0 ) { + if ( !sock_sendPadding( conn->client->sock, padding ) ) { + // Set error + return data_sn; + } + } + } + return (data_sn + 1UL); } @@ -545,8 +575,10 @@ static void iscsi_task_response(iscsi_connection *conn, iscsi_task *task, iscsi_ 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, ds_len ); + 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, ds_len, false ); if ( response_pdu == NULL ) { logadd( LOG_ERROR, "iscsi_task_response: Out of memory while allocating iSCSI SCSI response PDU" ); @@ -621,7 +653,6 @@ static void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task) scsi_task->cdb = 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; @@ -988,24 +1019,27 @@ static void iscsi_uplink_callback(void *data, uint64_t handle UNUSED, uint64_t s */ static int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, dnbd3_image_t *image, const uint64_t offset_blocks, const uint64_t num_blocks) { + int rc = 0; uint64_t offset_bytes; const uint64_t num_bytes = iscsi_scsi_emu_blocks_to_bytes( &offset_bytes, offset_blocks, num_blocks ); + scsi_task->file_offset = offset_bytes; dnbd3_cache_map_t *cache = ref_get_cachemap( image ); - bool readFromFile; - if ( cache == NULL ) { - readFromFile = true; - } else { + if ( cache != NULL ) { // This is a proxyed image, check if we need to relay the request... const uint64_t start = (offset_bytes & ~(uint64_t)(DNBD3_BLOCK_SIZE - 1)); const uint64_t end = ((offset_bytes + num_bytes + DNBD3_BLOCK_SIZE - 1) & ~(uint64_t) (DNBD3_BLOCK_SIZE - 1)); + bool readFromFile = image_isRangeCachedUnsafe( cache, start, end ); - readFromFile = image_isRangeCachedUnsafe( cache, start, end ); ref_put( &cache->reference ); if ( !readFromFile ) { // Not cached, request via uplink + scsi_task->buf = malloc( num_bytes ); + if ( scsi_task->buf == NULL ) { + return -ENOMEM; + } pthread_mutex_init( &scsi_task->uplink_mutex, NULL ); pthread_cond_init( &scsi_task->uplink_cond, NULL ); pthread_mutex_lock( &scsi_task->uplink_mutex ); @@ -1016,32 +1050,19 @@ static int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, dnbd3_imag logadd( LOG_DEBUG1, "Could not relay uncached request to upstream proxy for image %s:%d", image->name, image->rid ); - return -EIO; + rc = -EIO; + } else { + // Wait sync (Maybe use pthread_cond_timedwait to detect unavailable uplink instead of hanging...) + pthread_cond_wait( &scsi_task->uplink_cond, &scsi_task->uplink_mutex ); + pthread_mutex_unlock( &scsi_task->uplink_mutex ); + scsi_task->file_offset = (size_t)-1; } - - // Wait sync (Maybe use pthread_cond_timedwait to detect unavailable uplink instead of hanging...) - pthread_cond_wait( &scsi_task->uplink_cond, &scsi_task->uplink_mutex ); - pthread_mutex_unlock( &scsi_task->uplink_mutex ); pthread_cond_destroy( &scsi_task->uplink_cond ); pthread_mutex_destroy( &scsi_task->uplink_mutex ); } } - bool success; - - if ( readFromFile ) { - const int64_t len = pread( image->readFd, scsi_task->buf, (size_t) num_bytes, offset_bytes ); - success = ((uint64_t) len == num_bytes); - } else { - success = true; - } - - 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 ); - - return (success ? 0 : -1); + return rc; } /** @@ -1108,7 +1129,7 @@ static int iscsi_scsi_emu_block_read_write(dnbd3_image_t *image, iscsi_scsi_task 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 ) != 0ULL ) { + if ( iscsi_scsi_emu_bytes_to_blocks( &offset_blocks, &num_blocks, 0, scsi_task->len ) != 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; @@ -1116,18 +1137,7 @@ static int iscsi_scsi_emu_block_read_write(dnbd3_image_t *image, iscsi_scsi_task offset_blocks += lba; - int rc; - - scsi_task->buf = (uint8_t *) malloc( scsi_task->len ); - - if ( scsi_task->buf == NULL ) { - iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_HARDWARE_ERR, - ISCSI_SCSI_ASC_INTERNAL_TARGET_FAIL, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE ); - - return ISCSI_SCSI_TASK_RUN_COMPLETE; - } - - rc = iscsi_scsi_emu_io_blocks_read( scsi_task, image, offset_blocks, num_blocks ); + int rc = iscsi_scsi_emu_io_blocks_read( scsi_task, image, offset_blocks, num_blocks ); if ( rc < 0 ) { if ( rc == -ENOMEM ) { @@ -1237,7 +1247,7 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) != ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_ACTION_READ_CAPACITY_16 ) { return ISCSI_SCSI_TASK_RUN_UNKNOWN; } - 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) ); + iscsi_scsi_service_action_in_16_parameter_data_packet *buf = 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 ); @@ -2876,17 +2886,19 @@ static void iscsi_connection_login_response_reject(iscsi_pdu *login_response_pdu * linked later. * @param[in] ds_len Length of DataSegment packet data to be appended. * May not exceed 16MiB - 1 (16777215 bytes). + * @param no_ds_alloc Do not allocate buffer space for DS, only set + * value for header - for sending DS manually later * @return Pointer to allocated and zero filled PDU or NULL * in case of an error (usually memory exhaustion). */ -static iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint32_t ds_len) +static iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint32_t ds_len, bool no_ds_alloc) { if ( ds_len > ISCSI_MAX_DS_SIZE ) { logadd( LOG_ERROR, "iscsi_pdu_create: Invalid DS length" ); return NULL; } - const uint32_t pkt_ds_len = ISCSI_ALIGN( ds_len, ISCSI_ALIGN_SIZE ); + const uint32_t pkt_ds_len = no_ds_alloc ? 0 : ISCSI_ALIGN( ds_len, ISCSI_ALIGN_SIZE ); const uint32_t len = (uint32_t) ( sizeof(struct iscsi_bhs_packet) + pkt_ds_len ); iscsi_pdu *pdu = malloc( sizeof(struct iscsi_pdu) ); @@ -2916,7 +2928,7 @@ static iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint pdu->cmd_sn = 0UL; pdu->recv_pos = 0; - if ( pkt_ds_len != 0UL ) { + if ( pkt_ds_len > ds_len ) { memset( (((uint8_t *) pdu->ds_cmd_data) + ds_len), 0, (pkt_ds_len - ds_len) ); } @@ -2963,12 +2975,17 @@ static iscsi_bhs_packet *iscsi_connection_pdu_resize(iscsi_pdu *pdu, const uint logadd( LOG_ERROR, "iscsi_connection_pdu_resize: Invalid AHS or DataSegment packet size" ); return NULL; } + if ( pdu->ds_len != 0 && pdu->ds_cmd_data == NULL ) { + // If you really ever need this, handle it properly below (old_len, no copying, etc.) + logadd( LOG_ERROR, "iscsi_connection_pdu_resize: Cannot resize PDU with virtual DS" ); + return NULL; + } if ( (ahs_len != pdu->ahs_len) || (ds_len != pdu->ds_len) ) { 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 + ISCSI_ALIGN(pdu->ds_len, ISCSI_ALIGN_SIZE)); - const uint32_t new_len = (uint32_t) (sizeof(struct iscsi_bhs_packet) + (uint32_t) ahs_len + pkt_ds_len); + const size_t old_len = (sizeof(struct iscsi_bhs_packet) + (uint32_t) pdu->ahs_len + ISCSI_ALIGN(pdu->ds_len, ISCSI_ALIGN_SIZE)); + const size_t new_len = (sizeof(struct iscsi_bhs_packet) + (uint32_t) ahs_len + pkt_ds_len); if ( new_len > old_len ) { bhs_pkt = realloc( pdu->bhs_pkt, new_len ); @@ -3020,7 +3037,8 @@ static bool iscsi_connection_pdu_write(iscsi_connection *conn, iscsi_pdu *pdu) // During allocation we already round up to ISCSI_ALIGN_SIZE, but store the requested size in the ds_len // member, so it's safe to round up here before sending, the accessed memory will be valid and zeroed - const size_t len = (sizeof(struct iscsi_bhs_packet) + pdu->ahs_len + ISCSI_ALIGN(pdu->ds_len, ISCSI_ALIGN_SIZE)); + const size_t len = (sizeof(struct iscsi_bhs_packet) + pdu->ahs_len + + (pdu->ds_cmd_data == NULL ? 0 : ISCSI_ALIGN(pdu->ds_len, ISCSI_ALIGN_SIZE))); const ssize_t rc = sock_sendAll( conn->client->sock, pdu->bhs_pkt, len, ISCSI_CONNECT_SOCKET_WRITE_RETRIES ); iscsi_connection_pdu_destroy( pdu ); @@ -3090,7 +3108,7 @@ static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu pdu->flags |= ISCSI_PDU_FLAGS_REJECTED; 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, ds_len ); + iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, ds_len, false ); if ( response_pdu == NULL ) { logadd( LOG_ERROR, "iscsi_connection_handle_reject: Out of memory while allocating iSCSI reject response PDU" ); @@ -3206,7 +3224,7 @@ 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, 8192 ); + iscsi_pdu *login_response_pdu = iscsi_connection_pdu_create( conn, 8192, false ); if ( login_response_pdu == NULL ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; @@ -3383,7 +3401,7 @@ static int iscsi_connection_pdu_header_handle_logout_req(iscsi_connection *conn, 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, 0UL ); + iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 0UL, false ); if ( response_pdu == NULL ) { logadd( LOG_ERROR, "iscsi_connection_pdu_header_handle_logout_req: Out of memory while allocating iSCSI logout response PDU" ); @@ -3459,7 +3477,7 @@ static int iscsi_connection_pdu_header_handle(iscsi_connection *conn, iscsi_pdu 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, 0UL ); + iscsi_pdu *login_response_pdu = iscsi_connection_pdu_create( conn, 0UL, false ); if ( login_response_pdu == NULL ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; @@ -3540,7 +3558,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, ds_len ); + iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, ds_len, false ); if ( response_pdu == NULL ) { logadd( LOG_ERROR, "iscsi_connection_pdu_data_handle_nop_out: Out of memory while allocating iSCSI NOP-In response PDU" ); @@ -3607,7 +3625,6 @@ static int iscsi_connection_pdu_data_handle_scsi_cmd(iscsi_connection *conn, isc if ( (task->scsi_task.flags & ISCSI_SCSI_TASK_FLAGS_XFER_READ) != 0 ) { task->scsi_task.buf = NULL; - task->scsi_task.pos = 0UL; task->scsi_task.len = task->scsi_task.xfer_len; } iscsi_scsi_lun_task_run( &task->scsi_task, pdu ); @@ -3909,7 +3926,7 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } - iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 8192 ); + iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 8192, false ); if ( response_pdu == NULL ) { logadd( LOG_ERROR, "iscsi_connection_pdu_data_handle_text_req: Out of memory while allocating iSCSI text response PDU" ); @@ -4087,7 +4104,7 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn) switch ( conn->pdu_recv_state ) { case ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_READY : { assert( conn->pdu_processing == NULL ); - conn->pdu_processing = iscsi_connection_pdu_create( conn, 0UL ); + conn->pdu_processing = iscsi_connection_pdu_create( conn, 0UL, false ); if ( conn->pdu_processing == NULL ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; @@ -4223,7 +4240,7 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ return; } - conn->pdu_processing = iscsi_connection_pdu_create( conn, 0UL ); + conn->pdu_processing = iscsi_connection_pdu_create( conn, 0UL, false ); if ( conn->pdu_processing == NULL ) { iscsi_connection_destroy( conn ); diff --git a/src/server/iscsi.h b/src/server/iscsi.h index 0c279fe..6dfb515 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -5897,8 +5897,8 @@ typedef struct iscsi_scsi_task { /// Output buffer. uint8_t *buf; - /// Position of buffer in bytes. - uint32_t pos; + /// Offset in bytes in image for DATA-in command. + size_t file_offset; /// Length of buffer in bytes. uint32_t len; diff --git a/src/server/net.c b/src/server/net.c index 48890df..e94e549 100644 --- a/src/server/net.c +++ b/src/server/net.c @@ -26,6 +26,7 @@ #include "rpc.h" #include "altservers.h" #include "reference.h" +#include "sendfile.h" #include #include @@ -35,14 +36,6 @@ #include #include -#ifdef __linux__ -#include -#endif -#ifdef __FreeBSD__ -#include -#include -#include -#endif #include #include #include @@ -52,8 +45,6 @@ static dnbd3_client_t *_clients[SERVER_MAX_CLIENTS]; static int _num_clients = 0; static pthread_mutex_t _clients_lock; -static char nullbytes[500]; - static atomic_uint_fast64_t totalBytesSent = 0; // Adding and removing clients -- list management @@ -133,21 +124,6 @@ static inline bool send_reply(int sock, dnbd3_reply_t *reply, const void *payloa return true; } -/** - * Send given amount of null bytes. The caller has to acquire the sendMutex first. - */ -static inline bool sendPadding( const int fd, uint32_t bytes ) -{ - ssize_t ret; - while ( bytes >= sizeof(nullbytes) ) { - ret = sock_sendAll( fd, nullbytes, sizeof(nullbytes), 2 ); - if ( ret <= 0 ) - return false; - bytes -= (uint32_t)ret; - } - return sock_sendAll( fd, nullbytes, bytes, 2 ) == (ssize_t)bytes; -} - void net_init() { mutex_init( &_clients_lock, LOCK_CLIENT_LIST ); @@ -408,74 +384,33 @@ void* net_handleNewConnection(void *clientPtr) if ( lock ) mutex_lock( &client->sendMutex ); // Send reply header if ( send( client->sock, &reply, sizeof(dnbd3_reply_t), (request.size == 0 ? 0 : MSG_MORE) ) != sizeof(dnbd3_reply_t) ) { + logadd( LOG_DEBUG1, "Sending CMD_GET_BLOCK reply header to %s failed (errno=%d)", client->hostName, errno ); if ( lock ) mutex_unlock( &client->sendMutex ); - logadd( LOG_DEBUG1, "Sending CMD_GET_BLOCK reply header to %s failed", client->hostName ); goto exit_client_cleanup; } - if ( request.size != 0 ) { - // Send payload if request length > 0 - size_t done = 0; - off_t foffset = (off_t)offset; - size_t realBytes; - if ( offset + request.size <= image->realFilesize ) { - realBytes = request.size; - } else { - realBytes = (size_t)(image->realFilesize - offset); + const size_t realBytes = offset + request.size <= image->realFilesize + ? request.size : (image->realFilesize - offset); + bool ret = sendfile_all( image_file, client->sock, offset, realBytes ); + if ( !ret ) { + const int err = errno; + + if ( lock ) mutex_unlock( &client->sendMutex ); + if ( err != EPIPE && err != ECONNRESET && err != ESHUTDOWN + && err != EAGAIN && err != EWOULDBLOCK ) { + logadd( LOG_DEBUG1, "sendfile to %s failed (%d bytes, errno=%d)", + client->hostName, (int)realBytes, err ); } - while ( done < realBytes ) { - // TODO: Should we consider EOPNOTSUPP on BSD for sendfile and fallback to read/write? - // Linux would set EINVAL or ENOSYS instead, which it unfortunately also does for a couple of other failures :/ - // read/write would kill performance anyways so a fallback would probably be of little use either way. -#ifdef DNBD3_SERVER_AFL - char buf[1000]; - size_t cnt = realBytes - done; - if ( cnt > 1000 ) { - cnt = 1000; - } - const ssize_t sent = pread( image_file, buf, cnt, foffset ); - if ( sent > 0 ) { - //write( client->sock, buf, sent ); // This is not verified in any way, so why even do it... - } else { - const int err = errno; -#elif defined(__linux__) - const ssize_t sent = sendfile( client->sock, image_file, &foffset, realBytes - done ); - if ( sent <= 0 ) { - const int err = errno; -#elif defined(__FreeBSD__) - off_t sent; - const int ret = sendfile( image_file, client->sock, foffset, realBytes - done, NULL, &sent, 0 ); - if ( ret == -1 || sent == 0 ) { - const int err = errno; - if ( ret == -1 ) { - if ( err == EAGAIN || err == EINTR ) { // EBUSY? manpage doesn't explicitly mention *sent here.. But then again we dont set the according flag anyways - done += sent; - continue; - } - sent = -1; - } -#endif - if ( lock ) mutex_unlock( &client->sendMutex ); - if ( sent == -1 ) { - if ( err != EPIPE && err != ECONNRESET && err != ESHUTDOWN - && err != EAGAIN && err != EWOULDBLOCK ) { - logadd( LOG_DEBUG1, "sendfile to %s failed (image to net. sent %d/%d, errno=%d)", - client->hostName, (int)done, (int)realBytes, err ); - } - if ( err == EBADF || err == EFAULT || err == EINVAL || err == EIO ) { - logadd( LOG_INFO, "Disabling %s:%d", image->name, image->rid ); - image->problem.read = true; - } - } - goto exit_client_cleanup; - } - done += sent; + if ( err == EBADF || err == EFAULT || err == EINVAL || err == EIO ) { + logadd( LOG_INFO, "Disabling %s:%d", image->name, image->rid ); + image->problem.read = true; } - if ( request.size > (uint32_t)realBytes ) { - if ( !sendPadding( client->sock, request.size - (uint32_t)realBytes ) ) { - if ( lock ) mutex_unlock( &client->sendMutex ); - goto exit_client_cleanup; - } + goto exit_client_cleanup; + } + if ( request.size > (uint32_t)realBytes ) { + if ( !sock_sendPadding( client->sock, request.size - (uint32_t)realBytes ) ) { + if ( lock ) mutex_unlock( &client->sendMutex ); + goto exit_client_cleanup; } } if ( lock ) mutex_unlock( &client->sendMutex ); diff --git a/src/shared/sockhelper.c b/src/shared/sockhelper.c index 5096320..6972957 100644 --- a/src/shared/sockhelper.c +++ b/src/shared/sockhelper.c @@ -438,3 +438,15 @@ ssize_t sock_recv(const int sock, void *buffer, const size_t len) return done; } +bool sock_sendPadding(const int fd, uint32_t bytes) +{ + static char nullbytes[512] = {0}; + + while ( bytes >= sizeof(nullbytes) ) { + ssize_t ret = sock_sendAll( fd, nullbytes, sizeof(nullbytes), 2 ); + if ( ret <= 0 ) + return false; + bytes -= (uint32_t)ret; + } + return sock_sendAll( fd, nullbytes, bytes, 2 ) == (ssize_t)bytes; +} \ No newline at end of file -- cgit v1.2.3-55-g7522 From 65f3100f7ee79bfc8bda9e7ca2ce6015e8dc02dc Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Mon, 20 Oct 2025 16:40:15 +0200 Subject: [SERVER] Check if iSCSI server is enabled, check opcode --- src/server/globals.c | 6 +++--- src/server/globals.h | 2 +- src/server/net.c | 20 ++++++++++++-------- 3 files changed, 16 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/server/globals.c b/src/server/globals.c index 34941bf..37ee3d4 100644 --- a/src/server/globals.c +++ b/src/server/globals.c @@ -32,7 +32,7 @@ atomic_bool _vmdkLegacyMode = false; atomic_bool _proxyPrivateOnly = false; atomic_bool _pretendClient = false; atomic_int _autoFreeDiskSpaceDelay = 3600 * 10; -atomic_bool _iSCSIServer = true; +atomic_bool _iScsiServer = true; // [limits] atomic_int _maxClients = SERVER_MAX_CLIENTS; atomic_int _maxImages = SERVER_MAX_IMAGES; @@ -94,7 +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 ); + SAVE_TO_VAR_BOOL( dnbd3, iScsiServer ); if ( strcmp( section, "dnbd3" ) == 0 && strcmp( key, "backgroundReplication" ) == 0 ) { if ( strcmp( value, "hashblock" ) == 0 ) { _backgroundReplication = BGR_HASHBLOCK; @@ -366,7 +366,7 @@ size_t globals_dumpConfig(char *buffer, size_t size) PBOOL(proxyPrivateOnly); PBOOL(pretendClient); PINT(autoFreeDiskSpaceDelay); - PBOOL(iSCSIServer); + PBOOL(iScsiServer); P_ARG("[limits]\n"); PINT(maxClients); PINT(maxImages); diff --git a/src/server/globals.h b/src/server/globals.h index b51a81a..900b86d 100644 --- a/src/server/globals.h +++ b/src/server/globals.h @@ -337,7 +337,7 @@ extern atomic_int _autoFreeDiskSpaceDelay; * Specifies if the iSCSI server should be initialized, enabled * and used upon start of DNBD3 server. */ -extern atomic_bool _iSCSIServer; +extern atomic_bool _iScsiServer; /** * When handling a client request, this sets the maximum amount diff --git a/src/server/net.c b/src/server/net.c index e94e549..4c04462 100644 --- a/src/server/net.c +++ b/src/server/net.c @@ -184,17 +184,21 @@ void* net_handleNewConnection(void *clientPtr) if ( ((char*)&request)[0] == 'G' || ((char*)&request)[0] == 'P' ) { // Close enough... rpc_sendStatsJson( client->sock, &client->host, &request, ret ); - } else if ( true /* check opcode ... */ ) { - initClientStruct( client ); - if ( !addToList( client ) ) { - freeClientStruct( client ); - logadd( LOG_WARNING, "Could not add new iSCSI client to list when connecting" ); + } else if ( ((char*)&request)[0] == 0x43 ) { // Login opcode 0x03 + immediate bit (0x40) set + if ( !_iScsiServer ) { + logadd( LOG_INFO, "Received iSCSI login request from %s, but iSCSI server is not enabled", client->hostName ); } else { - iscsi_connection_handle( client, &request, ret ); - goto exit_client_cleanup; + initClientStruct( client ); + if ( !addToList( client ) ) { + freeClientStruct( client ); + logadd( LOG_WARNING, "Could not add new iSCSI client to list when connecting" ); + } else { + iscsi_connection_handle( client, &request, ret ); + goto exit_client_cleanup; + } } } else { - logadd( LOG_DEBUG1, "Magic in client handshake incorrect" ); + logadd( LOG_DEBUG1, "Magic in client handshake unknown" ); } goto fail_preadd; } -- cgit v1.2.3-55-g7522 From cdf12d13d1636db3ef14ea9d8acae3f919d3290b Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 22 Oct 2025 13:03:41 +0200 Subject: [SERVER] iscsi: Fix crashes --- src/server/iscsi.c | 62 ++++++++++++++++++++++++++----------------------- src/server/iscsi.h | 14 +++++------ src/shared/sockhelper.c | 2 +- 3 files changed, 41 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 30e4f7e..30cf312 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -362,7 +362,10 @@ static void iscsi_task_destroy(iscsi_task *task) if ( task == NULL ) return; - free( task->scsi_task.buf ); + if ( task->scsi_task.must_free ) { + free( task->scsi_task.buf ); + } + free( task->scsi_task.sense_data ); free( task ); } @@ -509,6 +512,9 @@ static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, iscsi_task *task res_cnt = (pos - xfer_len); flags |= ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_OVERFLOW; } + logadd( LOG_DEBUG1, "iscsi_task_xfer_scsi_data_in: pos=%lu, xfer_len=%lu, seg_len=%lu", pos, xfer_len, seg_len ); + if ( xfer_len == 0UL ) + return 0; uint32_t data_sn = task->data_sn; uint32_t max_burst_offset = 0UL; @@ -653,6 +659,7 @@ static void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task) scsi_task->cdb = NULL; scsi_task->sense_data = NULL; scsi_task->buf = NULL; + scsi_task->must_free = true; scsi_task->len = 0UL; scsi_task->id = 0ULL; scsi_task->flags = 0; @@ -775,28 +782,9 @@ static void iscsi_scsi_task_lun_process_none(iscsi_scsi_task *scsi_task) 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 ); + 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; - } + scsi_task->xfer_pos = 0UL; } /** @@ -1120,7 +1108,7 @@ static int iscsi_scsi_emu_block_read_write(dnbd3_image_t *image, iscsi_scsi_task const uint32_t max_xfer_len = ISCSI_MAX_DS_SIZE / ISCSI_SCSI_EMU_BLOCK_SIZE; - if ( xfer_len > max_xfer_len ) { + if ( xfer_len > max_xfer_len || xfer_len * ISCSI_SCSI_EMU_BLOCK_SIZE != scsi_task->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; @@ -1482,7 +1470,7 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task 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), ' ' ); + iscsi_strcpy_pad( (char *) vpd_page_design_desc_t10_vendor_id_inquiry_data_pkt->unit_serial_num, image->name, 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)); @@ -1534,7 +1522,7 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task 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 = vpd_page_design_desc_inquiry_data_pkt->desc; + 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) ISCSI_DEFAULT_DEVICE_ID ); @@ -2206,8 +2194,9 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) rc = iscsi_scsi_emu_primary_inquiry( scsi_task->connection->client->image, scsi_task, cdb_inquiry, std_inquiry_data_pkt, len ); if ( (rc >= 0) && (len > 0U) ) { - if ( len > alloc_len ) + if ( len > alloc_len ) { len = alloc_len; + } scsi_task->buf = (uint8_t *) std_inquiry_data_pkt; @@ -2215,6 +2204,8 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) memset( (((uint8_t *) std_inquiry_data_pkt) + rc), 0, (len - rc) ); rc = len; + } else { + free( std_inquiry_data_pkt ); } if ( rc >= 0 ) { @@ -2522,6 +2513,7 @@ static iscsi_connection *iscsi_connection_create(dnbd3_client_t *client) static void iscsi_connection_destroy(iscsi_connection *conn) { if ( conn != NULL ) { + iscsi_connection_pdu_destroy( conn->login_response_pdu ); iscsi_session_destroy( conn->session ); iscsi_connection_pdu_destroy( conn->pdu_processing ); free( conn ); @@ -2655,6 +2647,9 @@ static int iscsi_connection_pdu_login_response(iscsi_connection *conn, iscsi_pdu 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_connection_pdu_write( conn, resp_pdu ); + if ( conn->login_response_pdu == resp_pdu ) { + conn->login_response_pdu = NULL; + } return ISCSI_CONNECT_PDU_READ_OK; } @@ -3224,7 +3219,9 @@ 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, 8192, false ); + iscsi_pdu *login_response_pdu = conn->login_response_pdu != NULL + ? conn->login_response_pdu + : iscsi_connection_pdu_create( conn, 8192, false ); if ( login_response_pdu == NULL ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; @@ -3312,7 +3309,9 @@ static int iscsi_connection_pdu_header_handle_scsi_cmd(iscsi_connection *conn, i 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.buf = (uint8_t *) pdu->ds_cmd_data; + //logadd( LOG_DEBUG1, "ds_Cmd_data gets assigned: %d", task->scsi_task.must_free ); + //task->scsi_task.must_free = false; 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; @@ -3331,6 +3330,7 @@ static int iscsi_connection_pdu_header_handle_scsi_cmd(iscsi_connection *conn, i task->scsi_task.flags |= ISCSI_SCSI_TASK_FLAGS_XFER_READ; if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_WRITE) != 0 ) { + iscsi_task_destroy( task ); return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_COMMAND_NOT_SUPPORTED ); } @@ -3882,8 +3882,9 @@ static int iscsi_connection_pdu_data_handle_login_req(iscsi_connection *conn, is if ( conn->state == ISCSI_CONNECT_STATE_INVALID ) { rc = iscsi_connection_handle_login_phase_none( conn, login_response_pdu, &pairs, cid ); + logadd( LOG_DEBUG1, "rc2: %d", rc ); - if ( (rc == ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE) || (rc == ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER) ) { + if ( rc != ISCSI_CONNECT_PDU_READ_OK ) { iscsi_connection_pdu_login_response( conn, login_response_pdu ); return ISCSI_CONNECT_PDU_READ_OK; @@ -4207,6 +4208,9 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn) break; } } + if ( conn->state == ISCSI_CONNECT_STATE_EXITING ) { + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + } } while ( prev_recv_state != conn->pdu_recv_state ); return 0; diff --git a/src/server/iscsi.h b/src/server/iscsi.h index 6dfb515..622d49b 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -1470,10 +1470,10 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_inquiry_data_packet { #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 +#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_FIRST_BIT 4 /// 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) +#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) + 8 - 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)) @@ -1494,10 +1494,10 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_inquiry_data_packet { #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 +#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_CODE_SET_FIRST_BIT 0 /// 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) +#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) + 4 - 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)) @@ -5897,6 +5897,9 @@ typedef struct iscsi_scsi_task { /// Output buffer. uint8_t *buf; + /// Whether output buffer os owned by this struct and must be freed on destroy + bool must_free; + /// Offset in bytes in image for DATA-in command. size_t file_offset; @@ -6141,9 +6144,6 @@ typedef struct iscsi_pdu iscsi_pdu; /// iSCSI connection state: Exiting. #define ISCSI_CONNECT_STATE_EXITING 2 -/// iSCSI connection state: Invalid. -#define ISCSI_CONNECT_STATE_EXITED 3 - /// Number of attempts for writing to iSCSI connection socket. #define ISCSI_CONNECT_SOCKET_WRITE_RETRIES 3 diff --git a/src/shared/sockhelper.c b/src/shared/sockhelper.c index 6972957..0aad1a3 100644 --- a/src/shared/sockhelper.c +++ b/src/shared/sockhelper.c @@ -435,7 +435,7 @@ ssize_t sock_recv(const int sock, void *buffer, const size_t len) done += ret; } if ( done == 0 ) return ret; - return done; + return (ssize_t)done; } bool sock_sendPadding(const int fd, uint32_t bytes) -- cgit v1.2.3-55-g7522 From ef6bbe51f0ee4f64fe4f011d2a58622ebe5f457e Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 23 Oct 2025 14:46:37 +0200 Subject: [SERVER] Refactor classic dnbd3 code a bit, locking etc. --- src/server/net.c | 82 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/server/net.c b/src/server/net.c index 4c04462..7804100 100644 --- a/src/server/net.c +++ b/src/server/net.c @@ -104,23 +104,35 @@ static inline bool recv_request_payload(int sock, uint32_t size, serialized_buff } /** - * Send reply with optional payload. payload can be null. The caller has to - * acquire the sendMutex first. + * Send reply with optional payload. payload can be null. */ -static inline bool send_reply(int sock, dnbd3_reply_t *reply, const void *payload) +static bool send_reply(dnbd3_client_t *client, dnbd3_reply_t *reply, const void *payload, const bool lock) { - const uint32_t size = reply->size; + const uint32_t size = reply->size; // Copy because of fixup_reply() + fixup_reply( *reply ); - if ( sock_sendAll( sock, reply, sizeof(dnbd3_reply_t), 1 ) != sizeof(dnbd3_reply_t) ) { + if ( lock ) { + mutex_lock( &client->sendMutex ); + } + if ( sock_sendAll( client->sock, reply, sizeof(dnbd3_reply_t), 1 ) != sizeof(dnbd3_reply_t) ) { + if ( lock ) { + mutex_unlock( &client->sendMutex ); + } logadd( LOG_DEBUG1, "Sending reply header to client failed" ); return false; } if ( size != 0 && payload != NULL ) { - if ( sock_sendAll( sock, payload, size, 1 ) != (ssize_t)size ) { + if ( sock_sendAll( client->sock, payload, size, 1 ) != (ssize_t)size ) { + if ( lock ) { + mutex_unlock( &client->sendMutex ); + } logadd( LOG_DEBUG1, "Sending payload of %"PRIu32" bytes to client failed", size ); return false; } } + if ( lock ) { + mutex_unlock( &client->sendMutex ); + } return true; } @@ -311,7 +323,7 @@ void* net_handleNewConnection(void *clientPtr) serializer_put_uint64( &payload, image->virtualFilesize ); reply.cmd = CMD_SELECT_IMAGE; reply.size = serializer_get_written_length( &payload ); - if ( !send_reply( client->sock, &reply, &payload ) ) { + if ( !send_reply( client, &reply, &payload, false ) ) { bOk = false; } } @@ -330,7 +342,8 @@ void* net_handleNewConnection(void *clientPtr) while ( recv_request_header( client->sock, &request ) ) { if ( _shutdown ) break; if ( likely ( request.cmd == CMD_GET_BLOCK ) ) { - + // since the relayed count can only increase in this very loop, it is safe to check this here once + const bool lock = client->relayedCount > 0; const uint64_t offset = request.offset_small; // Copy to full uint64 to prevent repeated masking reply.handle = request.handle; if ( unlikely( offset >= image->virtualFilesize ) ) { @@ -338,7 +351,7 @@ void* net_handleNewConnection(void *clientPtr) logadd( LOG_WARNING, "Client %s requested non-existent block", client->hostName ); reply.size = 0; reply.cmd = CMD_ERROR; - send_reply( client->sock, &reply, NULL ); + send_reply( client, &reply, NULL, lock ); continue; } if ( unlikely( offset + request.size > image->virtualFilesize ) ) { @@ -346,7 +359,17 @@ void* net_handleNewConnection(void *clientPtr) logadd( LOG_WARNING, "Client %s requested data block that extends beyond image size", client->hostName ); reply.size = 0; reply.cmd = CMD_ERROR; - send_reply( client->sock, &reply, NULL ); + send_reply( client, &reply, NULL, lock ); + continue; + } + if ( unlikely( offset >= image->realFilesize ) ) { + // Shortcut - only virtual bytes (padding) + reply.cmd = CMD_GET_BLOCK; + reply.size = request.size; + if ( lock ) mutex_lock( &client->sendMutex ); + send_reply( client, &reply, NULL, false ); + sock_sendPadding( client->sock, request.size ); + if ( lock ) mutex_unlock( &client->sendMutex ); continue; } @@ -384,7 +407,6 @@ void* net_handleNewConnection(void *clientPtr) reply.size = request.size; fixup_reply( reply ); - const bool lock = image->uplinkref != NULL; if ( lock ) mutex_lock( &client->sendMutex ); // Send reply header if ( send( client->sock, &reply, sizeof(dnbd3_reply_t), (request.size == 0 ? 0 : MSG_MORE) ) != sizeof(dnbd3_reply_t) ) { @@ -436,22 +458,18 @@ void* net_handleNewConnection(void *clientPtr) num = altservers_getListForClient( client, server_list, NUMBER_SERVERS ); reply.cmd = CMD_GET_SERVERS; reply.size = (uint32_t)( num * sizeof(dnbd3_server_entry_t) ); - mutex_lock( &client->sendMutex ); - send_reply( client->sock, &reply, server_list ); - mutex_unlock( &client->sendMutex ); - goto set_name; + if ( !send_reply( client, &reply, server_list, true ) ) { + logadd( LOG_DEBUG1, "Sending CMD_GET_SERVERS reply to %s failed.", client->hostName ); + goto exit_client_cleanup; + } break; case CMD_KEEPALIVE: reply.cmd = CMD_KEEPALIVE; reply.size = 0; - mutex_lock( &client->sendMutex ); - send_reply( client->sock, &reply, NULL ); - mutex_unlock( &client->sendMutex ); -set_name: ; - if ( !hasName ) { - hasName = true; - setThreadName( client->hostName ); + if ( !send_reply( client, &reply, NULL, true ) ) { + logadd( LOG_DEBUG1, "Sending CMD_KEEPALIVE reply to %s failed.", client->hostName ); + goto exit_client_cleanup; } break; @@ -464,14 +482,18 @@ set_name: ; mutex_lock( &client->sendMutex ); if ( image->crc32 == NULL ) { reply.size = 0; - send_reply( client->sock, &reply, NULL ); + bOk = send_reply( client, &reply, NULL, false ); } else { const uint32_t size = reply.size = (uint32_t)( (IMGSIZE_TO_HASHBLOCKS(image->realFilesize) + 1) * sizeof(uint32_t) ); - send_reply( client->sock, &reply, NULL ); - send( client->sock, &image->masterCrc32, sizeof(uint32_t), MSG_MORE ); - send( client->sock, image->crc32, size - sizeof(uint32_t), 0 ); + bOk = send_reply( client, &reply, NULL, false ); + bOk = bOk && send( client->sock, &image->masterCrc32, sizeof(uint32_t), MSG_MORE ) == sizeof(uint32_t); + bOk = bOk && send( client->sock, image->crc32, size - sizeof(uint32_t), 0 ) == size - sizeof(uint32_t); } mutex_unlock( &client->sendMutex ); + if ( !bOk ) { + logadd( LOG_DEBUG1, "Sending CMD_GET_CRC32 reply to %s failed.", client->hostName ); + goto exit_client_cleanup; + } break; default: @@ -479,6 +501,10 @@ set_name: ; break; } // end switch + if ( !hasName ) { + hasName = true; + setThreadName( client->hostName ); + } } // end loop } // end bOk exit_client_cleanup: ; @@ -721,11 +747,11 @@ static void uplinkCallback(void *data, uint64_t handle, uint64_t start UNUSED, u .size = length, }; mutex_lock( &client->sendMutex ); - send_reply( client->sock, &reply, buffer ); + send_reply( client, &reply, buffer, false ); if ( buffer == NULL ) { shutdown( client->sock, SHUT_RDWR ); } - client->relayedCount--; mutex_unlock( &client->sendMutex ); + client->relayedCount--; } -- cgit v1.2.3-55-g7522 From 9e2e94ecb8140b159e1ba4d148d2e6dc57b5fc92 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Fri, 24 Oct 2025 08:52:25 +0200 Subject: Fix AFL build --- inc/dnbd3/afl.h | 4 ++++ inc/dnbd3/types.h | 7 +------ src/server/CMakeLists.txt | 19 ------------------- src/server/altservers.c | 2 ++ src/server/iscsi.c | 3 ++- src/server/net.c | 18 ++++++------------ src/server/rpc.c | 2 ++ src/server/server.c | 6 ++---- src/shared/CMakeLists.txt | 5 +++++ src/shared/sockhelper.c | 2 ++ 10 files changed, 26 insertions(+), 42 deletions(-) create mode 100644 inc/dnbd3/afl.h (limited to 'src') diff --git a/inc/dnbd3/afl.h b/inc/dnbd3/afl.h new file mode 100644 index 0000000..071293f --- /dev/null +++ b/inc/dnbd3/afl.h @@ -0,0 +1,4 @@ +#ifdef DNBD3_SERVER_AFL +#define send(a,b,c,d) write((a) == 0 ? 1 : (a), b, c) +#define recv(a,b,c,d) read(a, b, c) +#endif diff --git a/inc/dnbd3/types.h b/inc/dnbd3/types.h index bd15f4e..27dcbf7 100644 --- a/inc/dnbd3/types.h +++ b/inc/dnbd3/types.h @@ -71,11 +71,6 @@ #include #endif -#ifdef DNBD3_SERVER_AFL -#define send(a,b,c,d) write(a,b,c) -#define recv(a,b,c,d) read(a,b,c) -#endif - // ioctl #define DNBD3_MAGIC 'd' @@ -161,7 +156,7 @@ typedef struct __attribute__((packed)) uint16_t cmd; // 2byte uint32_t size; // 4byte union { - struct { + struct __attribute__((packed)) { #ifdef DNBD3_LITTLE_ENDIAN uint64_t offset_small:56; // 7byte uint8_t hops; // 1byte diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index d53a778..34eb695 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -18,27 +18,8 @@ find_package(Libatomic REQUIRED) add_definitions(-D_GNU_SOURCE) if(DNBD3_SERVER_AFL) - # check if DNBD3_RELEASE_HARDEN is disabled - if(DNBD3_RELEASE_HARDEN) - message(FATAL_ERROR "DNBD3_SERVER_AFL can only be enabled if DNBD3_RELEASE_HARDEN is disabled") - endif(DNBD3_RELEASE_HARDEN) - - # build dnbd3-server with AFL support message(STATUS "Building dnbd3-server with AFL support") add_definitions(-DDNBD3_SERVER_AFL) - - # change compiler for dnbd3-server sources if AFL enabled - include(CheckAFLCCompiler) - check_afl_c_compiler(AFL_C_COMPILER AFL_C_COMPILER_NAME ${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ID}) - if(AFL_C_COMPILER) - message(STATUS "Check for working AFL C compiler: ${AFL_C_COMPILER} - done") - # change C compiler to a corresponding AFL C compiler - set(CMAKE_C_COMPILER "${AFL_C_COMPILER}") - else(AFL_C_COMPILER) - # no corresponding AFL C compiler found - message(STATUS "Check for working AFL C compiler: ${AFL_C_COMPILER_NAME} - failed") - message(FATAL_ERROR "No corresponding AFL C compiler ${AFL_C_COMPILER_NAME} was found for the C compiler ${CMAKE_C_COMPILER}!") - endif(AFL_C_COMPILER) endif(DNBD3_SERVER_AFL) set(DNBD3_SERVER_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/altservers.c diff --git a/src/server/altservers.c b/src/server/altservers.c index 4413ca6..269fe28 100644 --- a/src/server/altservers.c +++ b/src/server/altservers.c @@ -14,6 +14,8 @@ #include #include +#include + #define LOG(lvl, msg, ...) logadd(lvl, msg " (%s:%d)", __VA_ARGS__, PIMG(image)) #define LOG_GOTO(jumplabel, lvl, ...) do { LOG(lvl, __VA_ARGS__); goto jumplabel; } while (0); #define ERROR_GOTO(jumplabel, ...) LOG_GOTO(jumplabel, LOG_ERROR, __VA_ARGS__) diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 30cf312..60b738b 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -52,6 +52,8 @@ #define ISCSI_DEFAULT_DEVICE_ID 1 #define ISCSI_DEFAULT_QUEUE_DEPTH 16 +#include + /** * @file iscsi.c * @author Sebastian Vater @@ -512,7 +514,6 @@ static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, iscsi_task *task res_cnt = (pos - xfer_len); flags |= ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_OVERFLOW; } - logadd( LOG_DEBUG1, "iscsi_task_xfer_scsi_data_in: pos=%lu, xfer_len=%lu, seg_len=%lu", pos, xfer_len, seg_len ); if ( xfer_len == 0UL ) return 0; diff --git a/src/server/net.c b/src/server/net.c index 7804100..f2f63b8 100644 --- a/src/server/net.c +++ b/src/server/net.c @@ -41,6 +41,8 @@ #include #include +#include + static dnbd3_client_t *_clients[SERVER_MAX_CLIENTS]; static int _num_clients = 0; static pthread_mutex_t _clients_lock; @@ -56,9 +58,7 @@ static void uplinkCallback(void *data, uint64_t handle, uint64_t start, uint32_t static inline bool recv_request_header(int sock, dnbd3_request_t *request) { ssize_t ret, fails = 0; -#ifdef DNBD3_SERVER_AFL - sock = 0; -#endif + // Read request header from socket while ( ( ret = recv( sock, request, sizeof(*request), MSG_WAITALL ) ) != sizeof(*request) ) { if ( errno == EINTR && ++fails < 10 ) continue; @@ -83,9 +83,6 @@ static inline bool recv_request_header(int sock, dnbd3_request_t *request) static inline bool recv_request_payload(int sock, uint32_t size, serialized_buffer_t *payload) { -#ifdef DNBD3_SERVER_AFL - sock = 0; -#endif if ( size == 0 ) { logadd( LOG_ERROR, "Called recv_request_payload() to receive 0 bytes" ); return false; @@ -94,8 +91,9 @@ static inline bool recv_request_payload(int sock, uint32_t size, serialized_buff logadd( LOG_ERROR, "Called recv_request_payload() for more bytes than the passed buffer could hold!" ); return false; } - if ( sock_recv( sock, payload->buffer, size ) != (ssize_t)size ) { - logadd( LOG_DEBUG1, "Could not receive request payload of length %d\n", (int)size ); + const ssize_t ret = sock_recv( sock, payload->buffer, size ); + if ( ret != (ssize_t)size ) { + logadd( LOG_DEBUG1, "Could not receive request payload of length %d (got %d, errno %d)\n", (int)size, (int)ret, errno ); return false; } // Prepare payload buffer for reading @@ -179,11 +177,7 @@ void* net_handleNewConnection(void *clientPtr) } } do { -#ifdef DNBD3_SERVER_AFL - const int ret = (int)recv( 0, &request, sizeof(request), MSG_WAITALL ); -#else const int ret = (int)recv( client->sock, &request, sizeof(request), MSG_WAITALL ); -#endif // It's expected to be a real dnbd3 client // Check request for validity. This implicitly dictates that all HTTP requests are more than 24 bytes... if ( ret != (int)sizeof(request) ) { diff --git a/src/server/rpc.c b/src/server/rpc.c index 119bbd5..528ae43 100644 --- a/src/server/rpc.c +++ b/src/server/rpc.c @@ -19,6 +19,8 @@ #include #include +#include + #if JANSSON_VERSION_HEX < 0x020600 #define json_stringn_nocheck(a,b) json_string_nocheck(a) #endif diff --git a/src/server/server.c b/src/server/server.c index b91e4ce..15a043d 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -314,10 +314,8 @@ int main(int argc, char *argv[]) exit( 3 ); } { - struct sockaddr_storage client; - memset( &client, 0, sizeof client ); - client.ss_family = AF_INET; - dnbd3_client_t *dnbd3_client = dnbd3_prepareClient( &client, 1 ); + struct sockaddr_storage client = { .ss_family = AF_INET }; + dnbd3_client_t *dnbd3_client = dnbd3_prepareClient( &client, 0 ); if ( dnbd3_client == NULL ) { fprintf( stderr, "New client failed\n" ); exit( 1 ); diff --git a/src/shared/CMakeLists.txt b/src/shared/CMakeLists.txt index a1bd49a..ce129af 100644 --- a/src/shared/CMakeLists.txt +++ b/src/shared/CMakeLists.txt @@ -11,6 +11,11 @@ find_package(Libatomic REQUIRED) # add compile option to get POLLRDHUP support for signals add_definitions(-D_GNU_SOURCE) +if(DNBD3_SERVER_AFL) + message(STATUS "Building dnbd3-shared with AFL support") + add_definitions(-DDNBD3_SERVER_AFL) +endif(DNBD3_SERVER_AFL) + set(DNBD3_SHARED_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/crc32.c ${CMAKE_CURRENT_SOURCE_DIR}/fdsignal.c ${CMAKE_CURRENT_SOURCE_DIR}/log.c diff --git a/src/shared/sockhelper.c b/src/shared/sockhelper.c index 0aad1a3..08d73fc 100644 --- a/src/shared/sockhelper.c +++ b/src/shared/sockhelper.c @@ -13,6 +13,8 @@ #define MAXLISTEN 20 +#include + struct _poll_list { int count; struct pollfd entry[MAXLISTEN]; -- cgit v1.2.3-55-g7522 From 145176a8b01b8f0eae25b9665ed57dc98353f7af Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Fri, 24 Oct 2025 14:20:16 +0200 Subject: [SERVER] iscsi: Change LUN to 0 Makes using the kernel's iscsi module simpler --- src/server/iscsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 60b738b..692a998 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -47,7 +47,7 @@ #include -#define ISCSI_DEFAULT_LUN 1 +#define ISCSI_DEFAULT_LUN 0 #define ISCSI_DEFAULT_PROTOCOL_ID 1 #define ISCSI_DEFAULT_DEVICE_ID 1 #define ISCSI_DEFAULT_QUEUE_DEPTH 16 -- cgit v1.2.3-55-g7522 From e5b1ff54227f90770e3d7e95e91fdbb4204dd4dd Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Fri, 24 Oct 2025 14:21:03 +0200 Subject: [SERVER] iscsi: Cleanup commented-out code --- src/server/iscsi.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 692a998..26c19eb 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -441,8 +441,6 @@ static uint32_t iscsi_scsi_data_in_send(iscsi_connection *conn, iscsi_task *task iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->buf_offset, pos ); - //memcpy( response_pdu->ds_cmd_data, (task->scsi_task.buf + pos), len ); - iscsi_connection_pdu_write( conn, response_pdu ); if ( task->scsi_task.buf != NULL ) { @@ -3310,9 +3308,6 @@ static int iscsi_connection_pdu_header_handle_scsi_cmd(iscsi_connection *conn, i uint32_t exp_xfer_len = iscsi_get_be32(scsi_cmd_pkt->exp_xfer_len); - //task->scsi_task.buf = (uint8_t *) pdu->ds_cmd_data; - //logadd( LOG_DEBUG1, "ds_Cmd_data gets assigned: %d", task->scsi_task.must_free ); - //task->scsi_task.must_free = false; 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; -- cgit v1.2.3-55-g7522 From 1f04d1ed8865edd68d351f66452e50eb19345d7a Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Fri, 24 Oct 2025 14:21:30 +0200 Subject: [SERVER] iscsi: Restore proper padding of DataSegment This broke when sending ds payload was refactored to avoid copying the buffer into the PDU's buffer before sending. iscsi_connection_pdu_create took care of this before, but now that we send the source buffer directly, pad the packet manually after sending the buffer contents if required. --- src/server/iscsi.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 26c19eb..c1fc01f 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -448,6 +448,13 @@ static uint32_t iscsi_scsi_data_in_send(iscsi_connection *conn, iscsi_task *task // Set error return data_sn; } + if ( len % ISCSI_ALIGN_SIZE != 0 ) { + const size_t padding = ISCSI_ALIGN_SIZE - (len % ISCSI_ALIGN_SIZE); + if ( !sock_sendPadding( conn->client->sock, padding ) ) { + // Set error + return data_sn; + } + } } else { const off_t off = task->scsi_task.file_offset + pos; size_t padding = 0; -- cgit v1.2.3-55-g7522 From 4eb4d11658b7276184019856e13a6d6d4a2456d4 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Mon, 27 Oct 2025 11:25:43 +0100 Subject: [SERVER] iscsi: Honor global _shutdown --- src/server/iscsi.c | 4 +--- src/server/iscsi.h | 3 --- 2 files changed, 1 insertion(+), 6 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index c1fc01f..668b8a0 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -4211,7 +4211,7 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn) break; } } - if ( conn->state == ISCSI_CONNECT_STATE_EXITING ) { + if ( conn->state == ISCSI_CONNECT_STATE_EXITING || _shutdown ) { return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } } while ( prev_recv_state != conn->pdu_recv_state ); @@ -4264,8 +4264,6 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ conn->id = ++CONN_ID; while ( iscsi_connection_pdu_read( conn ) >= ISCSI_CONNECT_PDU_READ_OK ) { - if ( (conn->flags & ISCSI_CONNECT_FLAGS_STOPPED) != 0 ) - break; } iscsi_connection_destroy( conn ); diff --git a/src/server/iscsi.h b/src/server/iscsi.h index 622d49b..05f22e7 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -6094,9 +6094,6 @@ typedef struct iscsi_pdu iscsi_pdu; #define ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER_XCHG_NOT_ONCE (-4) -/// iSCSI connection flags: Stopped. -#define ISCSI_CONNECT_FLAGS_STOPPED (1 << 0) - /// iSCSI connection flags: Rejected. #define ISCSI_CONNECT_FLAGS_REJECTED (1 << 1) -- cgit v1.2.3-55-g7522 From 062df52ab9f3464ce79ca91d54fa3d2ad209a31b Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 29 Oct 2025 18:05:00 +0100 Subject: [SERVER] iscsi: Refactor receive function and PDU handling - Fold header/data handling into one function This uncovered a few redundant checks and makes it easier to reason about control flow - Make all iscsi_pdu stack-allocated This greatly reduces the number of malloc and free calls during normal operation, lowers the risk of memory management bugs, and potentially increases performance in high concurrency scenarios. --- inc/dnbd3/shared/sockhelper.h | 2 +- src/server/iscsi.c | 1600 +++++++++++++++-------------------------- src/server/iscsi.h | 129 ++-- src/shared/sockhelper.c | 2 +- 4 files changed, 647 insertions(+), 1086 deletions(-) (limited to 'src') diff --git a/inc/dnbd3/shared/sockhelper.h b/inc/dnbd3/shared/sockhelper.h index aa482fa..728ebfc 100644 --- a/inc/dnbd3/shared/sockhelper.h +++ b/inc/dnbd3/shared/sockhelper.h @@ -120,6 +120,6 @@ ssize_t sock_recv(const int sock, void *buffer, const size_t len); /** * Send a desired number of nullbytes to socket. */ -bool sock_sendPadding(int fd, uint32_t bytes); +bool sock_sendPadding(int fd, size_t bytes); #endif /* SOCKHELPER_H_ */ diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 668b8a0..9734a4b 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -69,6 +69,8 @@ //#define malloc(x) (rand() % 100 == 0 ? NULL : malloc(x)) +// Use for stack-allocated iscsi_pdu +#define CLEANUP_PDU __attribute__((cleanup(iscsi_connection_pdu_destroy))) static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task); @@ -77,7 +79,7 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task); static void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task); // Allocates and initializes a SCSI task -static void iscsi_scsi_task_xfer_complete(iscsi_scsi_task *scsi_task, iscsi_pdu *pdu); // Callback function when an iSCSI SCSI task completed the data transfer +static void iscsi_scsi_task_xfer_complete(iscsi_scsi_task *scsi_task, iscsi_pdu *request_pdu); // Callback function when an iSCSI SCSI task completed the data transfer static void iscsi_scsi_task_lun_process_none(iscsi_scsi_task *scsi_task); // Processes a iSCSI SCSI task with no LUN identifier @@ -85,33 +87,25 @@ static void iscsi_scsi_task_lun_process_none(iscsi_scsi_task *scsi_task); // Pro static 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 static int iscsi_scsi_lun_get_from_iscsi(const uint64_t lun); // Converts an iSCSI LUN from packet data to internal SCSI LUN identifier -static void iscsi_scsi_lun_task_run( iscsi_scsi_task *scsi_task, iscsi_pdu *pdu); // Runs an iSCSI SCSI task for a specified iSCSI SCSI LUN - static int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, dnbd3_image_t *image, const uint64_t offset_blocks, const uint64_t num_blocks); // Reads a number of blocks from a block offset of a DNBD3 image to a specified buffer - static 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 - static iscsi_task *iscsi_task_create(iscsi_connection *conn); // Allocates and initializes an iSCSI task structure static void iscsi_task_destroy(iscsi_task *task); // Deallocates resources acquired by iscsi_task_create -static void iscsi_task_response(iscsi_connection *conn, iscsi_task *task, iscsi_pdu *pdu); // Creates, initializes and sends an iSCSI task reponse PDU. - static uint64_t iscsi_target_node_wwn_get(const uint8_t *name); // Calculates the WWN using 64-bit IEEE Extended NAA for a name -static iscsi_session *iscsi_session_create(iscsi_connection *conn, const int type); // Creates and initializes an iSCSI session +static iscsi_session *iscsi_session_create(const int type); // Creates and initializes an iSCSI session static void iscsi_session_destroy(iscsi_session *session); // Deallocates all resources acquired by iscsi_session_create static iscsi_connection *iscsi_connection_create(dnbd3_client_t *client); // Creates data structure for an iSCSI connection from iSCSI portal and TCP/IP socket static void iscsi_connection_destroy(iscsi_connection *conn); // Deallocates all resources acquired by iscsi_connection_create -static 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 - static void iscsi_connection_login_response_reject(iscsi_pdu *login_response_pdu, const iscsi_pdu *pdu); // Initializes a rejecting login response packet -static iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint32_t ds_len, bool no_ds_alloc); -static void iscsi_connection_pdu_destroy(iscsi_pdu *pdu); // Destroys an iSCSI PDU structure used by connections +static bool iscsi_connection_pdu_init(iscsi_pdu *pdu, const uint32_t ds_len, bool no_ds_alloc); +static void iscsi_connection_pdu_destroy(iscsi_pdu *pdu); static iscsi_bhs_packet *iscsi_connection_pdu_resize(iscsi_pdu *pdu, const uint ahs_len, const uint32_t ds_len); // Appends packet data to an iSCSI PDU structure used by connections @@ -145,6 +139,23 @@ static void iscsi_strcpy_pad(char *dst, const char *src, const size_t size, cons } } +/** + * @brief Parses a string representation of an integer and assigns the result to + * the provided destination variable, ensuring it is within valid range. + * + * This function checks for duplicate entries, empty strings, non-numeric + * characters, and out-of-range values. Logs debug messages for invalid or + * duplicate inputs and ensures values are clamped between 0 and INT_MAX. + * + * @param[in] name The name of the key associated with the integer value. + * Used for logging purposes. + * @param[in, out] dest Pointer to the destination integer variable where the + * parsed value will be stored. Must not be NULL. If the pointed + * value is -1, the parsed value will be assigned; otherwise, + * the function considers it a duplicate and does not update it. + * @param[in] src Pointer to the string containing the numeric representation + * of the value to parse. Must not be NULL or empty. + */ static void iscsi_copy_kvp_int(const char *name, int *dest, const char *src) { long long res = 0; @@ -178,6 +189,18 @@ static void iscsi_copy_kvp_int(const char *name, int *dest, const char *src) *dest = (int)res; } +/** + * @brief Copies a key-value pair string to the destination if it hasn't been copied already. + * + * This function ensures that a key has a single corresponding value by + * checking if the destination pointer has already been assigned. If assigned, + * a debug log entry is created, and the new value is ignored. + * + * @param[in] name The name of the key being assigned. Used for logging. + * @param[in,out] dest Pointer to the destination where the string is to be copied. + * If the destination is already assigned, the function will log and return. + * @param[in] src Pointer to the source string to be assigned to the destination. + */ static void iscsi_copy_kvp_str(const char *name, const char **dest, const char *src) { if ( *dest != NULL ) { @@ -278,7 +301,7 @@ static int iscsi_parse_text_key_value_pair(iscsi_negotiation_kvp *key_value_pair * @param[in] len Length of the remaining packet data. * @retval -1 An error occured during parsing key. * @retval 0 Key and value pair was parsed successfully and was added to - * hash map. + * kvp struct. */ static int iscsi_parse_login_key_value_pairs(iscsi_negotiation_kvp *pairs, const uint8_t *packet_data, uint len) { @@ -389,22 +412,16 @@ static void iscsi_task_destroy(iscsi_task *task) * @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. + * @return true success, false 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, bool immediate) +static bool 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, bool immediate) { - iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, len, true ); - - 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; + iscsi_pdu CLEANUP_PDU response_pdu; + if ( !iscsi_connection_pdu_init( &response_pdu, len, true ) ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - iscsi_scsi_data_in_response_packet *scsi_data_in_pkt = (iscsi_scsi_data_in_response_packet *) response_pdu->bhs_pkt; + 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)); @@ -441,22 +458,18 @@ static uint32_t iscsi_scsi_data_in_send(iscsi_connection *conn, iscsi_task *task iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->buf_offset, pos ); - iscsi_connection_pdu_write( conn, response_pdu ); + iscsi_connection_pdu_write( conn, &response_pdu ); if ( task->scsi_task.buf != NULL ) { - if ( !sock_sendAll( conn->client->sock, (task->scsi_task.buf + pos), len, ISCSI_CONNECT_SOCKET_WRITE_RETRIES ) ) { - // Set error - return data_sn; - } - if ( len % ISCSI_ALIGN_SIZE != 0 ) { - const size_t padding = ISCSI_ALIGN_SIZE - (len % ISCSI_ALIGN_SIZE); - if ( !sock_sendPadding( conn->client->sock, padding ) ) { - // Set error - return data_sn; - } + if ( !sock_sendAll( conn->client->sock, (task->scsi_task.buf + pos), len, ISCSI_CONNECT_SOCKET_WRITE_RETRIES ) ) + return false; + const size_t padding = ISCSI_ALIGN( len, ISCSI_ALIGN_SIZE ) - len; + if ( padding != 0 ) { + if ( !sock_sendPadding( conn->client->sock, padding ) ) + return false; } } else { - const off_t off = task->scsi_task.file_offset + pos; + const uint64_t off = task->scsi_task.file_offset + pos; size_t padding = 0; size_t realBytes = len; if ( off >= conn->client->image->realFilesize ) { @@ -467,20 +480,16 @@ static uint32_t iscsi_scsi_data_in_send(iscsi_connection *conn, iscsi_task *task realBytes -= padding; } bool ret = sendfile_all( conn->client->image->readFd, conn->client->sock, - off, realBytes ); - if ( !ret ) { - // Set error - return data_sn; - } + (off_t)off, realBytes ); + if ( !ret ) + return false; if ( padding > 0 ) { - if ( !sock_sendPadding( conn->client->sock, padding ) ) { - // Set error - return data_sn; - } + if ( !sock_sendPadding( conn->client->sock, padding ) ) + return false; } } - return (data_sn + 1UL); + return true; } /** @@ -525,7 +534,7 @@ static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, iscsi_task *task uint32_t data_sn = task->data_sn; uint32_t max_burst_offset = 0UL; const uint32_t max_burst_len = conn->session->opts.MaxBurstLength; - const uint32_t data_in_seq_count = ((xfer_len - 1UL) / max_burst_len) + 1UL; + const uint32_t data_in_seq_count = ((xfer_len - 1) / max_burst_len) + 1; int8_t status = 0; for ( uint32_t i = 0UL; i < data_in_seq_count; i++ ) { @@ -551,7 +560,10 @@ static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, iscsi_task *task } } - data_sn = iscsi_scsi_data_in_send( conn, task, offset, len, res_cnt, data_sn, flags, immediate ); + if ( !iscsi_scsi_data_in_send( conn, task, offset, len, res_cnt, data_sn, flags, immediate ) ) + return -1; + + data_sn++; } max_burst_offset += max_burst_len; @@ -563,63 +575,82 @@ static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, iscsi_task *task } /** - * @brief Creates, initializes and sends an iSCSI task reponse PDU. + * @brief Initializes a SCSI task. + * + * @param[in] scsi_task Pointer to SCSI task. This + * may NOT be NULL, so be careful. + */ +static void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task) +{ + scsi_task->cdb = NULL; + scsi_task->sense_data = NULL; + scsi_task->buf = NULL; + scsi_task->must_free = true; + scsi_task->len = 0UL; + scsi_task->id = 0ULL; + scsi_task->flags = 0; + scsi_task->xfer_pos = 0UL; + scsi_task->xfer_len = 0UL; + scsi_task->sense_data_len = 0U; + scsi_task->status = ISCSI_SCSI_STATUS_GOOD; +} + +/** + * @brief Callback function when an iSCSI SCSI task completed the data transfer. * - * This function also receives any remaining - * incoming data in case the task is reading. + * This function post-processes a task upon + * finish of data transfer. * - * @param[in] conn Pointer to iSCSI connection to handle the - * task resnponse for and may NOT be NULL, + * @param[in] scsi_task Pointer to iSCSI SCSI task which finished + * the data transfer 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. + * @param request_pdu */ -static void iscsi_task_response(iscsi_connection *conn, iscsi_task *task, iscsi_pdu *pdu) +static void iscsi_scsi_task_xfer_complete(iscsi_scsi_task *scsi_task, iscsi_pdu *request_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; + iscsi_task *task = container_of( scsi_task, iscsi_task, scsi_task ); + iscsi_connection *conn = task->conn; + + task->des_data_xfer_pos += scsi_task->len; + + iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) request_pdu->bhs_pkt; + const uint32_t xfer_len = 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, (pdu->bhs_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) != 0 ); + const int rc = iscsi_task_xfer_scsi_data_in( conn, task, (scsi_cmd_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) != 0 ); - if ( (rc > 0) || (task->des_data_xfer_pos != task->scsi_task.xfer_len) ) + if ( (rc > 0) || (task->des_data_xfer_pos != 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)) + const uint32_t ds_len = (scsi_task->sense_data_len != 0U) + ? (scsi_task->sense_data_len + offsetof(struct iscsi_scsi_ds_cmd_data, sense_data)) : 0UL; - iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, ds_len, false ); - - if ( response_pdu == NULL ) { - logadd( LOG_ERROR, "iscsi_task_response: Out of memory while allocating iSCSI SCSI response PDU" ); + iscsi_pdu CLEANUP_PDU response_pdu; + if ( !iscsi_connection_pdu_init( &response_pdu, ds_len, false ) ) return; - } - iscsi_scsi_response_packet *scsi_response_pkt = (iscsi_scsi_response_packet *) response_pdu->bhs_pkt; + 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; + if ( 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_be16( (uint8_t *) &ds_cmd_data_pkt->len, scsi_task->sense_data_len ); + memcpy( ds_cmd_data_pkt->sense_data, scsi_task->sense_data, 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; - 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 = task->scsi_task.xfer_pos; + const uint32_t pos = scsi_task->xfer_pos; - if ( (xfer_len != 0UL) && (task->scsi_task.status == ISCSI_SCSI_STATUS_GOOD) ) { + if ( (xfer_len != 0UL) && (scsi_task->status == ISCSI_SCSI_STATUS_GOOD) ) { if ( pos < xfer_len ) { const uint32_t res_cnt = (xfer_len - pos); @@ -637,7 +668,7 @@ static void iscsi_task_response(iscsi_connection *conn, iscsi_task *task, iscsi_ scsi_response_pkt->res_cnt = 0UL; } - scsi_response_pkt->status = task->scsi_task.status; + scsi_response_pkt->status = 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; @@ -651,50 +682,7 @@ static void iscsi_task_response(iscsi_connection *conn, iscsi_task *task, iscsi_ scsi_response_pkt->exp_data_sn = 0UL; scsi_response_pkt->bidi_read_res_cnt = 0UL; - iscsi_connection_pdu_write( conn, response_pdu ); -} - -/** - * @brief Initializes a SCSI task. - * - * @param[in] scsi_task Pointer to SCSI task. This - * may NOT be NULL, so be careful. - */ -static void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task) -{ - scsi_task->cdb = NULL; - scsi_task->sense_data = NULL; - scsi_task->buf = NULL; - scsi_task->must_free = true; - scsi_task->len = 0UL; - scsi_task->id = 0ULL; - scsi_task->flags = 0; - scsi_task->xfer_pos = 0UL; - scsi_task->xfer_len = 0UL; - scsi_task->sense_data_len = 0U; - scsi_task->status = ISCSI_SCSI_STATUS_GOOD; -} - -/** - * @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. - * @param pdu - */ -static void iscsi_scsi_task_xfer_complete(iscsi_scsi_task *scsi_task, iscsi_pdu *pdu) -{ - iscsi_task *task = container_of( scsi_task, iscsi_task, scsi_task ); - iscsi_connection *conn = task->conn; - - task->des_data_xfer_pos += task->scsi_task.len; - - iscsi_task_response( conn, task, pdu ); - iscsi_task_destroy( task ); + iscsi_connection_pdu_write( conn, &response_pdu ); } /** @@ -783,13 +771,8 @@ static void iscsi_scsi_task_status_set(iscsi_scsi_task *scsi_task, const uint8_t */ static 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; - 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; } @@ -849,58 +832,6 @@ static int iscsi_scsi_lun_get_from_iscsi(const uint64_t lun) return lun_id; } -/** - * @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] scsi_task Pointer to iSCSI SCSI task to be run. - * NULL is NOT valid here, take caution. - * @param pdu - */ -static void iscsi_scsi_lun_task_run( iscsi_scsi_task *scsi_task, iscsi_pdu *pdu) -{ - int rc; - - scsi_task->status = ISCSI_SCSI_STATUS_GOOD; - - 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 ); - // TODO: Free task - rc = ISCSI_SCSI_TASK_RUN_COMPLETE; - } - } - - if ( rc == ISCSI_SCSI_TASK_RUN_COMPLETE ) { - iscsi_scsi_task_xfer_complete( scsi_task, pdu ); - } -} - -/** - * @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) -{ - return ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE; -} - /** * @brief Retrieves the number of total logical blocks for a DNBD3 image. * @@ -1038,7 +969,7 @@ static int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, dnbd3_imag pthread_cond_init( &scsi_task->uplink_cond, NULL ); pthread_mutex_lock( &scsi_task->uplink_mutex ); - if ( !uplink_request( image, scsi_task, iscsi_uplink_callback, 0, offset_bytes, num_bytes ) ) { + if ( !uplink_request( image, scsi_task, iscsi_uplink_callback, 0, offset_bytes, (uint32_t)num_bytes ) ) { pthread_mutex_unlock( &scsi_task->uplink_mutex ); logadd( LOG_DEBUG1, "Could not relay uncached request to upstream proxy for image %s:%d", @@ -2429,7 +2360,7 @@ static uint64_t iscsi_target_node_wwn_get(const uint8_t *name) * @return Pointer to initialized iSCSI session or NULL in case an error * occured (usually due to memory exhaustion). */ -static iscsi_session *iscsi_session_create(iscsi_connection *conn, const int type) +static iscsi_session *iscsi_session_create(const int type) { iscsi_session *session = malloc( sizeof(struct iscsi_session) ); @@ -2443,7 +2374,6 @@ static iscsi_session *iscsi_session_create(iscsi_connection *conn, const int ty session->type = type; session->exp_cmd_sn = 0UL; session->max_cmd_sn = 0UL; - session->current_text_init_task_tag = 0xFFFFFFFFUL; return session; } @@ -2484,13 +2414,10 @@ static iscsi_connection *iscsi_connection_create(dnbd3_client_t *client) } conn->session = NULL; - conn->pdu_processing = NULL; - conn->login_response_pdu = NULL; conn->id = 0; conn->client = client; - conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_READY; conn->flags = 0; - conn->state = ISCSI_CONNECT_STATE_INVALID; + conn->state = ISCSI_CONNECT_STATE_NEW; conn->login_phase = ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION; conn->tsih = 0U; conn->cid = 0U; @@ -2519,40 +2446,11 @@ static iscsi_connection *iscsi_connection_create(dnbd3_client_t *client) static void iscsi_connection_destroy(iscsi_connection *conn) { if ( conn != NULL ) { - iscsi_connection_pdu_destroy( conn->login_response_pdu ); iscsi_session_destroy( conn->session ); - iscsi_connection_pdu_destroy( conn->pdu_processing ); free( 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. - * - * Returns ISCSI_CONNECT_PDU_READ_ERR_FATAL if the operation - * indicates a fatal error with the TCP connection (including - * if the TCP connection was closed unexpectedly). - * - * Otherwise returns the number of bytes successfully read. - */ -static int32_t iscsi_connection_read(const iscsi_connection *conn, uint8_t *buf, const uint32_t len) -{ - if ( len == 0UL ) - return 0L; - - int32_t rc; - do { - rc = (int32_t) recv( conn->client->sock, buf, (size_t) len, MSG_WAITALL ); - } while ( rc == -1 && errno == EINTR ); - - if ( rc == 0 ) - return -1; // EOF - return rc; -} - /** * @brief Appends a key and value pair to DataSegment packet data. * @@ -2630,7 +2528,7 @@ static void iscsi_connection_update_key_value_pairs(iscsi_connection *conn, iscs * @return 0 if the login response has been sent * successfully, a negative error code otherwise. */ -static int iscsi_connection_pdu_login_response(iscsi_connection *conn, iscsi_pdu *resp_pdu) +static int iscsi_send_login_response_pdu(iscsi_connection *conn, iscsi_pdu *resp_pdu) { iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) iscsi_connection_pdu_resize( resp_pdu, resp_pdu->ahs_len, resp_pdu->ds_write_pos ); @@ -2649,15 +2547,11 @@ static int iscsi_connection_pdu_login_response(iscsi_connection *conn, iscsi_pdu iscsi_put_be32( (uint8_t *) &login_response_pkt->max_cmd_sn, resp_pdu->cmd_sn ); } - if ( login_response_pkt->status_class != ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SUCCESS ) + 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_connection_pdu_write( conn, resp_pdu ); - if ( conn->login_response_pdu == resp_pdu ) { - conn->login_response_pdu = NULL; } - return ISCSI_CONNECT_PDU_READ_OK; + return iscsi_connection_pdu_write( conn, resp_pdu ) ? 0 : -1; } /** @@ -2686,41 +2580,36 @@ static int iscsi_connection_pdu_login_response_init(iscsi_pdu *login_response_pd login_response_pkt->flags |= (login_req_pkt->flags & ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_MASK); login_response_pkt->isid = login_req_pkt->isid; - login_response_pkt->tsih = login_req_pkt->tsih; // Copying over doesn't change endianess.' + login_response_pkt->tsih = 0; 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); + login_response_pkt->stat_sn = 0UL; + login_response_pkt->reserved2 = 0U; + login_response_pkt->reserved3 = 0ULL; - 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) ) { + if ( login_req_pkt->tsih != 0 ) { + // Session resumption, not supported + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_SESSION_NO_EXIST; + } else 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; login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; - - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - } else if ( (ISCSI_VERSION_MIN < login_req_pkt->version_min) || (ISCSI_VERSION_MAX > login_req_pkt->version_max) ) { + } else if ( (ISCSI_VERSION_MAX < login_req_pkt->version_min) || (ISCSI_VERSION_MIN > login_req_pkt->version_max) ) { login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_WRONG_VERSION; - - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; } else if ( (ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_RESERVED) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0) ) { login_response_pkt->flags &= (int8_t) ~(ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_MASK | ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT | ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_MASK); login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; + } else { + 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_ERR_LOGIN_RESPONSE; + return ISCSI_CONNECT_PDU_READ_OK; } - 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; + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; } /** @@ -2876,82 +2765,78 @@ static void iscsi_connection_login_response_reject(iscsi_pdu *login_response_pdu } /** - * @brief Creates an iSCSI PDU structure used by connections. + * @brief Initializes an iSCSI Protocol Data Unit (PDU) object for use in iSCSI communication. * - * The PDU structure is used for allowing partial - * reading from the TCP/IP socket and correctly - * filling the data until everything has been read. + * Allocates and assigns the required memory for the Basic Header Segment (BHS) packet + * and optionally for the aligned data segment (DS). Resets and initializes various fields + * within the given PDU structure. Ensures proper memory alignment for data segment if + * applicable, and zeroes out unused buffer regions. * - * @param[in] conn Pointer to connection to link the PDU with. - * If this is NULL the connection has to be - * linked later. - * @param[in] ds_len Length of DataSegment packet data to be appended. - * May not exceed 16MiB - 1 (16777215 bytes). - * @param no_ds_alloc Do not allocate buffer space for DS, only set - * value for header - for sending DS manually later - * @return Pointer to allocated and zero filled PDU or NULL - * in case of an error (usually memory exhaustion). + * @param[in,out] pdu Pointer to the iSCSI PDU structure to initialize. Must not be NULL. + * @param[in] ds_len Length of the Data Segment (DS) in bytes. Must not exceed ISCSI_MAX_DS_SIZE. + * @param[in] no_ds_alloc If true, the Data Segment memory allocation is skipped. + * + * @retval true if initialization is successful. + * @retval false if memory allocation for the BHS packet fails or ds_len exceeds the maximum allowed size. */ -static iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint32_t ds_len, bool no_ds_alloc) +static bool iscsi_connection_pdu_init(iscsi_pdu *pdu, const uint32_t ds_len, bool no_ds_alloc) { + // Always set this pointer to NULL before any sanity checks, + // so the attribute-cleanup magic won't screw up if init fails + pdu->big_alloc = NULL; + if ( ds_len > ISCSI_MAX_DS_SIZE ) { - logadd( LOG_ERROR, "iscsi_pdu_create: Invalid DS length" ); - return NULL; + logadd( LOG_ERROR, "iscsi_pdu_init: Invalid DS length" ); + return false; } const uint32_t pkt_ds_len = no_ds_alloc ? 0 : ISCSI_ALIGN( ds_len, ISCSI_ALIGN_SIZE ); - const uint32_t len = (uint32_t) ( sizeof(struct iscsi_bhs_packet) + pkt_ds_len ); - - iscsi_pdu *pdu = malloc( sizeof(struct iscsi_pdu) ); - if ( pdu == NULL ) { - logadd( LOG_ERROR, "iscsi_pdu_create: Out of memory while allocating iSCSI PDU" ); - - return NULL; - } - - iscsi_bhs_packet *bhs_pkt = malloc( len ); - if ( bhs_pkt == NULL ) { - free( pdu ); - logadd( LOG_ERROR, "iscsi_pdu_create: Out of memory while allocating iSCSI BHS packet" ); + const uint32_t alloc_len = (uint32_t) ( sizeof(struct iscsi_bhs_packet) + pkt_ds_len ); - return NULL; + if ( alloc_len > ISCSI_INTERNAL_BUFFER_SIZE ) { + pdu->bhs_pkt = pdu->big_alloc = malloc( alloc_len ); + if ( pdu->bhs_pkt == NULL ) { + logadd( LOG_ERROR, "iscsi_pdu_init: Out of memory while allocating iSCSI BHS packet" ); + return false; + } + } else { + pdu->bhs_pkt = (iscsi_bhs_packet *)pdu->internal_buffer; } - pdu->bhs_pkt = bhs_pkt; pdu->ahs_pkt = NULL; - pdu->ds_cmd_data = (pkt_ds_len != 0UL) ? (iscsi_scsi_ds_cmd_data *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet)) : NULL; - pdu->task = NULL; + pdu->ds_cmd_data = (pkt_ds_len != 0UL) + ? (iscsi_scsi_ds_cmd_data *) (((uint8_t *) pdu->bhs_pkt) + sizeof(struct iscsi_bhs_packet)) + : NULL; pdu->flags = 0; pdu->bhs_pos = 0U; pdu->ahs_len = 0; pdu->ds_len = ds_len; pdu->ds_write_pos = 0; pdu->cmd_sn = 0UL; - pdu->recv_pos = 0; if ( pkt_ds_len > ds_len ) { memset( (((uint8_t *) pdu->ds_cmd_data) + ds_len), 0, (pkt_ds_len - ds_len) ); } - return pdu; + return true; } /** - * @brief Destroys an iSCSI PDU structure used by connections. + * @brief Frees resources associated with an iSCSI PDU (Protocol Data Unit). * - * All associated data which has been read so - * far will be freed as well. + * This function releases memory allocated for certain members of the iSCSI + * PDU structure. It ensures that the allocated resources are properly freed. + * If the provided PDU pointer is NULL, the function returns immediately without + * performing any operations. * - * @param[in] pdu Pointer to PDU structure to be deallocated, - * may be NULL in which case this function - * does nothing. + * @param[in] pdu Pointer to the iSCSI PDU structure to be destroyed. + * If NULL, the function has no effect. */ static void iscsi_connection_pdu_destroy(iscsi_pdu *pdu) { if ( pdu == NULL ) return; - free( pdu->bhs_pkt ); - free( pdu ); + free( pdu->big_alloc ); } /** @@ -2972,34 +2857,54 @@ static void iscsi_connection_pdu_destroy(iscsi_pdu *pdu) */ static iscsi_bhs_packet *iscsi_connection_pdu_resize(iscsi_pdu *pdu, const uint ahs_len, const uint32_t ds_len) { - if ( (ahs_len > ISCSI_MAX_AHS_SIZE) || (ds_len > ISCSI_MAX_DS_SIZE) || (ahs_len % 4 != 0) ) { - logadd( LOG_ERROR, "iscsi_connection_pdu_resize: Invalid AHS or DataSegment packet size" ); - return NULL; - } - if ( pdu->ds_len != 0 && pdu->ds_cmd_data == NULL ) { - // If you really ever need this, handle it properly below (old_len, no copying, etc.) - logadd( LOG_ERROR, "iscsi_connection_pdu_resize: Cannot resize PDU with virtual DS" ); - return NULL; - } - if ( (ahs_len != pdu->ahs_len) || (ds_len != pdu->ds_len) ) { + if ( (ahs_len > ISCSI_MAX_AHS_SIZE) || (ds_len > ISCSI_MAX_DS_SIZE) || (ahs_len % ISCSI_ALIGN_SIZE != 0) ) { + logadd( LOG_ERROR, "iscsi_connection_pdu_resize: Invalid AHS or DataSegment packet size" ); + return NULL; + } + if ( pdu->ds_len != 0 && pdu->ds_cmd_data == NULL ) { + // If you really ever need this, handle it properly below (old_len, no copying, etc.) + logadd( LOG_ERROR, "iscsi_connection_pdu_resize: Cannot resize PDU with virtual DS" ); + return NULL; + } + if ( pdu->ds_len != 0 && pdu->ahs_len != ahs_len && ds_len != 0 ) { + // Cannot resize the AHS of a PDU that already has a DS and should keep the DS - we'd need to move the data + // around. Implement this when needed (and make sure it works). + logadd( LOG_ERROR, "iscsi_connection_pdu_resize: Cannot resize PDU's AHS that also has a DS" ); + return NULL; + } + iscsi_bhs_packet *bhs_pkt; const uint32_t pkt_ds_len = ISCSI_ALIGN(ds_len, ISCSI_ALIGN_SIZE); - const size_t old_len = (sizeof(struct iscsi_bhs_packet) + (uint32_t) pdu->ahs_len + ISCSI_ALIGN(pdu->ds_len, ISCSI_ALIGN_SIZE)); - const size_t new_len = (sizeof(struct iscsi_bhs_packet) + (uint32_t) ahs_len + pkt_ds_len); - - if ( new_len > old_len ) { - bhs_pkt = realloc( pdu->bhs_pkt, new_len ); + const size_t old_len = (sizeof(struct iscsi_bhs_packet) + (uint32_t) pdu->ahs_len + ISCSI_ALIGN(pdu->ds_len, ISCSI_ALIGN_SIZE)); + const size_t new_len = (sizeof(struct iscsi_bhs_packet) + (uint32_t) ahs_len + pkt_ds_len); + const bool old_alloced = pdu->big_alloc != NULL; + const bool new_alloced = new_len > ISCSI_INTERNAL_BUFFER_SIZE; - if ( bhs_pkt == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_pdu_resize: Out of memory while reallocating iSCSI PDU packet data" ); - - return NULL; - } - - pdu->bhs_pkt = bhs_pkt; - } else { + if ( new_len == old_len ) { + // Nothing changed bhs_pkt = pdu->bhs_pkt; + } else { + if ( new_alloced ) { + // New block doesn't fit in internal buffer - (re)allocate big buffer + bhs_pkt = realloc( pdu->big_alloc, new_len ); + if ( bhs_pkt == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_pdu_resize: Out of memory while reallocating iSCSI PDU packet data" ); + return NULL; + } + if ( !old_alloced ) { + // Old was in internal buffer, copy contents + memcpy( bhs_pkt, pdu->internal_buffer, MIN(new_len, old_len) ); + } + // Update PDU's BHS pointer + pdu->big_alloc = bhs_pkt; + pdu->bhs_pkt = bhs_pkt; + } else { + // New block fits into internal buffer - ignore for now and keep in big buffer + // to avoid needless overhead - PDUs are short-lived anyways. + // Keep using old BHS pointer + bhs_pkt = pdu->bhs_pkt; + } } pdu->ahs_pkt = (ahs_len != 0U) ? (iscsi_ahs_packet *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet)) : NULL; @@ -3032,7 +2937,6 @@ static iscsi_bhs_packet *iscsi_connection_pdu_resize(iscsi_pdu *pdu, const uint static bool iscsi_connection_pdu_write(iscsi_connection *conn, iscsi_pdu *pdu) { if ( conn->state >= ISCSI_CONNECT_STATE_EXITING ) { - iscsi_connection_pdu_destroy( pdu ); return false; } @@ -3042,8 +2946,6 @@ static bool iscsi_connection_pdu_write(iscsi_connection *conn, iscsi_pdu *pdu) + (pdu->ds_cmd_data == NULL ? 0 : ISCSI_ALIGN(pdu->ds_len, ISCSI_ALIGN_SIZE))); const ssize_t rc = sock_sendAll( conn->client->sock, pdu->bhs_pkt, len, ISCSI_CONNECT_SOCKET_WRITE_RETRIES ); - iscsi_connection_pdu_destroy( pdu ); - if ( rc != (ssize_t)len ) { conn->state = ISCSI_CONNECT_STATE_EXITING; return false; @@ -3108,16 +3010,12 @@ static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu { pdu->flags |= ISCSI_PDU_FLAGS_REJECTED; - 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, ds_len, false ); - - if ( response_pdu == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_handle_reject: Out of memory while allocating iSCSI reject response PDU" ); - + const uint32_t ds_len = (uint32_t) sizeof(struct iscsi_bhs_packet) + (uint32_t) (pdu->bhs_pkt->total_ahs_len * ISCSI_ALIGN_SIZE); + iscsi_pdu CLEANUP_PDU response_pdu; + if ( !iscsi_connection_pdu_init( &response_pdu, ds_len, false ) ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } - iscsi_reject_packet *reject_pkt = (iscsi_reject_packet *) response_pdu->bhs_pkt; + iscsi_reject_packet *reject_pkt = (iscsi_reject_packet *) response_pdu.bhs_pkt; reject_pkt->opcode = ISCSI_OPCODE_SERVER_REJECT; reject_pkt->flags = -0x80; @@ -3139,58 +3037,52 @@ static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu reject_pkt->reserved4 = 0ULL; - memcpy( response_pdu->ds_cmd_data, pdu->bhs_pkt, ds_len ); + memcpy( response_pdu.ds_cmd_data, pdu->bhs_pkt, ds_len ); - iscsi_connection_pdu_write( conn, response_pdu ); + iscsi_connection_pdu_write( conn, &response_pdu ); return ISCSI_CONNECT_PDU_READ_OK; } /** - * @brief Updates Command Sequence Number (CmdSN) of an incoming iSCSI PDU request. + * @brief Updates the expected command sequence number (ExpCmdSN) and validates sequence number bounds. * - * This function updates the Command Sequence - * Number (CmdSN) for incoming data sent by - * the client. + * This function extracts the CmdSN and checks whether it fits within the session's + * expected command sequence range, considering session type and iSCSI operation types. + * Also updates session-related sequence numbers as needed based on the received command. * - * @param[in] conn Pointer to iSCSI connection to handle. May - * NOT be NULL, so take caution. - * @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. + * @param[in] conn Pointer to the iSCSI connection. Must not be NULL, and its session pointer should also be valid. + * @param[in] request_pdu Pointer to the iSCSI PDU (Protocol Data Unit) containing command information. Must not be NULL. + * + * @return Returns `ISCSI_CONNECT_PDU_READ_OK` (0) on success or + * `ISCSI_CONNECT_PDU_READ_ERR_FATAL` (-1) if sequence numbers or other data are invalid. */ -static int iscsi_connection_update_cmd_sn(iscsi_connection *conn, iscsi_pdu *pdu) +static int iscsi_connection_handle_cmd_sn(iscsi_connection *conn, iscsi_pdu *request_pdu) { iscsi_session *session = conn->session; if ( session == NULL ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) pdu->bhs_pkt; + iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) request_pdu->bhs_pkt; const int opcode = ISCSI_GET_OPCODE(scsi_cmd_pkt->opcode); - pdu->cmd_sn = iscsi_get_be32(scsi_cmd_pkt->cmd_sn); + request_pdu->cmd_sn = iscsi_get_be32(scsi_cmd_pkt->cmd_sn); 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 )) + if ( (iscsi_seq_num_cmp_lt( request_pdu->cmd_sn, session->exp_cmd_sn ) + || iscsi_seq_num_cmp_gt( request_pdu->cmd_sn, session->max_cmd_sn )) && ((session->type == ISCSI_SESSION_TYPE_NORMAL) && (opcode != ISCSI_OPCODE_CLIENT_SCSI_DATA_OUT)) ) { logadd( LOG_WARNING, "Seqnum messup. Is: %u, want >= %u, < %u", - pdu->cmd_sn, session->exp_cmd_sn, session->max_cmd_sn ); + request_pdu->cmd_sn, session->exp_cmd_sn, session->max_cmd_sn ); return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } - } else if ( (pdu->cmd_sn != session->exp_cmd_sn) && (opcode != ISCSI_OPCODE_CLIENT_NOP_OUT) ) { + } else if ( (request_pdu->cmd_sn != session->exp_cmd_sn) && (opcode != ISCSI_OPCODE_CLIENT_NOP_OUT) ) { logadd( LOG_WARNING, "Seqnum messup. Is: %u, want: %u", - pdu->cmd_sn, session->exp_cmd_sn ); + request_pdu->cmd_sn, session->exp_cmd_sn ); 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 ( ((scsi_cmd_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0) && (opcode != ISCSI_OPCODE_CLIENT_SCSI_DATA_OUT) ) session->exp_cmd_sn++; @@ -3198,378 +3090,121 @@ static int iscsi_connection_update_cmd_sn(iscsi_connection *conn, iscsi_pdu *pdu } /** - * @brief Handles an incoming iSCSI header login request PDU. + * @brief Handles an incoming iSCSI header logout request PDU. * - * This function handles login request header + * This function handles logout request header * 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] pdu Pointer to iSCSI client request PDU to handle. + * @param[in] request_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_login_req(iscsi_connection *conn, iscsi_pdu *pdu) +static int iscsi_connection_handle_logout_req(iscsi_connection *conn, iscsi_pdu *request_pdu) { - if ( ((conn->flags & ISCSI_CONNECT_FLAGS_FULL_FEATURE) != 0) && (conn->session != NULL) - && (conn->session->type == ISCSI_SESSION_TYPE_DISCOVERY) ) + iscsi_logout_req_packet *logout_req_pkt = (iscsi_logout_req_packet *) request_pdu->bhs_pkt; + + 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; - const iscsi_login_req_packet *login_req_pkt = (iscsi_login_req_packet *) pdu->bhs_pkt; + iscsi_pdu CLEANUP_PDU response_pdu; + if ( !iscsi_connection_pdu_init( &response_pdu, 0, false ) ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - pdu->cmd_sn = iscsi_get_be32(login_req_pkt->cmd_sn); + iscsi_logout_response_packet *logout_response_pkt = (iscsi_logout_response_packet *) response_pdu.bhs_pkt; - if ( pdu->ds_len > ISCSI_DEFAULT_RECV_DS_LEN ) - return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); + logout_response_pkt->opcode = ISCSI_OPCODE_SERVER_LOGOUT_RES; + logout_response_pkt->flags = -0x80; - iscsi_pdu *login_response_pdu = conn->login_response_pdu != NULL - ? conn->login_response_pdu - : iscsi_connection_pdu_create( conn, 8192, false ); + const uint16_t cid = iscsi_get_be16(logout_req_pkt->cid); - if ( login_response_pdu == NULL ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + if ( cid == conn->cid ) { + logout_response_pkt->response = ISCSI_LOGOUT_RESPONSE_CLOSED_SUCCESSFULLY; + } else { + logout_response_pkt->response = ISCSI_LOGOUT_RESPONSE_CID_NOT_FOUND; + } - const int rc = iscsi_connection_pdu_login_response_init( login_response_pdu, pdu ); + 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 ( rc < 0 ) { - iscsi_connection_pdu_login_response( conn, login_response_pdu ); + if ( conn->session != NULL ) { + conn->session->max_cmd_sn++; - return ISCSI_CONNECT_PDU_READ_OK; + 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, request_pdu->cmd_sn ); + iscsi_put_be32( (uint8_t *) &logout_response_pkt->max_cmd_sn, request_pdu->cmd_sn ); } - conn->login_response_pdu = login_response_pdu; + logout_response_pkt->reserved4 = 0UL; + logout_response_pkt->time_wait = 0U; + logout_response_pkt->time_retain = 0U; + logout_response_pkt->reserved5 = 0UL; - return ISCSI_CONNECT_PDU_READ_OK; + bool ret = iscsi_connection_pdu_write( conn, &response_pdu ); + + if ( cid == conn->cid ) { + conn->state = ISCSI_CONNECT_STATE_EXITING; + } + + return ret ? ISCSI_CONNECT_PDU_READ_OK : ISCSI_CONNECT_PDU_READ_ERR_FATAL; } /** - * @brief Handles an incoming iSCSI header NOP-Out request PDU. + * @brief Handles an incoming iSCSI payload data NOP-Out request PDU. * - * This function handles NOP-Out request header + * This function handles NOP-Out 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] pdu Pointer to iSCSI client request PDU to handle. + * @param[in] request_pdu Pointer to iSCSI client request PDU to handle. * May be NULL in which case an error is returned. + * @param response_pdu * @return 0 on success. A negative value indicates * an error. A positive value a warning. */ -static int iscsi_connection_pdu_header_handle_nop_out(iscsi_connection *conn, iscsi_pdu *pdu) +static int iscsi_connection_handle_nop_out(iscsi_connection *conn, iscsi_pdu *request_pdu) { if ( conn->session->type == ISCSI_SESSION_TYPE_DISCOVERY ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - if ( pdu->ds_len > ISCSI_DEFAULT_MAX_RECV_DS_LEN ) - return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); + if ( request_pdu->ds_len > ISCSI_DEFAULT_MAX_RECV_DS_LEN ) + return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); - iscsi_nop_out_packet *nop_out_pkt = (iscsi_nop_out_packet *) pdu->bhs_pkt; - 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); + iscsi_nop_out_packet *nop_out_pkt = (iscsi_nop_out_packet *) request_pdu->bhs_pkt; + const uint32_t target_xfer_tag = iscsi_get_be32(nop_out_pkt->target_xfer_tag); + uint32_t ds_len = request_pdu->ds_len; + const uint64_t lun = iscsi_get_be64(nop_out_pkt->lun); + + if ( nop_out_pkt->init_task_tag == 0xFFFFFFFFUL ) // Was response to a NOP by us - do not reply + return ISCSI_CONNECT_PDU_READ_OK; 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. + return iscsi_connection_handle_reject( conn, request_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 & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) + if ( (nop_out_pkt->init_task_tag == 0xFFFFFFFFUL) && (nop_out_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - return ISCSI_CONNECT_PDU_READ_OK; -} - -/** - * @brief Handles an incoming iSCSI header SCSI command request PDU. - * - * This function handles SCSI command request - * header 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] 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) -{ - 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 ); - - 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.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->init_task_tag = iscsi_get_be32(scsi_cmd_pkt->init_task_tag); - - const uint64_t lun = iscsi_get_be64(scsi_cmd_pkt->lun); - task->lun_id = iscsi_scsi_lun_get_from_iscsi( lun ); - - 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; - - if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_WRITE) != 0 ) { - iscsi_task_destroy( task ); - return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_COMMAND_NOT_SUPPORTED ); - } - - pdu->task = task; - - return ISCSI_CONNECT_PDU_READ_OK; -} - -/** - * @brief Handles an incoming iSCSI header text request PDU. - * - * This function handles text request header - * 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] 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) -{ - if ( pdu->ds_len > (uint) (sizeof(struct iscsi_bhs_packet) + ISCSI_DEFAULT_RECV_DS_LEN) ) - return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); - - 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; - return ISCSI_CONNECT_PDU_READ_OK; - } - return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); -} - -/** - * @brief Handles an incoming iSCSI header logout request PDU. - * - * This function handles logout request header - * 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] 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) -{ - iscsi_logout_req_packet *logout_req_pkt = (iscsi_logout_req_packet *) pdu->bhs_pkt; - - 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, 0UL, false ); - - 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 ) { - 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 ); - - return ISCSI_CONNECT_PDU_READ_OK; -} - -/** - * @brief Handles an incoming iSCSI header PDU. - * - * This function handles all header data sent - * by the client, including authentication.\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] 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(iscsi_connection *conn, iscsi_pdu *pdu) -{ - if ( pdu == NULL ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - - const int opcode = ISCSI_GET_OPCODE(pdu->bhs_pkt->opcode); - - 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, 0UL, false ); - - if ( login_response_pdu == NULL ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - - iscsi_connection_login_response_reject( login_response_pdu, pdu ); - iscsi_connection_pdu_write( conn, login_response_pdu ); - - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - } - if ( conn->state == ISCSI_CONNECT_STATE_INVALID ) { - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } - - int rc = iscsi_connection_update_cmd_sn( conn, pdu ); - - if ( rc != 0 ) - return rc; - - switch ( opcode ) { - case ISCSI_OPCODE_CLIENT_NOP_OUT : { - rc = iscsi_connection_pdu_header_handle_nop_out( conn, pdu ); - - break; - } - case ISCSI_OPCODE_CLIENT_SCSI_CMD : { - rc = iscsi_connection_pdu_header_handle_scsi_cmd( conn, pdu ); - - break; - } - case ISCSI_OPCODE_CLIENT_TEXT_REQ : { - rc = iscsi_connection_pdu_header_handle_text_req( conn, pdu ); - - break; - } - case ISCSI_OPCODE_CLIENT_LOGOUT_REQ : { - rc = iscsi_connection_pdu_header_handle_logout_req( conn, pdu ); - - break; - } - default : { - rc = iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_COMMAND_NOT_SUPPORTED ); - - break; - } - } - - return rc; -} - -/** - * @brief Handles an incoming iSCSI payload data NOP-Out request PDU. - * - * This function handles NOP-Out 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] 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_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; - - if ( ds_len > conn->session->opts.MaxRecvDataSegmentLength ) - ds_len = conn->session->opts.MaxRecvDataSegmentLength; - - const uint64_t lun = iscsi_get_be64(nop_out_pkt->lun); - const uint32_t init_task_tag = iscsi_get_be32(nop_out_pkt->init_task_tag); - - conn->flags &= ~ISCSI_CONNECT_FLAGS_NOP_OUTSTANDING; - - if ( init_task_tag == 0xFFFFFFFFUL ) - return ISCSI_CONNECT_PDU_READ_OK; - - iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, ds_len, false ); - - if ( response_pdu == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_pdu_data_handle_nop_out: Out of memory while allocating iSCSI NOP-In response PDU" ); + if ( ds_len > (uint32_t)conn->session->opts.MaxRecvDataSegmentLength ) + ds_len = conn->session->opts.MaxRecvDataSegmentLength; + iscsi_pdu CLEANUP_PDU response_pdu; + if ( !iscsi_connection_pdu_init( &response_pdu, ds_len, false ) ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } - iscsi_nop_in_packet *nop_in_pkt = (iscsi_nop_in_packet *) response_pdu->bhs_pkt; + iscsi_nop_in_packet *nop_in_pkt = (iscsi_nop_in_packet *) response_pdu.bhs_pkt; nop_in_pkt->opcode = ISCSI_OPCODE_SERVER_NOP_IN; nop_in_pkt->flags = -0x80; @@ -3577,7 +3212,7 @@ static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscs 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; // Minus one does not require endianess conversion - iscsi_put_be32( (uint8_t *) &nop_in_pkt->init_task_tag, init_task_tag ); + nop_in_pkt->init_task_tag = nop_out_pkt->init_task_tag; // Copyed from request packet, no endian conversion required iscsi_put_be32( (uint8_t *) &nop_in_pkt->stat_sn, conn->stat_sn++ ); if ( (nop_out_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) @@ -3588,10 +3223,11 @@ static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscs 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 ); + if ( ds_len != 0UL ) { + memcpy( response_pdu.ds_cmd_data, request_pdu->ds_cmd_data, ds_len ); + } - iscsi_connection_pdu_write( conn, response_pdu ); + iscsi_connection_pdu_write( conn, &response_pdu ); return ISCSI_CONNECT_PDU_READ_OK; } @@ -3606,63 +3242,82 @@ static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscs * * @param[in] conn Pointer to iSCSI connection to handle. May * NOT be NULL, so take caution. - * @param[in] pdu Pointer to iSCSI client request PDU to handle. + * @param[in] request_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) +static int iscsi_connection_handle_scsi_cmd(iscsi_connection *conn, iscsi_pdu *request_pdu) { - iscsi_task *task = pdu->task; + iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) request_pdu->bhs_pkt; - if ( task == NULL ) - return ISCSI_CONNECT_PDU_READ_OK; + if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_WRITE) != 0 ) { // Bidirectional transfer is not supported + logadd( LOG_DEBUG1, "Received SCSI write command from %s", conn->client->hostName ); + // Should really return a write protect error on SCSI layer, but a well-behaving client shouldn't ever + // send a write command anyways, since we declare the device read only. + return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_COMMAND_NOT_SUPPORTED ); + } - if ( task->lun_id != ISCSI_DEFAULT_LUN ) { - logadd( LOG_WARNING, "Received SCSI command for unknown LUN %d", task->lun_id ); - iscsi_scsi_task_lun_process_none( &task->scsi_task ); - iscsi_scsi_task_xfer_complete( &task->scsi_task, pdu ); + iscsi_task *task = iscsi_task_create( conn ); - return ISCSI_CONNECT_PDU_READ_OK; + if ( task == NULL ) { + return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_OUT_OF_RESOURCES ); } - if ( (task->scsi_task.flags & ISCSI_SCSI_TASK_FLAGS_XFER_READ) != 0 ) { - task->scsi_task.buf = NULL; - task->scsi_task.len = task->scsi_task.xfer_len; - } - iscsi_scsi_lun_task_run( &task->scsi_task, pdu ); + uint32_t exp_xfer_len = iscsi_get_be32(scsi_cmd_pkt->exp_xfer_len); - return ISCSI_CONNECT_PDU_READ_OK; -} + task->scsi_task.len = (uint) (((uint8_t *) request_pdu->ds_cmd_data) - ((uint8_t *) request_pdu->bhs_pkt)); // Length of BHS + AHS + task->scsi_task.cdb = &scsi_cmd_pkt->scsi_cdb; + task->scsi_task.xfer_len = exp_xfer_len; + task->init_task_tag = iscsi_get_be32(scsi_cmd_pkt->init_task_tag); -/* - * This function is used to set the info in the connection data structure - * return - * 0: success - * otherwise: error - */ -static int iscsi_connection_login_set_info(iscsi_connection *conn, iscsi_pdu *login_response_pdu, const int type, const uint cid) -{ - conn->flags &= ~ISCSI_CONNECT_FLAGS_AUTH; - conn->cid = (uint16_t) cid; + const uint64_t lun = iscsi_get_be64(scsi_cmd_pkt->lun); + task->lun_id = iscsi_scsi_lun_get_from_iscsi( lun ); - if ( conn->session == NULL ) { - iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - conn->session = iscsi_session_create( conn, type ); + if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_READ) == 0 ) { + if ( exp_xfer_len != 0UL ) { + // Not a read request, but expecting data - not valid + iscsi_task_destroy( task ); - if ( conn->session == NULL ) { - 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_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_INVALID_PDU_FIELD ); + } + } else { + task->scsi_task.flags |= ISCSI_SCSI_TASK_FLAGS_XFER_READ; + } - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + int rc; + + if ( task->lun_id != ISCSI_DEFAULT_LUN ) { + logadd( LOG_WARNING, "Received SCSI command for unknown LUN %d", task->lun_id ); + iscsi_scsi_task_lun_process_none( &task->scsi_task ); + rc = ISCSI_CONNECT_PDU_READ_OK; + } else { + if ( (task->scsi_task.flags & ISCSI_SCSI_TASK_FLAGS_XFER_READ) != 0 ) { + task->scsi_task.buf = NULL; + task->scsi_task.len = task->scsi_task.xfer_len; } - conn->stat_sn = iscsi_get_be32(login_response_pkt->stat_sn); + task->scsi_task.status = ISCSI_SCSI_STATUS_GOOD; - conn->session->exp_cmd_sn = login_response_pdu->cmd_sn; - conn->session->max_cmd_sn = (uint32_t) (login_response_pdu->cmd_sn + ISCSI_DEFAULT_QUEUE_DEPTH - 1UL); + rc = iscsi_scsi_emu_block_process( &task->scsi_task ); + + if ( rc == ISCSI_SCSI_TASK_RUN_UNKNOWN ) { + rc = iscsi_scsi_emu_primary_process( &task->scsi_task ); + + if ( rc == ISCSI_SCSI_TASK_RUN_UNKNOWN ) { + iscsi_scsi_task_status_set( &task->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 ); + // TODO: Free task + rc = ISCSI_SCSI_TASK_RUN_COMPLETE; + } + } } + if ( rc == ISCSI_SCSI_TASK_RUN_COMPLETE ) { + iscsi_scsi_task_xfer_complete( &task->scsi_task, request_pdu ); + } + + iscsi_task_destroy( task ); + return ISCSI_CONNECT_PDU_READ_OK; } @@ -3681,7 +3336,7 @@ static int iscsi_connection_login_set_info(iscsi_connection *conn, iscsi_pdu *lo * @param[in] cid Connection ID (CID). * @return 0 on success, a negative error code otherwise. */ -static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_negotiation_kvp *kvpairs, uint cid) +static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_negotiation_kvp *kvpairs) { int type, rc; iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; @@ -3691,21 +3346,59 @@ static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscs if ( rc < 0 ) return rc; - if ( kvpairs->TargetName != NULL && type == ISCSI_SESSION_TYPE_NORMAL ) { + if ( type != ISCSI_SESSION_TYPE_NORMAL ) { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_SESSION_NO_SUPPORT; + rc = ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } else if ( kvpairs->TargetName != NULL ) { rc = iscsi_image_from_target( conn, login_response_pdu, kvpairs->TargetName ); } else { 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 ) return rc; - return iscsi_connection_login_set_info( conn, login_response_pdu, type, cid ); + if ( conn->session == NULL ) { + iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; + conn->session = iscsi_session_create( type ); + + if ( conn->session == NULL ) { + 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_LOGIN_RESPONSE; + } + + conn->stat_sn = iscsi_get_be32(login_response_pkt->stat_sn); + + conn->session->exp_cmd_sn = login_response_pdu->cmd_sn; + conn->session->max_cmd_sn = (uint32_t) (login_response_pdu->cmd_sn + ISCSI_DEFAULT_QUEUE_DEPTH - 1UL); + } + + return ISCSI_CONNECT_PDU_READ_OK; } +/** + * @brief Writes login options to a PDU (Protocol Data Unit). + * + * This function processes key-value pairs of login negotiation options and + * appends them to the specified PDU. The function ensures the payload of the + * response PDU does not exceed its designated length. + * + * @param[in] conn Pointer to the iSCSI connection structure containing session + * options and other connection-specific information. + * @param[in] pairs Pointer to the iSCSI negotiation key-value pairs structure + * that holds applicable key-value options for the login phase. + * @param[in,out] response_pdu Pointer to the PDU where the login options should + * be added. The PDU's fields, such as data segment and payload length, are + * updated within the function. + * + * @return The updated payload length of the response PDU if successful. + * Returns -1 if an error occurs during key-value pair appending. + */ static int iscsi_write_login_options_to_pdu( iscsi_connection *conn, iscsi_negotiation_kvp *pairs, iscsi_pdu *response_pdu ) { uint payload_len = response_pdu->ds_write_pos; @@ -3760,11 +3453,15 @@ if ( pairs->key != NULL ) ADD_KV_INTERNAL( false, #key, value ); \ */ static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_negotiation_kvp *pairs) { + if ( iscsi_connection_pdu_resize( login_response_pdu, 0, ISCSI_DEFAULT_RECV_DS_LEN ) == NULL ) { + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - iscsi_connection_update_key_value_pairs( conn, pairs ); int payload_len = iscsi_write_login_options_to_pdu( conn, pairs, login_response_pdu ); - if ( payload_len < 0 || payload_len > login_response_pdu->ds_len ) { + if ( payload_len < 0 || (uint32_t)payload_len > login_response_pdu->ds_len ) { + logadd( LOG_DEBUG1, "iscsi_connecction_handle_login_response: Invalid payload length %d, ds_len: %u, write_pos: %u", + payload_len, login_response_pdu->ds_len, login_response_pdu->ds_write_pos ); 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; @@ -3774,9 +3471,8 @@ static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi // Handle current stage (CSG bits) switch ( ISCSI_LOGIN_RESPONSE_FLAGS_GET_CURRENT_STAGE(login_response_pkt->flags) ) { case ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_SECURITY_NEGOTIATION : { - if ( pairs->AuthMethod != NULL && strcasecmp( pairs->AuthMethod, "None" ) == 0 ) { - conn->flags |= ISCSI_CONNECT_FLAGS_AUTH; - } else { + logadd( LOG_DEBUG1, "security nego" ); + if ( pairs->AuthMethod == NULL || strcasecmp( pairs->AuthMethod, "None" ) != 0 ) { // Only "None" supported 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; @@ -3787,10 +3483,7 @@ static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi break; } case ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION : { - if ( conn->state == ISCSI_CONNECT_STATE_INVALID ) { - conn->flags |= ISCSI_CONNECT_FLAGS_AUTH; - } - + // Nothing to do, expect client to request transition to full feature phase break; } case ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FULL_FEATURE_PHASE : @@ -3805,32 +3498,13 @@ static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi if ( (login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0 ) { // Client set the transition bit - requests to move on to next stage switch ( ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) ) { - case ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION : { - conn->login_phase = ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION; - - break; - } - case ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION : { - conn->login_phase = ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION; - - break; - } 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, (uint16_t) conn->session->tsih ); - - if ( (conn->session->type != ISCSI_SESSION_TYPE_NORMAL) && (conn->session->type != ISCSI_SESSION_TYPE_DISCOVERY) ) { - logadd( LOG_DEBUG1, "Unsupported session type %d, rejecting", conn->session->type ); - iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - - login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; - login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; - - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - } + iscsi_put_be16( (uint8_t *) &login_response_pkt->tsih, 42 ); - conn->flags |= ISCSI_CONNECT_FLAGS_FULL_FEATURE; + conn->state = ISCSI_CONNECT_STATE_NORMAL_SESSION; + iscsi_connection_update_key_value_pairs( conn, pairs ); break; } @@ -3856,53 +3530,53 @@ static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi * * @param[in] conn Pointer to iSCSI connection to handle. May * NOT be NULL, so take caution. - * @param[in] pdu Pointer to iSCSI client request PDU to handle. + * @param[in] request_pdu Pointer to iSCSI client request PDU to handle. * May be NULL in which case an error is returned. + * @param login_response_pdu * @return 0 on success. A negative value indicates * an error. A positive value a warning. */ -static int iscsi_connection_pdu_data_handle_login_req(iscsi_connection *conn, iscsi_pdu *pdu) +static int iscsi_connection_handle_login_req(iscsi_connection *conn, iscsi_pdu *request_pdu) { int rc; - iscsi_pdu *login_response_pdu = (iscsi_pdu *) conn->login_response_pdu; - if ( login_response_pdu == NULL ) - return ISCSI_CONNECT_PDU_READ_OK; + if ( request_pdu->ds_len > ISCSI_DEFAULT_RECV_DS_LEN || conn->state != ISCSI_CONNECT_STATE_NEW ) + return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); - iscsi_login_req_packet *login_req_pkt = (iscsi_login_req_packet *) pdu->bhs_pkt; - uint cid = iscsi_get_be16(login_req_pkt->cid); + const iscsi_login_req_packet *login_req_pkt = (iscsi_login_req_packet *) request_pdu->bhs_pkt; - iscsi_negotiation_kvp pairs; - iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - rc = iscsi_parse_login_key_value_pairs( &pairs, (uint8_t *) pdu->ds_cmd_data, pdu->ds_len ); + request_pdu->cmd_sn = iscsi_get_be32(login_req_pkt->cmd_sn); - if ( rc < 0 || rc > login_response_pdu->ds_len ) { - 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; + iscsi_pdu CLEANUP_PDU login_response_pdu; + if ( !iscsi_connection_pdu_init( &login_response_pdu, 0, false ) ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + rc = iscsi_connection_pdu_login_response_init( &login_response_pdu, request_pdu ); + + if ( rc < 0 ) { + // response_init set an error code in the response pdu, send it right away and bail out + return iscsi_send_login_response_pdu( conn, &login_response_pdu ); } - if ( conn->state == ISCSI_CONNECT_STATE_INVALID ) { - rc = iscsi_connection_handle_login_phase_none( conn, login_response_pdu, &pairs, cid ); - logadd( LOG_DEBUG1, "rc2: %d", rc ); + iscsi_negotiation_kvp pairs; + iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu.bhs_pkt; + rc = iscsi_parse_login_key_value_pairs( &pairs, (uint8_t *) request_pdu->ds_cmd_data, request_pdu->ds_len ); - if ( rc != ISCSI_CONNECT_PDU_READ_OK ) { - iscsi_connection_pdu_login_response( conn, login_response_pdu ); + if ( rc < 0 ) { + 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_OK; - } + return iscsi_send_login_response_pdu( conn, &login_response_pdu ); } - rc = iscsi_connecction_handle_login_response( conn, login_response_pdu, &pairs ); + rc = iscsi_connection_handle_login_phase_none( conn, &login_response_pdu, &pairs ); - if ( rc == ISCSI_CONNECT_PDU_READ_OK ) { - conn->state = ISCSI_CONNECT_STATE_RUNNING; + if ( rc != ISCSI_CONNECT_PDU_READ_OK ) { + return iscsi_send_login_response_pdu( conn, &login_response_pdu ); } - iscsi_connection_pdu_login_response( conn, login_response_pdu ); - - return ISCSI_CONNECT_PDU_READ_OK; + iscsi_connecction_handle_login_response( conn, &login_response_pdu, &pairs ); + return iscsi_send_login_response_pdu( conn, &login_response_pdu ); } /** @@ -3915,308 +3589,241 @@ static int iscsi_connection_pdu_data_handle_login_req(iscsi_connection *conn, is * * @param[in] conn Pointer to iSCSI connection to handle. May * NOT be NULL, so take caution. - * @param[in] pdu Pointer to iSCSI client request PDU to handle. + * @param[in] request_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) +static int iscsi_connection_handle_text_req(iscsi_connection *conn, iscsi_pdu *request_pdu) { - iscsi_text_req_packet *text_req_pkt = (iscsi_text_req_packet *) pdu->bhs_pkt; - iscsi_negotiation_kvp pairs; - int rc = iscsi_parse_login_key_value_pairs( &pairs, (uint8_t *) pdu->ds_cmd_data, pdu->ds_len ); + iscsi_text_req_packet *text_req_pkt = (iscsi_text_req_packet *) request_pdu->bhs_pkt; - if ( rc < 0 ) { - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + if ( request_pdu->ds_len > ISCSI_MAX_DS_SIZE ) + return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); + + 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) ) { + // Continue and Final at the same time is invalid + return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); + } + if ( (text_req_pkt->flags & ISCSI_TEXT_REQ_FLAGS_FINAL) == 0 ) { + // Text request spread across multiple PDUs not supported + return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_COMMAND_NOT_SUPPORTED ); + } + if ( text_req_pkt->target_xfer_tag != 0xFFFFFFFFUL ) { + // Initial request must have this set to all 1 + return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); } - iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 8192, false ); + 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 ( response_pdu == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_pdu_data_handle_text_req: Out of memory while allocating iSCSI text response PDU" ); + iscsi_negotiation_kvp pairs; + int rc = iscsi_parse_login_key_value_pairs( &pairs, (uint8_t *) request_pdu->ds_cmd_data, request_pdu->ds_len ); + if ( rc < 0 ) { return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } - iscsi_connection_update_key_value_pairs( conn, &pairs ); + iscsi_pdu CLEANUP_PDU response_pdu; + if ( !iscsi_connection_pdu_init( &response_pdu, MIN( 8192, conn->session->opts.MaxRecvDataSegmentLength ), false ) ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - int payload_len = iscsi_write_login_options_to_pdu( conn, &pairs, response_pdu ); + iscsi_connection_update_key_value_pairs( conn, &pairs ); - if ( payload_len < 0 || payload_len > response_pdu->ds_len ) { - iscsi_connection_pdu_destroy( response_pdu ); + // TODO: Handle SendTargets + int payload_len = iscsi_write_login_options_to_pdu( conn, &pairs, &response_pdu ); + if ( payload_len < 0 || (uint32_t)payload_len > response_pdu.ds_len ) { return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } - iscsi_text_response_packet *text_response_pkt = (iscsi_text_response_packet *) iscsi_connection_pdu_resize( response_pdu, 0, response_pdu->ds_write_pos ); + iscsi_text_response_packet *text_response_pkt = + (iscsi_text_response_packet *) iscsi_connection_pdu_resize( &response_pdu, 0, response_pdu.ds_write_pos ); text_response_pkt->opcode = ISCSI_OPCODE_SERVER_TEXT_RES; text_response_pkt->flags = (int8_t) ISCSI_TEXT_RESPONSE_FLAGS_FINAL; text_response_pkt->reserved = 0; - iscsi_put_be32( (uint8_t *) &text_response_pkt->total_ahs_len, payload_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 ); - } + // TotalAHSLength is always 0 and DataSegmentLength is 24-bit, so write in one step. + iscsi_put_be32( (uint8_t *) &text_response_pkt->total_ahs_len, response_pdu.ds_write_pos ); + 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. + text_response_pkt->target_xfer_tag = 0xFFFFFFFFUL; // Minus one does not require endianess conversion 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++; + 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 ); - - return ISCSI_CONNECT_PDU_READ_OK; + return iscsi_connection_pdu_write( conn, &response_pdu ) ? ISCSI_CONNECT_PDU_READ_OK : ISCSI_CONNECT_PDU_READ_ERR_FATAL; } /** - * @brief Handles an incoming iSCSI payload data PDU. + * @brief Handles an incoming iSCSI PDU. * - * This function handles all 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] pdu Pointer to iSCSI client request PDU to handle. + * @param[in] request_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(iscsi_connection *conn, iscsi_pdu *pdu) +static int iscsi_connection_pdu_handle(iscsi_connection *conn, iscsi_pdu *request_pdu) { int rc = 0; - const uint8_t opcode = ISCSI_GET_OPCODE(pdu->bhs_pkt->opcode); + const uint8_t opcode = ISCSI_GET_OPCODE(request_pdu->bhs_pkt->opcode); - switch ( opcode ) { - case ISCSI_OPCODE_CLIENT_NOP_OUT : { - rc = iscsi_connection_pdu_data_handle_nop_out( conn, pdu ); - - break; + if ( conn->state == ISCSI_CONNECT_STATE_NEW ) { + // Fresh connection, not logged in yet - we only support LOGIN in this state + if ( opcode == ISCSI_OPCODE_CLIENT_LOGIN_REQ ) { + rc = iscsi_connection_handle_login_req( conn, request_pdu ); + } else { + rc = iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); } - case ISCSI_OPCODE_CLIENT_SCSI_CMD : { - rc = iscsi_connection_pdu_data_handle_scsi_cmd( conn, pdu ); + } else if ( conn->state == ISCSI_CONNECT_STATE_EXITING ) { + // Exiting, nothing to do + rc = ISCSI_CONNECT_PDU_READ_OK; + } else if ( conn->state == ISCSI_CONNECT_STATE_NORMAL_SESSION ) { + // Normal operation + rc = iscsi_connection_handle_cmd_sn( conn, request_pdu ); + if ( rc != 0 ) + return rc; - break; - } - case ISCSI_OPCODE_CLIENT_LOGIN_REQ : { - rc = iscsi_connection_pdu_data_handle_login_req( conn, pdu ); + switch ( opcode ) { + case ISCSI_OPCODE_CLIENT_NOP_OUT : { + rc = iscsi_connection_handle_nop_out( conn, request_pdu ); - break; - } - case ISCSI_OPCODE_CLIENT_TEXT_REQ : { - rc = iscsi_connection_pdu_data_handle_text_req( conn, pdu ); + break; + } + case ISCSI_OPCODE_CLIENT_SCSI_CMD : { + rc = iscsi_connection_handle_scsi_cmd( conn, request_pdu ); - break; - } - case ISCSI_OPCODE_CLIENT_TASK_FUNC_REQ : - case ISCSI_OPCODE_CLIENT_LOGOUT_REQ : - case ISCSI_OPCODE_CLIENT_SNACK_REQ : { - break; - } - default : { - return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); + break; + } + case ISCSI_OPCODE_CLIENT_TEXT_REQ : { + rc = iscsi_connection_handle_text_req( conn, request_pdu ); - break; + break; + } + case ISCSI_OPCODE_CLIENT_LOGOUT_REQ : { + rc = iscsi_connection_handle_logout_req( conn, request_pdu ); + + break; + } + case ISCSI_OPCODE_CLIENT_TASK_FUNC_REQ : { + // TODO: Send OK if a task is requested to be cancelled + break; + } + default : { + rc = iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); + + break; + } } } if ( rc < 0 ) { logadd( LOG_ERROR, "Fatal error during payload handler (opcode 0x%02x) detected for client %s", (int) opcode, conn->client->hostName ); } + return rc; } /** - * @brief Retrieves and merges splitted iSCSI PDU data read from TCP/IP socket. - * - * This function handles partial reads of data - * packets.\n - * The function is guaranteed to read as many bytes as indicated by the - * PDU struct, unless the read timeout is reached, or the connection - * is closed/reset.\n - * Care is also taken for padding bytes that have to be read. It is - * assumed the according buffer will have enough space for the padding - * bytes, which is always guaranteed when using the create and resize - * helpers for allocating PDUs. - * - * @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. + * @brief Reads and processes incoming iSCSI connection PDUs in a loop. + * + * This function continuously reads Protocol Data Units (PDUs) on an iSCSI + * connection and performs operations based on the type and content of the + * received data. The function processes Basic Header Segment (BHS), Additional + * Header Segment (AHS), and Data Segment (DS) as part of the PDU handling. If + * any errors occur during the process, the function gracefully exits the loop. + * + * @param[in] conn Pointer to the iSCSI connection object. Must not be NULL and + * contains the state and data required for processing the iSCSI connection. + * @param[in] request Pointer to the initial received data for the PDU. This + * serves as the partially received data of the BHS. Must not be NULL. + * @param[in] len Length of the already received portion of the BHS in bytes. */ -static int iscsi_connection_pdu_data_read(iscsi_connection *conn, iscsi_pdu *pdu) +static void iscsi_connection_pdu_read_loop(iscsi_connection *conn, const dnbd3_request_t *request, const int len) { - const uint32_t ds_len = ISCSI_ALIGN( pdu->ds_len, ISCSI_ALIGN_SIZE ); + iscsi_pdu CLEANUP_PDU request_pdu; - if ( pdu->recv_pos < ds_len ) { - const int32_t len = iscsi_connection_read( conn, (((uint8_t *) pdu->ds_cmd_data) + pdu->recv_pos), (ds_len - pdu->recv_pos) ); - - if ( len < 0L ) - return len; + if ( !iscsi_connection_pdu_init( &request_pdu, 0, false ) ) + return; - pdu->recv_pos += len; + // 1) Receive BHS (partially already received, in "request", merge and finish) + memcpy( request_pdu.bhs_pkt, request, len ); + if ( sock_recv( conn->client->sock, ((uint8_t *)request_pdu.bhs_pkt) + len, sizeof(iscsi_bhs_packet) - len ) + != sizeof(iscsi_bhs_packet) - len ) { + logadd( LOG_INFO, "Cannot receive first BHS for client %s", conn->client->hostName ); + return; } - if ( pdu->recv_pos < ds_len ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - - return ISCSI_CONNECT_PDU_READ_OK; -} - -/** - * @brief Retrieves and merges splitted iSCSI PDU data read from TCP/IP socket. - * - * This function handles partial reads of BHS, AHS - * and DS packet data.\n - * Since iSCSI data can span multiple packets, not - * only by TCP/IP itself, but also by iSCSI protocol - * specifications, multiple calls are needed in order - * to be sure that all data packets have been - * received. - * - * @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. - */ -static int iscsi_connection_pdu_read(iscsi_connection *conn) -{ - int prev_recv_state; - do { - iscsi_pdu *pdu = conn->pdu_processing; - - prev_recv_state = conn->pdu_recv_state; - - switch ( conn->pdu_recv_state ) { - case ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_READY : { - assert( conn->pdu_processing == NULL ); - conn->pdu_processing = iscsi_connection_pdu_create( conn, 0UL, false ); - - if ( conn->pdu_processing == NULL ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - - conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_HDR; + // 2) Evaluate BHS regarding length of AHS and DS + iscsi_bhs_packet *bhs_pkt = request_pdu.bhs_pkt; + const uint ahs_len = ((uint) bhs_pkt->total_ahs_len * ISCSI_ALIGN_SIZE); + const uint32_t ds_len = iscsi_get_be24(bhs_pkt->ds_len); - break; - } - case ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_HDR : { - while ( 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 < 0L ) { - conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; - - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } - - pdu->bhs_pos += len; - } - - if ( (conn->flags & ISCSI_CONNECT_FLAGS_LOGGED_OUT) != 0 ) { - conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; - - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } - - iscsi_bhs_packet *bhs_pkt = pdu->bhs_pkt; - const uint ahs_len = ((uint) bhs_pkt->total_ahs_len * 4); - const uint32_t ds_len = iscsi_get_be24(bhs_pkt->ds_len); - - bhs_pkt = iscsi_connection_pdu_resize( pdu, ahs_len, ds_len ); - - if ( bhs_pkt == NULL ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - - int pos = 0; - while ( pos < ahs_len ) { - const int32_t len = iscsi_connection_read( conn, (((uint8_t *) pdu->ahs_pkt) + pos), (ahs_len - pos) ); + bhs_pkt = iscsi_connection_pdu_resize( &request_pdu, ahs_len, ds_len ); - if ( len < 0L ) { - conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; - - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } + if ( bhs_pkt == NULL ) { + logadd( LOG_WARNING, "Cannot resize PDU for client %s", conn->client->hostName ); + break; + } - pos += len; - } + // 3) Receive the optional AHS + if ( ahs_len != 0 && sock_recv( conn->client->sock, request_pdu.ahs_pkt, ahs_len ) != ahs_len ) { + logadd( LOG_DEBUG1, "Could not receive AHS for client %s", conn->client->hostName ); + break; + } - if ( iscsi_connection_pdu_header_handle( conn, pdu ) < 0 ) { - conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; - } else { - conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_DATA; - } + // 4) Receive the optional DS + if ( request_pdu.ds_len != 0U ) { + const uint32_t padded_ds_len = ISCSI_ALIGN( request_pdu.ds_len, ISCSI_ALIGN_SIZE ); + if ( sock_recv( conn->client->sock, request_pdu.ds_cmd_data, padded_ds_len ) != padded_ds_len ) { + logadd( LOG_DEBUG1, "Could not receive DS for client %s", conn->client->hostName ); break; } - case ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_DATA : { - if ( pdu->ds_len != 0U ) { - const int len = iscsi_connection_pdu_data_read( conn, pdu ); - - if ( len < 0 ) { - conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; - - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } else if ( len > 0 ) { - return ISCSI_CONNECT_PDU_READ_OK; - } - } - - int rc; - - conn->pdu_processing = NULL; - if ( (conn->flags & ISCSI_CONNECT_FLAGS_REJECTED) != 0 ) { - rc = 0; - } else { - rc = iscsi_connection_pdu_data_handle( conn, pdu ); - } - - iscsi_connection_pdu_destroy( pdu ); - - if ( rc == 0 ) { - conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_READY; + } - return ISCSI_CONNECT_PDU_READ_PROCESSED; - } - conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; + // 5) Handle PDU + if ( iscsi_connection_pdu_handle( conn, &request_pdu ) != ISCSI_CONNECT_PDU_READ_OK + || conn->state == ISCSI_CONNECT_STATE_EXITING ) { + break; + } + // In case we needed an extra buffer, reset + if ( request_pdu.big_alloc != NULL ) { + iscsi_connection_pdu_destroy( &request_pdu ); + if ( !iscsi_connection_pdu_init( &request_pdu, 0, false ) ) { + logadd( LOG_WARNING, "Cannot re-initialize PDU for client %s", conn->client->hostName ); break; } - case ISCSI_CONNECT_PDU_RECV_STATE_ERR : { - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + } - break; - } - default : { - logadd( LOG_ERROR, "iscsi_connection_pdu_read: Fatal error reading, unknown packet status." - " Should NEVER happen! Please report this bug to the developer" ); + // Move first part of next iteration last in this loop, as we completed the first, partial + // header before the loop - this saves us from accounting for this within the mainloop - break; - } - } - if ( conn->state == ISCSI_CONNECT_STATE_EXITING || _shutdown ) { - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + // 1) Receive entire BHS + if ( sock_recv( conn->client->sock, request_pdu.bhs_pkt, sizeof(iscsi_bhs_packet) ) != sizeof(iscsi_bhs_packet) ) { + logadd( LOG_INFO, "Cannot receive BHS for client %s", conn->client->hostName ); + break; } - } while ( prev_recv_state != conn->pdu_recv_state ); - - return 0; + } while ( !_shutdown ); } /** @@ -4247,24 +3854,15 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ return; } - conn->pdu_processing = iscsi_connection_pdu_create( conn, 0UL, false ); - - if ( conn->pdu_processing == NULL ) { - iscsi_connection_destroy( conn ); - - 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; - static atomic_int CONN_ID = 0; conn->id = ++CONN_ID; - while ( iscsi_connection_pdu_read( conn ) >= ISCSI_CONNECT_PDU_READ_OK ) { - } + iscsi_connection_pdu_read_loop( conn, request, len ); + + // Wait for the client to receive any pending outgoing PDUs + shutdown( client->sock, SHUT_WR ); + sock_setTimeout( client->sock, 100 ); + while ( recv( client->sock, (void *)request, len, 0 ) > 0 ) {} iscsi_connection_destroy( conn ); } diff --git a/src/server/iscsi.h b/src/server/iscsi.h index 05f22e7..f4fa6b1 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -627,7 +627,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_10 { iscsi_scsi_cdb cdb; /// Flags. - int8_t flags; + uint8_t flags; /// Logical Block Address (LBA). uint32_t lba; @@ -653,7 +653,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_12 { iscsi_scsi_cdb cdb; /// Flags. - int8_t flags; + uint8_t flags; /// Logical Block Address (LBA). uint32_t lba; @@ -679,7 +679,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_16 { iscsi_scsi_cdb cdb; /// Flags. - int8_t flags; + uint8_t flags; /// Logical Block Address (LBA). uint64_t lba; @@ -842,7 +842,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_mode_sense_6 { iscsi_scsi_cdb cdb; /// Flags. - int8_t flags; + uint8_t flags; /// Page code and page control. uint8_t page_code_control; @@ -918,7 +918,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_mode_sense_10 { iscsi_scsi_cdb cdb; /// Flags. - int8_t flags; + uint8_t flags; /// Page code and page control. uint8_t page_code_control; @@ -1266,7 +1266,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_std_inquiry_data_packet { int8_t services_flags; /// Flags. - int8_t flags; + uint8_t flags; /// Vendor identification. uint8_t vendor_id[8]; @@ -1311,7 +1311,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_ext_inquiry_data_packet { uint8_t vendor_spec[20]; /// Flags. - int8_t flags; + uint8_t flags; /// Reserved for future usage (always MUST be 0). uint8_t reserved; @@ -1591,7 +1591,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_design_desc_inquiry_d uint8_t protocol_id_code_set; /// Flags. - int8_t flags; + uint8_t flags; /// Reserved for future usage (always MUST be 0). uint8_t reserved; @@ -1897,7 +1897,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_ext_inquiry_data_pack */ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_block_limits_inquiry_data_packet { /// Flags. - int8_t flags; + uint8_t flags; /// Maximum COMPARE AND WRITE length in logical blocks. uint8_t max_cmp_write_len; @@ -2036,7 +2036,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_block_dev_chars_inqui uint8_t product_type; /// Flags. - int8_t flags; + uint8_t flags; /// Support flags. uint8_t support_flags; @@ -2297,7 +2297,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_service_action_in_16_parameter uint32_t block_len; /// Flags: RC_BASIS, P_TYPE and PROT_EN. - int8_t flags; + uint8_t flags; /// P_I_EXPONENT and logical blocks per physical block exponent. uint8_t exponents; @@ -2380,7 +2380,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_6_parameter_header_ uint8_t medium_type; /// Flags. - int8_t flags; + uint8_t flags; /// Block descriptor length in bytes. uint8_t block_desc_len; @@ -2412,7 +2412,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_10_parameter_header uint8_t medium_type; /// Flags. - int8_t flags; + uint8_t flags; /// Long Logical Block Address (LONGLBA). uint8_t long_lba; @@ -2739,7 +2739,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_read_write_err_reco iscsi_scsi_mode_sense_mode_page_data_packet mode_page; /// Flags. - int8_t flags; + uint8_t flags; /// Read retry count. uint8_t read_retry_cnt; @@ -2806,7 +2806,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_verify_err_recovery iscsi_scsi_mode_sense_mode_page_data_packet mode_page; /// Flags. - int8_t flags; + uint8_t flags; /// Verify retry count. uint8_t verify_retry_cnt; @@ -2869,7 +2869,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_caching_mode_page_d iscsi_scsi_mode_sense_mode_page_data_packet mode_page; /// Flags. - int8_t flags; + uint8_t flags; /// Retention priority. uint8_t retention_pri; @@ -2913,7 +2913,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_control_mode_page_d iscsi_scsi_mode_sense_mode_page_data_packet mode_page; /// Flags. - int8_t flags; + uint8_t flags; /// Queue flags. int8_t queue_flags; @@ -2945,7 +2945,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_control_ext_mode_pa iscsi_scsi_mode_sense_mode_sub_page_data_packet mode_sub_page; /// Flags. - int8_t flags; + uint8_t flags; /// Initial command priority. uint8_t init_cmd_pri; @@ -2971,7 +2971,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_xor_ext_mode_page_d iscsi_scsi_mode_sense_mode_page_data_packet mode_page; /// Flags. - int8_t flags; + uint8_t flags; /// Reserved for future usage (always MUST be 0 for now). uint8_t reserved; @@ -3006,7 +3006,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_power_cond_mode_pag iscsi_scsi_mode_sense_mode_page_data_packet mode_page; /// Flags. - int8_t flags; + uint8_t flags; /// Idle and standby flags. int8_t idle_standby_flags; @@ -3053,7 +3053,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_info_exceptions_con iscsi_scsi_mode_sense_mode_page_data_packet mode_page; /// Flags. - int8_t flags; + uint8_t flags; /// Method Of Reporting Informational Exceptions (MRIE) flags. uint8_t mrie; @@ -3296,7 +3296,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cmd_packet { uint8_t opcode; /// Flags and Task Attributes. - int8_t flags_task; + uint8_t flags_task; /// Reserved for future usage, MUST always be 0. uint16_t reserved; @@ -3570,7 +3570,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_response_packet { uint8_t opcode; /// Flags. - int8_t flags; + uint8_t flags; /// This field contains the iSCSI service response. uint8_t response; @@ -3794,7 +3794,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_data_in_response_packet { uint8_t opcode; /// Incoming data flags. The fields StatSN, Status, and Residual Count only have meaningful content if the S bit is set to 1. - int8_t flags; + uint8_t flags; /// Rserved for future usage, always MUST be 0. uint8_t reserved; @@ -3937,7 +3937,7 @@ typedef struct __attribute__((packed)) iscsi_text_req_packet { uint8_t opcode; /// Text request flags. - int8_t flags; + uint8_t flags; /// Reserved for future usage, always MUST be 0. uint16_t reserved; @@ -4072,7 +4072,7 @@ typedef struct __attribute__((packed)) iscsi_text_response_packet { uint8_t opcode; /// Text response flags. - int8_t flags; + uint8_t flags; /// Reserved for future usage, always MUST be 0. uint16_t reserved; @@ -4359,7 +4359,7 @@ typedef struct __attribute__((packed)) iscsi_login_req_packet { uint8_t opcode; /// Login request flags. - int8_t flags; + uint8_t flags; /** * @brief Version-max indicates the maximum version number supported. @@ -4459,17 +4459,8 @@ typedef struct __attribute__((packed)) iscsi_login_req_packet { /// Reserved for future usage, always MUST be 0. uint64_t reserved2[2]; - - /** - * @brief Data segment - Login Parameters in Text Request Format. - * - * The initiator MUST provide some basic parameters in order - * 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_scsi_ds_cmd_data ds_cmd_data; } iscsi_login_req_packet; - +ASSERT_IS_BHS( iscsi_login_req_packet ); /// Login response Next Stage (NSG) flags: SecurityNegotiation. #define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION 0x0 @@ -4718,7 +4709,7 @@ typedef struct __attribute__((packed)) iscsi_login_response_packet { uint8_t opcode; /// Login response flags. - int8_t flags; + uint8_t flags; /** * @brief This is the highest version number supported by the target. @@ -5025,7 +5016,7 @@ typedef struct __attribute__((packed)) iscsi_logout_response_packet { uint8_t opcode; /// Reserved for future usage (must be always 0x80 for now). - int8_t flags; + uint8_t flags; /// Response. uint8_t response; @@ -5175,7 +5166,7 @@ typedef struct __attribute__((packed)) iscsi_reject_packet { uint8_t opcode; /// Reserved for future usage (must be always 0x80 for now). - int8_t flags; + uint8_t flags; /** * @brief Reject reason. @@ -5284,7 +5275,7 @@ typedef struct __attribute__((packed)) iscsi_nop_out_packet { uint8_t opcode; /// Reserved for future usage (must be always 0x80 for now). - int8_t flags; + uint8_t flags; /// Reserved for future usage, always MUST be 0. uint16_t reserved; @@ -5382,7 +5373,7 @@ typedef struct __attribute__((packed)) iscsi_nop_in_packet { uint8_t opcode; /// Reserved for future usage (must be always 0x80 for now). - int8_t flags; + uint8_t flags; /// Reserved for future usage, always MUST be 0. uint16_t reserved; @@ -6064,9 +6055,6 @@ typedef struct iscsi_session { /// MaxCmdSN. uint32_t max_cmd_sn; - /// Current text Initiator Task Tag (ITT). - uint32_t current_text_init_task_tag; - /// Session options client sent in login request. iscsi_session_options opts; } iscsi_session; @@ -6094,27 +6082,9 @@ typedef struct iscsi_pdu iscsi_pdu; #define ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER_XCHG_NOT_ONCE (-4) -/// iSCSI connection flags: Rejected. -#define ISCSI_CONNECT_FLAGS_REJECTED (1 << 1) - -/// iSCSI connection flags: Logged out. -#define ISCSI_CONNECT_FLAGS_LOGGED_OUT (1 << 2) - /// iSCSI connection flags: Full feature. #define ISCSI_CONNECT_FLAGS_FULL_FEATURE (1 << 3) -/// iSCSI connection flags: CHAP authentication is disabled. -#define ISCSI_CONNECT_FLAGS_CHAP_DISABLE (1 << 4) - -/// iSCSI connection flags: CHAP authentication is required. -#define ISCSI_CONNECT_FLAGS_CHAP_REQUIRE (1 << 5) - -/// iSCSI connection flags: CHAP authentication is mutual. -#define ISCSI_CONNECT_FLAGS_CHAP_MUTUAL (1 << 6) - -/// iSCSI connection flags: Authenticated. -#define ISCSI_CONNECT_FLAGS_AUTH (1 << 7) - /// iSCSI connection flags: Oustanding NOP. #define ISCSI_CONNECT_FLAGS_NOP_OUTSTANDING (1 << 8) @@ -6132,13 +6102,13 @@ typedef struct iscsi_pdu iscsi_pdu; #define ISCSI_CONNECT_PDU_RECV_STATE_ERR 3 -/// iSCSI connection state: Invalid. -#define ISCSI_CONNECT_STATE_INVALID 0 +/// iSCSI connection state: Fresh connection, no login yet. +#define ISCSI_CONNECT_STATE_NEW 0 -/// iSCSI connection state: Running. -#define ISCSI_CONNECT_STATE_RUNNING 1 +/// iSCSI connection state: Running as session type "normal". +#define ISCSI_CONNECT_STATE_NORMAL_SESSION 1 -/// iSCSI connection state: Exiting. +/// iSCSI connection state: Exiting, teardown of connection imminent. #define ISCSI_CONNECT_STATE_EXITING 2 @@ -6161,18 +6131,9 @@ typedef struct iscsi_connection { /// Associated dnbd3 client dnbd3_client_t *client; - /// Current PDU being processed. - iscsi_pdu *pdu_processing; - - /// Login response PDU. - iscsi_pdu *login_response_pdu; - /// Internal connection identifier int id; - /// iSCSI connection receiving state. - int pdu_recv_state; - /// iSCSI connection flags. int flags; @@ -6214,6 +6175,8 @@ typedef struct iscsi_task iscsi_task; /// iSCSI PDU flags: Rejected. #define ISCSI_PDU_FLAGS_REJECTED (1 << 0) +/// iSCSI PDU will contain a small buffer for sending/receiving trivial PDUs with no/very small DS, and small AH +#define ISCSI_INTERNAL_BUFFER_SIZE (2 * ISCSI_BHS_SIZE) /** * @brief This structure is used to partially read PDU data. @@ -6232,9 +6195,6 @@ typedef struct iscsi_pdu { /// iSCSI DataSegment (DS) packet data for fast access and is straight after BHS, AHS and header digest packet in memory. iscsi_scsi_ds_cmd_data *ds_cmd_data; - /// iSCSI task handling this PDU. - iscsi_task *task; - /// Flags. int flags; @@ -6250,11 +6210,14 @@ typedef struct iscsi_pdu { /// DS Buffer write pos when filling buffer for sending. uint32_t ds_write_pos; - /// Read position in AHS + DS buffer (recv). - uint32_t recv_pos; - /// CmdSN. uint32_t cmd_sn; + + /// If we need a larger area than internal_buffer + void *big_alloc; + + /// Used for smaller PDUs to avoid extra malloc/free + char internal_buffer[ISCSI_INTERNAL_BUFFER_SIZE]; } iscsi_pdu; diff --git a/src/shared/sockhelper.c b/src/shared/sockhelper.c index 08d73fc..b3f479a 100644 --- a/src/shared/sockhelper.c +++ b/src/shared/sockhelper.c @@ -440,7 +440,7 @@ ssize_t sock_recv(const int sock, void *buffer, const size_t len) return (ssize_t)done; } -bool sock_sendPadding(const int fd, uint32_t bytes) +bool sock_sendPadding(const int fd, size_t bytes) { static char nullbytes[512] = {0}; -- cgit v1.2.3-55-g7522 From 186eecd2417b6937341e6c07b446f991142207d1 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 30 Oct 2025 23:31:23 +0100 Subject: [SERVER] iscsi: Unify usage of length and position variables There were a lot of similarly named and redundant variables in various structs named pos, len, xfer_len, des_xfer_pos, etc. It could be very challenging to keep track of what information is stored where when working with the code. Attempt to minimize this by relying only on a single "len" variable in the scsi_task struct. This refactoring uncovered a few inconsistencies in how allocation length limitations were handled, which were addressed here too. --- src/server/iscsi.c | 445 ++++++++++++++++++++++++----------------------------- src/server/iscsi.h | 229 +-------------------------- 2 files changed, 213 insertions(+), 461 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 9734a4b..d155fc4 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -77,7 +77,7 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task); static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task); -static void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task); // Allocates and initializes a SCSI task +static void iscsi_scsi_task_init(iscsi_scsi_task *scsi_task); // Initializes a SCSI task static void iscsi_scsi_task_xfer_complete(iscsi_scsi_task *scsi_task, iscsi_pdu *request_pdu); // Callback function when an iSCSI SCSI task completed the data transfer @@ -360,15 +360,12 @@ static iscsi_task *iscsi_task_create(iscsi_connection *conn) 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; - iscsi_scsi_task_create( &task->scsi_task ); + iscsi_scsi_task_init( &task->scsi_task ); task->scsi_task.connection = conn; return task; @@ -401,17 +398,18 @@ static void iscsi_task_destroy(iscsi_task *task) * associated DNBD3 image as well and sends * it to the initiator. * - * @pararm[in] conn Pointer to iSCSI connection for which the + * @param[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 + * @param[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. + * @param[in] pos Offset of data to be sent in bytes. + * @param[in] len Length of data to be sent in bytes + * @param[in] res_cnt Residual Count. + * @param[in] data_sn Data Sequence Number (DataSN). + * @param[in] flags Flags for this data packet. + * @param[in] immediate whether immediate bit was set in this request * @return true success, false error */ static bool iscsi_scsi_data_in_send(iscsi_connection *conn, iscsi_task *task, @@ -514,26 +512,28 @@ 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->session->opts.MaxRecvDataSegmentLength; - uint32_t res_cnt = 0UL; - int8_t flags = 0; + const uint32_t expected_len = task->scsi_task.exp_xfer_len; + uint32_t xfer_len = task->scsi_task.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); + if ( expected_len < xfer_len ) { + res_cnt = (xfer_len - expected_len); + xfer_len = expected_len; flags |= ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_OVERFLOW; + } else if ( expected_len > xfer_len ) { + res_cnt = (expected_len - xfer_len); + flags |= ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_UNDERFLOW; } if ( xfer_len == 0UL ) return 0; uint32_t data_sn = task->data_sn; uint32_t max_burst_offset = 0UL; + // Max burst length = total length of payload in all PDUs const uint32_t max_burst_len = conn->session->opts.MaxBurstLength; + // Max recv segment length = total length of one individual PDU + const uint32_t seg_len = conn->session->opts.MaxRecvDataSegmentLength; const uint32_t data_in_seq_count = ((xfer_len - 1) / max_burst_len) + 1; int8_t status = 0; @@ -554,7 +554,7 @@ static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, iscsi_task *task 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) && (task->des_data_xfer_pos == task->scsi_task.xfer_len) ) { + if ( (task->scsi_task.sense_data_len == 0U) && ((offset + len) == xfer_len) ) { flags |= (int8_t) ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS; status |= flags; } @@ -580,7 +580,7 @@ static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, iscsi_task *task * @param[in] scsi_task Pointer to SCSI task. This * may NOT be NULL, so be careful. */ -static void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task) +static void iscsi_scsi_task_init(iscsi_scsi_task *scsi_task) { scsi_task->cdb = NULL; scsi_task->sense_data = NULL; @@ -588,9 +588,9 @@ static void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task) scsi_task->must_free = true; scsi_task->len = 0UL; scsi_task->id = 0ULL; - scsi_task->flags = 0; - scsi_task->xfer_pos = 0UL; - scsi_task->xfer_len = 0UL; + scsi_task->is_read = false; + scsi_task->is_write = false; + scsi_task->exp_xfer_len = 0UL; scsi_task->sense_data_len = 0U; scsi_task->status = ISCSI_SCSI_STATUS_GOOD; } @@ -611,15 +611,12 @@ static void iscsi_scsi_task_xfer_complete(iscsi_scsi_task *scsi_task, iscsi_pdu iscsi_task *task = container_of( scsi_task, iscsi_task, scsi_task ); iscsi_connection *conn = task->conn; - task->des_data_xfer_pos += scsi_task->len; - iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) request_pdu->bhs_pkt; - const uint32_t xfer_len = 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, (scsi_cmd_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) != 0 ); - if ( (rc > 0) || (task->des_data_xfer_pos != scsi_task->xfer_len) ) + if ( rc > 0 ) return; } @@ -647,17 +644,18 @@ static void iscsi_scsi_task_xfer_complete(iscsi_scsi_task *scsi_task, iscsi_pdu 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 exp_xfer_len = scsi_task->exp_xfer_len; - const uint32_t pos = scsi_task->xfer_pos; + if ( (exp_xfer_len != 0UL) && (scsi_task->status == ISCSI_SCSI_STATUS_GOOD) ) { + const uint32_t resp_len = ds_len; - if ( (xfer_len != 0UL) && (scsi_task->status == ISCSI_SCSI_STATUS_GOOD) ) { - if ( pos < xfer_len ) { - const uint32_t res_cnt = (xfer_len - pos); + if ( resp_len < exp_xfer_len ) { + const uint32_t res_cnt = (exp_xfer_len - resp_len); 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); + } else if ( resp_len > exp_xfer_len ) { + const uint32_t res_cnt = (resp_len - exp_xfer_len); scsi_response_pkt->flags |= ISCSI_SCSI_RESPONSE_FLAGS_RES_OVERFLOW; iscsi_put_be32( (uint8_t *) &scsi_response_pkt->res_cnt, res_cnt ); @@ -771,9 +769,8 @@ static void iscsi_scsi_task_status_set(iscsi_scsi_task *scsi_task, const uint8_t */ static void iscsi_scsi_task_lun_process_none(iscsi_scsi_task *scsi_task) { - scsi_task->len = scsi_task->xfer_len; - 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; + 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 ); } /** @@ -862,7 +859,6 @@ static inline uint64_t iscsi_scsi_emu_block_get_count(const dnbd3_image_t *image * 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. @@ -947,7 +943,12 @@ static int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, dnbd3_imag int rc = 0; uint64_t offset_bytes; const uint64_t num_bytes = iscsi_scsi_emu_blocks_to_bytes( &offset_bytes, offset_blocks, num_blocks ); + + if ( offset_bytes + num_bytes > image->virtualFilesize ) + return -ERANGE; + scsi_task->file_offset = offset_bytes; + scsi_task->len = (uint32_t)num_bytes; dnbd3_cache_map_t *cache = ref_get_cachemap( image ); @@ -991,14 +992,12 @@ static int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, dnbd3_imag } /** - * @brief Executes a read or write operation on a DNBD3 image. + * @brief Executes a read 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 @@ -1006,37 +1005,11 @@ static int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, dnbd3_imag * @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) +static int iscsi_scsi_emu_block_read(dnbd3_image_t *image, iscsi_scsi_task *scsi_task, const uint64_t lba, const uint32_t xfer_len) { - scsi_task->xfer_pos = 0UL; - - if ( (flags & ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE) != 0 ) { - 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; - } - - 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 imgBlockCount = iscsi_scsi_emu_block_get_count( image ); - - if ( (imgBlockCount <= lba) || ((imgBlockCount - 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; @@ -1045,39 +1018,34 @@ static int iscsi_scsi_emu_block_read_write(dnbd3_image_t *image, iscsi_scsi_task const uint32_t max_xfer_len = ISCSI_MAX_DS_SIZE / ISCSI_SCSI_EMU_BLOCK_SIZE; - if ( xfer_len > max_xfer_len || xfer_len * ISCSI_SCSI_EMU_BLOCK_SIZE != scsi_task->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 ); + if ( xfer_len > max_xfer_len || !scsi_task->is_read || scsi_task->is_write ) { + 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; } - uint64_t offset_blocks; - uint64_t num_blocks; - - if ( iscsi_scsi_emu_bytes_to_blocks( &offset_blocks, &num_blocks, 0, scsi_task->len ) != 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 ); + int rc = iscsi_scsi_emu_io_blocks_read( scsi_task, image, lba, xfer_len ); + if ( rc == 0 ) return ISCSI_SCSI_TASK_RUN_COMPLETE; - } - offset_blocks += lba; + if ( rc == -ENOMEM ) { + iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_HARDWARE_ERR, + ISCSI_SCSI_ASC_INTERNAL_TARGET_FAIL, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE ); - int rc = iscsi_scsi_emu_io_blocks_read( scsi_task, image, offset_blocks, num_blocks ); - - if ( rc < 0 ) { - if ( rc == -ENOMEM ) { - iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_HARDWARE_ERR, - ISCSI_SCSI_ASC_INTERNAL_TARGET_FAIL, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE ); - - return ISCSI_SCSI_TASK_RUN_COMPLETE; - } + return ISCSI_SCSI_TASK_RUN_COMPLETE; + } - 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 ); + if ( rc == -ERANGE ) { + 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; } - scsi_task->xfer_pos = scsi_task->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; } @@ -1110,7 +1078,7 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) if ( xfer_len == 0UL ) xfer_len = 256UL; - return iscsi_scsi_emu_block_read_write( image, scsi_task, lba, xfer_len, 0 ); + return iscsi_scsi_emu_block_read( image, scsi_task, lba, xfer_len ); } 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; @@ -1118,7 +1086,7 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) 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( image, scsi_task, lba, xfer_len, 0 ); + return iscsi_scsi_emu_block_read( image, scsi_task, lba, xfer_len ); } 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; @@ -1126,7 +1094,7 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) 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( image, scsi_task, lba, xfer_len, 0 ); + return iscsi_scsi_emu_block_read( image, scsi_task, lba, xfer_len ); } 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; @@ -1134,7 +1102,7 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) 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( image, scsi_task, lba, xfer_len, 0 ); + return iscsi_scsi_emu_block_read( image, scsi_task, lba, xfer_len ); } case ISCSI_SCSI_OPCODE_READCAPACITY10 : { 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) ); @@ -1156,11 +1124,12 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) uint len = scsi_task->len; - if ( len > sizeof(struct iscsi_scsi_read_capacity_10_parameter_data_packet) ) + 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->len = len; scsi_task->status = ISCSI_SCSI_STATUS_GOOD; break; @@ -1175,7 +1144,8 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) iscsi_scsi_service_action_in_16_parameter_data_packet *buf = 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 ); + 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; } @@ -1197,15 +1167,27 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) uint len = cdb_servce_in_action_16->alloc_len; - if ( len > sizeof(struct iscsi_scsi_service_action_in_16_parameter_data_packet) ) + 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->len = len; scsi_task->status = ISCSI_SCSI_STATUS_GOOD; 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 : { + iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_WRITE_PROTECTED, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE ); + + break; + } default : { return ISCSI_SCSI_TASK_RUN_UNKNOWN; @@ -1305,15 +1287,14 @@ static size_t iscsi_scsi_emu_pad_scsi_name(uint8_t *buf, const uint8_t *name) * data with. * @param[in] len Length of inquiry result buffer * in bytes. - * @return 0 on successful operation, a negative + * @return length of data 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 ); + 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; } @@ -1322,7 +1303,8 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task 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 ); + 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; } @@ -1568,8 +1550,6 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task 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; @@ -1766,6 +1746,7 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc { uint page_len; int len = 0; + int tmplen; switch ( pc ) { case ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_CURRENT_VALUES : @@ -1774,7 +1755,8 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc 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 ); + 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; @@ -1916,8 +1898,14 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc 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 ); + tmplen = 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 ); + if ( tmplen == -1 ) + return -1; + len += tmplen; + tmplen = 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 ); + if ( tmplen == -1 ) + return -1; + len += tmplen; break; } @@ -1970,18 +1958,27 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc 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 ); + tmplen = 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 ); + if ( tmplen == -1 ) + return -1; + len += tmplen; } 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 ); + tmplen = 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 ); + if ( tmplen == -1 ) + return -1; + len += tmplen; } 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 ); + tmplen = 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 ); + if ( tmplen == -1 ) + return -1; + len += tmplen; } break; @@ -2086,6 +2083,39 @@ static int iscsi_scsi_emu_primary_mode_sense(dnbd3_image_t *image, iscsi_scsi_ta return alloc_len; } +/** + * @brief Determines the temporary allocation size for a SCSI reply. + * + * This function calculates the temporary allocation size to be used for SCSI + * commands based on the requested allocation size. It ensures the allocation + * size has a minimum size, to simplify buffer-filling. The response can then + * later be truncated if it's larger than the alloc_size. + * If the requested size exceeds the default maximum allowed size, a SCSI task + * status with an error condition is set, and the allocation size is returned + * as zero. + * + * @param[in] scsi_task Pointer to the SCSI task, used to set error status. + * @param[in] alloc_size The client-requested allocation size in bytes. + * + * @return The determined temporary allocation size. Returns 0 if the size + * exceeds the maximum allowed limit; otherwise, the size is either adjusted + * to the default size or remains the requested size. + */ +static uint32_t iscsi_get_temporary_allocation_size(iscsi_scsi_task *scsi_task, uint32_t alloc_size) +{ + if ( alloc_size > ISCSI_DEFAULT_RECV_DS_LEN ) { + // Don't allocate gigabytes of memory just because the client says so + 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 0; + } + if ( alloc_size < ISCSI_DEFAULT_RECV_DS_LEN ) + return ISCSI_DEFAULT_RECV_DS_LEN; + + return alloc_size; +} + /** * @brief Executes SCSI non-block emulation on a DNBD3 image. * @@ -2102,109 +2132,73 @@ static int iscsi_scsi_emu_primary_mode_sense(dnbd3_image_t *image, iscsi_scsi_ta */ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) { - 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; + const uint alloc_len = iscsi_get_be16(cdb_inquiry->alloc_len); - 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; + len = iscsi_get_temporary_allocation_size( scsi_task, alloc_len ); + if ( len == 0 ) + break; - if ( len > 0U ) { - std_inquiry_data_pkt = (iscsi_scsi_std_inquiry_data_packet *) malloc( len ); + iscsi_scsi_std_inquiry_data_packet *std_inquiry_data_pkt = 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 ); + 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; - } + break; } rc = iscsi_scsi_emu_primary_inquiry( scsi_task->connection->client->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->buf = (uint8_t *) std_inquiry_data_pkt; + scsi_task->len = MIN( rc, alloc_len ); + scsi_task->status = ISCSI_SCSI_STATUS_GOOD; } else { free( std_inquiry_data_pkt ); } - 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; + const uint alloc_len = iscsi_get_be32(cdb_report_luns->alloc_len); - 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 ) + len = iscsi_get_temporary_allocation_size( scsi_task, alloc_len ); + if ( len == 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; + iscsi_scsi_report_luns_parameter_data_lun_list_packet *report_luns_parameter_data_pkt = malloc( len ); - 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 ); - 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; - } + break; } rc = iscsi_scsi_emu_primary_report_luns( report_luns_parameter_data_pkt, len, cdb_report_luns->select_report ); - if ( rc < 0 ) { + if ( rc >= 0 ) { + scsi_task->buf = (uint8_t *) report_luns_parameter_data_pkt; + scsi_task->len = MIN( rc, alloc_len ); + scsi_task->status = ISCSI_SCSI_STATUS_GOOD; + } else { 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; + 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 ); } - scsi_task->xfer_pos = 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; - - alloc_len = cdb_mode_sense_6->alloc_len; + const uint 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); @@ -2228,32 +2222,21 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) rc = iscsi_scsi_emu_primary_mode_sense( scsi_task->connection->client->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; + scsi_task->buf = (uint8_t *) mode_sense_6_parameter_hdr_data_pkt; + scsi_task->len = MIN( rc, alloc_len ); + scsi_task->status = ISCSI_SCSI_STATUS_GOOD; + } else { + free( mode_sense_6_parameter_hdr_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; } 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; - - alloc_len = iscsi_get_be16(cdb_mode_sense_10->alloc_len); + const uint 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); @@ -2278,36 +2261,24 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) rc = iscsi_scsi_emu_primary_mode_sense( scsi_task->connection->client->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; + scsi_task->buf = (uint8_t *) mode_sense_10_parameter_hdr_data_pkt; + scsi_task->len = MIN( rc, alloc_len ); + scsi_task->status = ISCSI_SCSI_STATUS_GOOD; + } else { + free( mode_sense_10_parameter_hdr_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; } case ISCSI_SCSI_OPCODE_TESTUNITREADY : { - scsi_task->xfer_pos = 0UL; scsi_task->status = ISCSI_SCSI_STATUS_GOOD; break; } case ISCSI_SCSI_OPCODE_STARTSTOPUNIT : { - scsi_task->xfer_pos = 0UL; scsi_task->status = ISCSI_SCSI_STATUS_GOOD; break; @@ -3008,8 +2979,6 @@ static inline int iscsi_seq_num_cmp_gt(const uint32_t seq_num, const uint32_t se */ static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu, const int reason_code) { - pdu->flags |= ISCSI_PDU_FLAGS_REJECTED; - const uint32_t ds_len = (uint32_t) sizeof(struct iscsi_bhs_packet) + (uint32_t) (pdu->bhs_pkt->total_ahs_len * ISCSI_ALIGN_SIZE); iscsi_pdu CLEANUP_PDU response_pdu; if ( !iscsi_connection_pdu_init( &response_pdu, ds_len, false ) ) @@ -3266,10 +3235,9 @@ static int iscsi_connection_handle_scsi_cmd(iscsi_connection *conn, iscsi_pdu *r uint32_t exp_xfer_len = iscsi_get_be32(scsi_cmd_pkt->exp_xfer_len); - task->scsi_task.len = (uint) (((uint8_t *) request_pdu->ds_cmd_data) - ((uint8_t *) request_pdu->bhs_pkt)); // Length of BHS + AHS - task->scsi_task.cdb = &scsi_cmd_pkt->scsi_cdb; - task->scsi_task.xfer_len = exp_xfer_len; - task->init_task_tag = iscsi_get_be32(scsi_cmd_pkt->init_task_tag); + task->scsi_task.cdb = &scsi_cmd_pkt->scsi_cdb; + task->scsi_task.exp_xfer_len = exp_xfer_len; + task->init_task_tag = iscsi_get_be32(scsi_cmd_pkt->init_task_tag); const uint64_t lun = iscsi_get_be64(scsi_cmd_pkt->lun); task->lun_id = iscsi_scsi_lun_get_from_iscsi( lun ); @@ -3277,13 +3245,15 @@ static int iscsi_connection_handle_scsi_cmd(iscsi_connection *conn, iscsi_pdu *r if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_READ) == 0 ) { if ( exp_xfer_len != 0UL ) { // Not a read request, but expecting data - not valid + iscsi_scsi_task_status_set( &task->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 ); iscsi_task_destroy( task ); return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_INVALID_PDU_FIELD ); } } else { - task->scsi_task.flags |= ISCSI_SCSI_TASK_FLAGS_XFER_READ; + task->scsi_task.is_read = true; } + task->scsi_task.is_write = (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_WRITE) != 0; int rc; @@ -3292,11 +3262,6 @@ static int iscsi_connection_handle_scsi_cmd(iscsi_connection *conn, iscsi_pdu *r iscsi_scsi_task_lun_process_none( &task->scsi_task ); rc = ISCSI_CONNECT_PDU_READ_OK; } else { - if ( (task->scsi_task.flags & ISCSI_SCSI_TASK_FLAGS_XFER_READ) != 0 ) { - task->scsi_task.buf = NULL; - task->scsi_task.len = task->scsi_task.xfer_len; - } - task->scsi_task.status = ISCSI_SCSI_STATUS_GOOD; rc = iscsi_scsi_emu_block_process( &task->scsi_task ); @@ -3306,7 +3271,6 @@ static int iscsi_connection_handle_scsi_cmd(iscsi_connection *conn, iscsi_pdu *r if ( rc == ISCSI_SCSI_TASK_RUN_UNKNOWN ) { iscsi_scsi_task_status_set( &task->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 ); - // TODO: Free task rc = ISCSI_SCSI_TASK_RUN_COMPLETE; } } @@ -3457,16 +3421,6 @@ static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; } iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - int payload_len = iscsi_write_login_options_to_pdu( conn, pairs, login_response_pdu ); - - if ( payload_len < 0 || (uint32_t)payload_len > login_response_pdu->ds_len ) { - logadd( LOG_DEBUG1, "iscsi_connecction_handle_login_response: Invalid payload length %d, ds_len: %u, write_pos: %u", - payload_len, login_response_pdu->ds_len, login_response_pdu->ds_write_pos ); - 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_LOGIN_RESPONSE; - } // Handle current stage (CSG bits) switch ( ISCSI_LOGIN_RESPONSE_FLAGS_GET_CURRENT_STAGE(login_response_pkt->flags) ) { @@ -3504,7 +3458,18 @@ static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi iscsi_put_be16( (uint8_t *) &login_response_pkt->tsih, 42 ); conn->state = ISCSI_CONNECT_STATE_NORMAL_SESSION; + iscsi_connection_update_key_value_pairs( conn, pairs ); + int payload_len = iscsi_write_login_options_to_pdu( conn, pairs, login_response_pdu ); + + if ( payload_len < 0 || (uint32_t)payload_len > login_response_pdu->ds_len ) { + logadd( LOG_DEBUG1, "iscsi_connecction_handle_login_response: Invalid payload length %d, ds_len: %u, write_pos: %u", + payload_len, login_response_pdu->ds_len, login_response_pdu->ds_write_pos ); + 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_LOGIN_RESPONSE; + } break; } diff --git a/src/server/iscsi.h b/src/server/iscsi.h index f4fa6b1..d8cd43b 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -314,22 +314,12 @@ static inline bool iscsi_is_pow2(const uint32_t value) /// iSCSI Default receive DataSegment (DS) size in bytes. -#define ISCSI_DEFAULT_RECV_DS_LEN 131072UL +#define ISCSI_DEFAULT_RECV_DS_LEN 16384UL /// iSCSI default maximum DataSegment receive length in bytes. #define ISCSI_DEFAULT_MAX_RECV_DS_LEN 524288UL -/// 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 - - /// Current minimum iSCSI protocol version supported by this implementation. #define ISCSI_VERSION_MIN 0 @@ -2325,18 +2315,6 @@ typedef struct __attribute__((packed)) iscsi_scsi_report_luns_parameter_data_lun } 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 MODE SELECT(6) parameter list packet data. * @@ -5326,9 +5304,6 @@ typedef struct __attribute__((packed)) iscsi_nop_out_packet { /// Reserved for future usage, always MUST be 0. uint64_t reserved2[2]; - /// Optional header digest. - uint32_t hdr_digest; - /** * @brief DataSegment - Ping Data (optional). * @@ -5338,9 +5313,6 @@ typedef struct __attribute__((packed)) iscsi_nop_out_packet { * for the DataSegmentLength and indicates the absence of ping data. */ iscsi_scsi_ds_cmd_data ds_ping_data; - - /// Optional data digest. - uint32_t data_digest; } iscsi_nop_out_packet; @@ -5425,75 +5397,11 @@ typedef struct __attribute__((packed)) iscsi_nop_in_packet { /// Reserved for future usage, always MUST be 0. uint64_t reserved3; - /// Optional header digest. - uint32_t hdr_digest; - /// DataSegment - Return Ping Data. iscsi_scsi_ds_cmd_data ds_ping_data; - - /// Optional data digest. - uint32_t data_digest; } iscsi_nop_in_packet; -/// 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 - -/// 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; - - /// Reserved for future usage (always MUST be 0). - uint8_t reserved; - - /// Additional length of name. - uint16_t add_len; - - /// Name. - uint8_t name[0]; -} iscsi_transport_id; - - /// Maximum length of a key according to iSCSI specifications. #define ISCSI_TEXT_KEY_MAX_LEN 63U @@ -5503,65 +5411,6 @@ typedef struct __attribute__((packed)) iscsi_transport_id { /// Maximum length of value for a normal key. #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 -1 - -/// iSCSI text key=value pair type: Unspecified type. -#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_UNSPECIFIED 0 - -/// iSCSI text key=value pair type: List. -#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 2 - -/// iSCSI text key=value pair type: Numerical maximum. -#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 4 - -/// iSCSI text key=value pair type: Declarative. -#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 6 - -/// iSCSI text key=value pair type: Boolean AND. -#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 << 0) - -/// iSCSI key value pair flags: Multi negotiation. -#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 << 2) - -/// iSCSI key value pair flags: CHAP type. -#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 << 4) - -/// iSCSI key value pair flags: Use previous default value. -#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 << 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 << 7) - /** * @brief iSCSI connection and session lookup table entry, used for allowed key values and determining key type. @@ -5636,18 +5485,6 @@ typedef struct iscsi_key_value_pair_packet { int discovery; } iscsi_key_value_pair_packet; -/// iSCSI main global data flags: Allow duplicate ISIDs. -#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 << 1) - -/// iSCSI main global data flags: CHAP authentication is required. -#define ISCSI_GLOBALS_FLAGS_CHAP_REQUIRE (1 << 2) - -/// iSCSI main global data flags: CHAP authentication is mutual. -#define ISCSI_GLOBALS_FLAGS_CHAP_MUTUAL (1 << 3) - /// 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; @@ -5826,28 +5663,6 @@ typedef struct iscsi_key_value_pair_packet { -/// 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) - /// iSCSI SCSI task run: Unknown. #define ISCSI_SCSI_TASK_RUN_UNKNOWN -1 @@ -5855,19 +5670,10 @@ typedef struct iscsi_key_value_pair_packet { /// 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; -/// 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. @@ -5897,17 +5703,17 @@ typedef struct iscsi_scsi_task { /// Length of buffer in bytes. uint32_t len; + /// Expected data transfer length (from iSCSI PDU field) + uint32_t exp_xfer_len; + /// Unique identifier for this task. uint64_t id; - /// Flags. - int flags; - - /// Transfer position in bytes. - uint32_t xfer_pos; + /// Whether the R bit was set in the iSCSI request (BHS). + bool is_read; - /// Transfer length in bytes. - uint32_t xfer_len; + /// Whether the W bit was set in the iSCSI request (BHS). + bool is_write; /// Sense data length. uint8_t sense_data_len; @@ -6172,9 +5978,6 @@ typedef struct iscsi_connection { typedef struct iscsi_task iscsi_task; -/// iSCSI PDU flags: Rejected. -#define ISCSI_PDU_FLAGS_REJECTED (1 << 0) - /// iSCSI PDU will contain a small buffer for sending/receiving trivial PDUs with no/very small DS, and small AH #define ISCSI_INTERNAL_BUFFER_SIZE (2 * ISCSI_BHS_SIZE) @@ -6221,13 +6024,6 @@ typedef struct iscsi_pdu { } iscsi_pdu; -/// iSCSI task flags: Ready To Transfer is active. -#define ISCSI_TASK_FLAGS_R2T_ACTIVE (1 << 0) - -/// iSCSI task flags: Task is enqueued in SCSI layer. -#define ISCSI_TASK_FLAGS_QUEUED (1 << 1) - - /** * @brief This structure is used for iSCSI task management. * @@ -6250,9 +6046,6 @@ typedef struct iscsi_task { /// 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; @@ -6262,12 +6055,6 @@ typedef struct iscsi_task { /// 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; } iscsi_task; -- cgit v1.2.3-55-g7522 From d0dcdec3dba13108f61e4926ea0a5a9802fa45c0 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Fri, 31 Oct 2025 13:59:26 +0100 Subject: [SERVER] iscsi: Rename constant to reflect it means LOGICAL block size ... The other one is already named PHYSICAL --- src/server/iscsi.c | 41 +++++++++++++++++++++-------------------- src/server/iscsi.h | 2 +- 2 files changed, 22 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index d155fc4..95ce1ec 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -842,7 +842,7 @@ static int iscsi_scsi_lun_get_from_iscsi(const uint64_t lun) */ static inline uint64_t iscsi_scsi_emu_block_get_count(const dnbd3_image_t *image) { - return (image->virtualFilesize / ISCSI_SCSI_EMU_BLOCK_SIZE); + return (image->virtualFilesize / ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE); } /** @@ -865,10 +865,10 @@ static inline uint64_t iscsi_scsi_emu_block_get_count(const dnbd3_image_t *image */ 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) { - *offset_blocks = (offset_bytes / ISCSI_SCSI_EMU_BLOCK_SIZE); - *num_blocks = (num_bytes / ISCSI_SCSI_EMU_BLOCK_SIZE); + *offset_blocks = (offset_bytes / ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE); + *num_blocks = (num_bytes / ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE); - return ((offset_bytes % ISCSI_SCSI_EMU_BLOCK_SIZE) | (num_bytes % ISCSI_SCSI_EMU_BLOCK_SIZE)); + return ((offset_bytes % ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE) | (num_bytes % ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE)); } /** @@ -886,9 +886,9 @@ static uint64_t iscsi_scsi_emu_bytes_to_blocks(uint64_t *offset_blocks, uint64_t */ static uint64_t iscsi_scsi_emu_blocks_to_bytes(uint64_t *offset_bytes, const uint64_t offset_blocks, const uint64_t num_blocks) { - *offset_bytes = (offset_blocks * ISCSI_SCSI_EMU_BLOCK_SIZE); + *offset_bytes = (offset_blocks * ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE); - return (num_blocks * ISCSI_SCSI_EMU_BLOCK_SIZE); + return (num_blocks * ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE); } /** @@ -1016,7 +1016,7 @@ static int iscsi_scsi_emu_block_read(dnbd3_image_t *image, iscsi_scsi_task *scsi return ISCSI_SCSI_TASK_RUN_COMPLETE; } - const uint32_t max_xfer_len = ISCSI_MAX_DS_SIZE / ISCSI_SCSI_EMU_BLOCK_SIZE; + const uint32_t max_xfer_len = ISCSI_MAX_DS_SIZE / ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE; if ( xfer_len > max_xfer_len || !scsi_task->is_read || scsi_task->is_write ) { iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, @@ -1120,16 +1120,10 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) else iscsi_put_be32( (uint8_t *) &buf->lba, (uint32_t) lba ); - iscsi_put_be32( (uint8_t *) &buf->block_len, ISCSI_SCSI_EMU_BLOCK_SIZE ); - - 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 - } + iscsi_put_be32( (uint8_t *) &buf->block_len, ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE ); scsi_task->buf = (uint8_t *) buf; - scsi_task->len = len; + scsi_task->len = sizeof(*buf); scsi_task->status = ISCSI_SCSI_STATUS_GOOD; break; @@ -1150,10 +1144,10 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) return ISCSI_SCSI_TASK_RUN_COMPLETE; } - lba = iscsi_scsi_emu_block_get_count( image ) - 1ULL; + lba = iscsi_scsi_emu_block_get_count( image ) - 1ULL; iscsi_put_be64( (uint8_t *) &buf->lba, lba ); - iscsi_put_be32( (uint8_t *) &buf->block_len, ISCSI_SCSI_EMU_BLOCK_SIZE ); + iscsi_put_be32( (uint8_t *) &buf->block_len, ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE ); buf->flags = 0; @@ -1310,6 +1304,7 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task } if ( evpd != 0 ) { + // VPD requested iscsi_scsi_vpd_page_inquiry_data_packet *vpd_page_inquiry_data_pkt = (iscsi_scsi_vpd_page_inquiry_data_packet *) std_inquiry_data_pkt; uint alloc_len; const uint8_t pti = ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PUT_PERIPHERAL_TYPE(ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_DIRECT) | ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PUT_PERIPHERAL_ID(ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_ID_POSSIBLE); @@ -1497,11 +1492,15 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task vpd_page_block_limits_inquiry_data_pkt->flags = 0; - uint32_t blocks = (ISCSI_MAX_DS_SIZE / ISCSI_SCSI_EMU_BLOCK_SIZE); + // Calculate maximum number of logical blocks that would fit into a maximum-size transfer (16MiB), + // but make sure it is a multiple of the physical block size + const uint32_t blocks = ((ISCSI_MAX_DS_SIZE / ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE) + * ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE) / ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE; vpd_page_block_limits_inquiry_data_pkt->max_cmp_write_len = (uint8_t) blocks; - iscsi_put_be16( (uint8_t *) &vpd_page_block_limits_inquiry_data_pkt->optimal_granularity_xfer_len, (uint16_t) blocks ); + iscsi_put_be16( (uint8_t *) &vpd_page_block_limits_inquiry_data_pkt->optimal_granularity_xfer_len, + (uint16_t) ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE / ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE ); 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; @@ -1561,6 +1560,8 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task return (int) (alloc_len + sizeof(struct iscsi_scsi_vpd_page_inquiry_data_packet)); } + // Normal INQUIRY, no VPD + const uint8_t pti = ISCSI_SCSI_BASIC_INQUIRY_DATA_PUT_PERIPHERAL_TYPE(ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_DIRECT) | 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; @@ -2060,7 +2061,7 @@ static int iscsi_scsi_emu_primary_mode_sense(dnbd3_image_t *image, iscsi_scsi_ta } const uint64_t num_blocks = iscsi_scsi_emu_block_get_count( image ); - const uint32_t block_size = ISCSI_SCSI_EMU_BLOCK_SIZE; + const uint32_t block_size = ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE; 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); diff --git a/src/server/iscsi.h b/src/server/iscsi.h index d8cd43b..1a98fff 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -5733,7 +5733,7 @@ typedef struct iscsi_scsi_task { #define ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE DNBD3_BLOCK_SIZE /// iSCSI SCSI emulation logical block size in bytes. -#define ISCSI_SCSI_EMU_BLOCK_SIZE (512) +#define ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE (512) /// Block shift difference between dnbd3 (4k) and iSCSI (512b) #define ISCSI_SCSI_EMU_BLOCK_DIFF_SHIFT (3) -- cgit v1.2.3-55-g7522 From 884735d4af0d96c4011eb8b63ffa0e72a81e9a85 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Fri, 31 Oct 2025 14:29:02 +0100 Subject: [SERVER] iscsi: Remove unused functions & casts, turn [0] into [] In functions that can handle multiple different structs, instead of picking an arbitrary one as the pointer type in the function signature, pass an uint8_t and cast to the according struct in the sub-cases in the method body. --- src/server/iscsi.c | 207 ++++++++++++++++++----------------------------------- src/server/iscsi.h | 203 +++++++++------------------------------------------ 2 files changed, 104 insertions(+), 306 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 95ce1ec..b0b171e 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -45,8 +45,6 @@ #include "uplink.h" #include "reference.h" -#include - #define ISCSI_DEFAULT_LUN 0 #define ISCSI_DEFAULT_PROTOCOL_ID 1 #define ISCSI_DEFAULT_DEVICE_ID 1 @@ -79,7 +77,7 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task); static void iscsi_scsi_task_init(iscsi_scsi_task *scsi_task); // Initializes a SCSI task -static void iscsi_scsi_task_xfer_complete(iscsi_scsi_task *scsi_task, iscsi_pdu *request_pdu); // Callback function when an iSCSI SCSI task completed the data transfer +static void iscsi_scsi_task_xfer_complete(iscsi_connection *conn, iscsi_scsi_task *scsi_task, iscsi_pdu *request_pdu); // Callback function when an iSCSI SCSI task completed the data transfer static void iscsi_scsi_task_lun_process_none(iscsi_scsi_task *scsi_task); // Processes a iSCSI SCSI task with no LUN identifier @@ -356,14 +354,10 @@ static iscsi_task *iscsi_task_create(iscsi_connection *conn) return NULL; } - task->conn = conn; - task->pos = 0UL; task->len = 0UL; - task->id = 0ULL; task->lun_id = 0; task->init_task_tag = 0UL; task->target_xfer_tag = 0UL; - task->data_sn = 0UL; iscsi_scsi_task_init( &task->scsi_task ); task->scsi_task.connection = conn; @@ -528,7 +522,7 @@ static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, iscsi_task *task if ( xfer_len == 0UL ) return 0; - uint32_t data_sn = task->data_sn; + uint32_t data_sn = 0; uint32_t max_burst_offset = 0UL; // Max burst length = total length of payload in all PDUs const uint32_t max_burst_len = conn->session->opts.MaxBurstLength; @@ -569,8 +563,6 @@ static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, iscsi_task *task max_burst_offset += max_burst_len; } - task->data_sn = data_sn; - return (status & ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS); } @@ -606,10 +598,9 @@ static void iscsi_scsi_task_init(iscsi_scsi_task *scsi_task) * so be careful. * @param request_pdu */ -static void iscsi_scsi_task_xfer_complete(iscsi_scsi_task *scsi_task, iscsi_pdu *request_pdu) +static void iscsi_scsi_task_xfer_complete(iscsi_connection *conn, iscsi_scsi_task *scsi_task, iscsi_pdu *request_pdu) { iscsi_task *task = container_of( scsi_task, iscsi_task, scsi_task ); - iscsi_connection *conn = task->conn; iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) request_pdu->bhs_pkt; @@ -845,31 +836,6 @@ static inline uint64_t iscsi_scsi_emu_block_get_count(const dnbd3_image_t *image return (image->virtualFilesize / ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE); } -/** - * @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. - * @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) -{ - *offset_blocks = (offset_bytes / ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE); - *num_blocks = (num_bytes / ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE); - - return ((offset_bytes % ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE) | (num_bytes % ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE)); -} /** * @brief Converts offset and length specified by a block size to offset and length in bytes. @@ -934,7 +900,6 @@ static void iscsi_uplink_callback(void *data, uint64_t handle UNUSED, uint64_t s * 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. * @return 0 on successful operation, a negative * error code otherwise. */ @@ -1105,7 +1070,7 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) return iscsi_scsi_emu_block_read( image, scsi_task, lba, xfer_len ); } case ISCSI_SCSI_OPCODE_READCAPACITY10 : { - 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) ); + iscsi_scsi_read_capacity_10_parameter_data_packet *buf = 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 ); @@ -1192,32 +1157,6 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) return ISCSI_SCSI_TASK_RUN_COMPLETE; } -/** - * @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. * @@ -1691,32 +1630,33 @@ static int iscsi_scsi_emu_primary_report_luns( iscsi_scsi_report_luns_parameter_ * is initialized, the sub page code * will also be set. * - * @param[in] mode_sense_mode_page_pkt Pointer to mode sense parameter + * @param[in] buffer 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] len Length in bytes to initialize. Any padding will be zeroed. * @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) +static void iscsi_scsi_emu_primary_mode_sense_page_init(uint8_t *buffer, const uint len, const uint page, const uint sub_page) { - if ( mode_sense_mode_page_pkt == NULL ) + if ( buffer == NULL ) return; if ( sub_page == 0U ) { + iscsi_scsi_mode_sense_mode_page_data_header *mode_sense_mode_page_pkt = (iscsi_scsi_mode_sense_mode_page_data_header *) buffer; 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)); + mode_sense_mode_page_pkt->page_len = (uint8_t) (len - sizeof(*mode_sense_mode_page_pkt)); - memset( mode_sense_mode_page_pkt->params, 0, (len - offsetof(struct iscsi_scsi_mode_sense_mode_page_data_packet, params)) ); + memset( mode_sense_mode_page_pkt + 1, 0, (len - sizeof(*mode_sense_mode_page_pkt)) ); } 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; + iscsi_scsi_mode_sense_mode_sub_page_data_header *mode_sense_mode_sub_page_pkt = (iscsi_scsi_mode_sense_mode_sub_page_data_header *) buffer; 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)) ); + iscsi_put_be16( (uint8_t *) &mode_sense_mode_sub_page_pkt->page_len, (uint16_t) (len - sizeof(*mode_sense_mode_sub_page_pkt)) ); - memset( mode_sense_mode_sub_page_pkt->params, 0, (len - offsetof(struct iscsi_scsi_mode_sense_mode_sub_page_data_packet, params)) ); + memset( mode_sense_mode_sub_page_pkt + 1, 0, (len - sizeof(*mode_sense_mode_sub_page_pkt)) ); } } @@ -1733,7 +1673,7 @@ static void iscsi_scsi_emu_primary_mode_sense_page_init(iscsi_scsi_mode_sense_mo * 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 + * @param[in] buffer 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. @@ -1743,10 +1683,10 @@ static void iscsi_scsi_emu_primary_mode_sense_page_init(iscsi_scsi_mode_sense_mo * @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) +static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_scsi_task *scsi_task, uint8_t *buffer, const uint pc, const uint page, const uint sub_page) { uint page_len; - int len = 0; + uint len = 0; int tmplen; switch ( pc ) { @@ -1827,9 +1767,9 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc if ( sub_page != 0U ) break; - page_len = sizeof(struct iscsi_scsi_mode_sense_read_write_err_recovery_mode_page_data_packet); + page_len = sizeof(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 ); + iscsi_scsi_emu_primary_mode_sense_page_init( buffer, page_len, page, sub_page ); len += page_len; @@ -1839,9 +1779,9 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc if ( sub_page != 0U ) break; - page_len = sizeof(struct iscsi_scsi_mode_sense_disconnect_reconnect_mode_page_data_packet); + page_len = sizeof(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 ); + iscsi_scsi_emu_primary_mode_sense_page_init( buffer, page_len, page, sub_page ); len += page_len; @@ -1851,9 +1791,9 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc if ( sub_page != 0U ) break; - page_len = sizeof(struct iscsi_scsi_mode_sense_verify_err_recovery_mode_page_data_packet); + page_len = sizeof(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 ); + iscsi_scsi_emu_primary_mode_sense_page_init( buffer, page_len, page, sub_page ); len += page_len; @@ -1863,13 +1803,13 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc 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; + iscsi_scsi_mode_sense_caching_mode_page_data_packet *mode_sense_caching_mode_page_pkt = (iscsi_scsi_mode_sense_caching_mode_page_data_packet *) buffer; - page_len = sizeof(struct iscsi_scsi_mode_sense_caching_mode_page_data_packet); + page_len = sizeof(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 ); + iscsi_scsi_emu_primary_mode_sense_page_init( buffer, page_len, page, sub_page ); - if ( (mode_sense_mode_page_pkt != NULL) && (pc != ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_CHG_VALUES) ) + if ( (buffer != 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; @@ -1879,9 +1819,9 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc 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); + page_len = sizeof(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 ); + iscsi_scsi_emu_primary_mode_sense_page_init( buffer, page_len, page, sub_page ); len += page_len; @@ -1892,18 +1832,18 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc 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 ); + iscsi_scsi_emu_primary_mode_sense_page_init( buffer, page_len, page, sub_page ); len += page_len; break; } case ISCSI_SCSI_MODE_SENSE_MODE_SUB_PAGE_CODE_CONTROL_ALL : { - tmplen = 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 ); + tmplen = iscsi_scsi_emu_primary_mode_sense_page( image, scsi_task, ((buffer != NULL) ? (buffer + len) : NULL), pc, page, ISCSI_SCSI_MODE_SENSE_MODE_SUB_PAGE_CODE_CONTROL ); if ( tmplen == -1 ) return -1; len += tmplen; - tmplen = 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 ); + tmplen = iscsi_scsi_emu_primary_mode_sense_page( image, scsi_task, ((buffer != NULL) ? (buffer + len) : NULL), pc, page, ISCSI_SCSI_MODE_SENSE_MODE_SUB_PAGE_CODE_CONTROL_EXT ); if ( tmplen == -1 ) return -1; len += tmplen; @@ -1923,7 +1863,7 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc 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 ); + iscsi_scsi_emu_primary_mode_sense_page_init( buffer, page_len, page, sub_page ); len += page_len; @@ -1935,7 +1875,7 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc 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 ); + iscsi_scsi_emu_primary_mode_sense_page_init( buffer, page_len, page, sub_page ); len += page_len; @@ -1947,19 +1887,17 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc 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 ); + iscsi_scsi_emu_primary_mode_sense_page_init( buffer, 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++ ) { - tmplen = 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 ( uint i = ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC; i < ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_REPORT_ALL_MODE_PAGES; i++ ) { + tmplen = iscsi_scsi_emu_primary_mode_sense_page( image, scsi_task, ((buffer != NULL) ? (buffer + len) : NULL), pc, i, ISCSI_SCSI_MODE_SENSE_MODE_SUB_PAGE_CODE_REPORT_ALL_MODE_PAGES ); if ( tmplen == -1 ) return -1; len += tmplen; @@ -1968,15 +1906,15 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc 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++ ) { - tmplen = 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 ( uint i = ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC; i < ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_REPORT_ALL_MODE_PAGES; i++ ) { + tmplen = iscsi_scsi_emu_primary_mode_sense_page( image, scsi_task, ((buffer != NULL) ? (buffer + len) : NULL), pc, i, ISCSI_SCSI_MODE_SENSE_MODE_SUB_PAGE_CODE_REPORT_ALL_MODE_PAGES ); if ( tmplen == -1 ) return -1; len += tmplen; } - for ( i = ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC; i < ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_REPORT_ALL_MODE_PAGES; i++ ) { - tmplen = 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 ); + for ( uint i = ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC; i < ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_REPORT_ALL_MODE_PAGES; i++ ) { + tmplen = iscsi_scsi_emu_primary_mode_sense_page( image, scsi_task, ((buffer != NULL) ? (buffer + len) : NULL), pc, i, ISCSI_SCSI_MODE_SENSE_MODE_SUB_PAGE_CODE_REPORT_ALL_MODE_SUB_PAGES ); if ( tmplen == -1 ) return -1; len += tmplen; @@ -1996,7 +1934,7 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc } } - return len; + return (int)len; } /** @@ -2012,7 +1950,7 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc * 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 + * @param[in] buffer Pointer to mode sense parameter * header data packet to fill the * mode sense data with. If this is * NULL, only the length of sense @@ -2028,26 +1966,29 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc * 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) +static int iscsi_scsi_emu_primary_mode_sense(dnbd3_image_t *image, iscsi_scsi_task *scsi_task, uint8_t *buffer, + 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 ); + // Pointer to right after header and LBA block description; where the pages go + uint8_t *mode_sense_payload = (buffer != NULL) ? (buffer + hdr_len + block_desc_len) : NULL; + const int page_len = iscsi_scsi_emu_primary_mode_sense_page( image, scsi_task, mode_sense_payload, 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 ( buffer == NULL ) + return (int)alloc_len; - if ( hdr_len == sizeof(struct iscsi_scsi_mode_sense_6_parameter_header_data_packet) ) { + if ( hdr_len == sizeof(iscsi_scsi_mode_sense_6_parameter_header_data_packet) ) { + 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 *) buffer; 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 = ISCSI_SCSI_MODE_SENSE_6_PARAM_HDR_DATA_FLAGS_WP; mode_sense_6_parameter_hdr_data_pkt->block_desc_len = (uint8_t) block_desc_len; - } else if ( hdr_len == sizeof(struct iscsi_scsi_mode_sense_10_parameter_header_data_packet) ) { - 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; + } else if ( hdr_len == sizeof(iscsi_scsi_mode_sense_10_parameter_header_data_packet) ) { + 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 *) buffer; 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; @@ -2063,8 +2004,8 @@ static int iscsi_scsi_emu_primary_mode_sense(dnbd3_image_t *image, iscsi_scsi_ta const uint64_t num_blocks = iscsi_scsi_emu_block_get_count( image ); const uint32_t block_size = ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE; - 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 ( block_desc_len == sizeof(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 *) (buffer + hdr_len); if ( num_blocks > 0xFFFFFFFFULL ) lba_parameter_block_desc->num_blocks = 0xFFFFFFFFUL; // Minus one does not require endianess conversion @@ -2073,15 +2014,15 @@ static int iscsi_scsi_emu_primary_mode_sense(dnbd3_image_t *image, iscsi_scsi_ta 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); + } else if ( block_desc_len == sizeof(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 *) (buffer + 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; + return (int)alloc_len; } /** @@ -2158,7 +2099,7 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) if ( rc >= 0 ) { scsi_task->buf = (uint8_t *) std_inquiry_data_pkt; - scsi_task->len = MIN( rc, alloc_len ); + scsi_task->len = MIN( (uint)rc, alloc_len ); scsi_task->status = ISCSI_SCSI_STATUS_GOOD; } else { free( std_inquiry_data_pkt ); @@ -2187,7 +2128,7 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) if ( rc >= 0 ) { scsi_task->buf = (uint8_t *) report_luns_parameter_data_pkt; - scsi_task->len = MIN( rc, alloc_len ); + scsi_task->len = MIN( (uint)rc, alloc_len ); scsi_task->status = ISCSI_SCSI_STATUS_GOOD; } else { free( report_luns_parameter_data_pkt ); @@ -2213,7 +2154,7 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) 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 ); + uint8_t *mode_sense_6_parameter_hdr_data_pkt = 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 ); @@ -2224,8 +2165,8 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) rc = iscsi_scsi_emu_primary_mode_sense( scsi_task->connection->client->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 ) { - scsi_task->buf = (uint8_t *) mode_sense_6_parameter_hdr_data_pkt; - scsi_task->len = MIN( rc, alloc_len ); + scsi_task->buf = mode_sense_6_parameter_hdr_data_pkt; + scsi_task->len = MIN( (uint)rc, alloc_len ); scsi_task->status = ISCSI_SCSI_STATUS_GOOD; } else { free( mode_sense_6_parameter_hdr_data_pkt ); @@ -2245,14 +2186,14 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) 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( scsi_task->connection->client->image, scsi_task, NULL, sizeof(struct iscsi_scsi_mode_sense_10_parameter_header_data_packet), block_desc_len, long_lba, pc10, page10, sub_page10 ); + rc = iscsi_scsi_emu_primary_mode_sense( scsi_task->connection->client->image, scsi_task, NULL, sizeof(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 ); + uint8_t *mode_sense_10_parameter_hdr_data_pkt = 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 ); @@ -2260,11 +2201,11 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) break; } - rc = iscsi_scsi_emu_primary_mode_sense( scsi_task->connection->client->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 ); + rc = iscsi_scsi_emu_primary_mode_sense( scsi_task->connection->client->image, scsi_task, 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 ) { - scsi_task->buf = (uint8_t *) mode_sense_10_parameter_hdr_data_pkt; - scsi_task->len = MIN( rc, alloc_len ); + scsi_task->buf = mode_sense_10_parameter_hdr_data_pkt; + scsi_task->len = MIN( (uint)rc, alloc_len ); scsi_task->status = ISCSI_SCSI_STATUS_GOOD; } else { free( mode_sense_10_parameter_hdr_data_pkt ); @@ -2274,11 +2215,7 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) break; } - case ISCSI_SCSI_OPCODE_TESTUNITREADY : { - scsi_task->status = ISCSI_SCSI_STATUS_GOOD; - - break; - } + case ISCSI_SCSI_OPCODE_TESTUNITREADY : case ISCSI_SCSI_OPCODE_STARTSTOPUNIT : { scsi_task->status = ISCSI_SCSI_STATUS_GOOD; @@ -3278,7 +3215,7 @@ static int iscsi_connection_handle_scsi_cmd(iscsi_connection *conn, iscsi_pdu *r } if ( rc == ISCSI_SCSI_TASK_RUN_COMPLETE ) { - iscsi_scsi_task_xfer_complete( &task->scsi_task, request_pdu ); + iscsi_scsi_task_xfer_complete( conn, &task->scsi_task, request_pdu ); } iscsi_task_destroy( task ); @@ -3298,7 +3235,6 @@ static int iscsi_connection_handle_scsi_cmd(iscsi_connection *conn, iscsi_pdu *r * NULL is not allowed here, so take caution. * @param[in] kvpairs Pointer to key and value pairs. * which may NOT be NULL, so take caution. - * @param[in] cid Connection ID (CID). * @return 0 on success, a negative error code otherwise. */ static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_negotiation_kvp *kvpairs) @@ -3327,7 +3263,6 @@ static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscs return rc; if ( conn->session == NULL ) { - iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; conn->session = iscsi_session_create( type ); if ( conn->session == NULL ) { diff --git a/src/server/iscsi.h b/src/server/iscsi.h index 1a98fff..ff33acd 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -38,10 +38,8 @@ #include #include #include -#include #include "globals.h" -#include "image.h" #if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) // GCC-compatible compiler, targeting x86/x86-64 @@ -218,26 +216,6 @@ static inline void iscsi_put_le64(uint8_t *data, const uint64_t value) #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); -} - - /// 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)) @@ -495,9 +473,6 @@ typedef struct __attribute__((packed)) iscsi_ahs_packet { /// AHS-Specific. uint8_t specific; - - /// AHS-Specific data. - uint8_t data[0]; } iscsi_ahs_packet; /** @@ -553,7 +528,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb { uint8_t opcode; /// Additional op-code specific data. - uint8_t data[0]; + uint8_t data[15]; } iscsi_scsi_cdb; @@ -571,7 +546,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb { */ typedef struct __attribute__((packed)) iscsi_scsi_cdb_inquiry { /// SCSI opcode. - iscsi_scsi_cdb cdb; + uint8_t opcode; /// Logical Unit Number (LUN), CMMDT and EVPD. uint8_t lun_flags; @@ -594,7 +569,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_inquiry { */ typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_6 { /// SCSI opcode. - iscsi_scsi_cdb cdb; + uint8_t opcode; /// Logical Block Address (LBA). uint8_t lba[3]; @@ -614,7 +589,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_6 { */ typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_10 { /// SCSI opcode. - iscsi_scsi_cdb cdb; + uint8_t opcode; /// Flags. uint8_t flags; @@ -640,7 +615,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_10 { */ typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_12 { /// SCSI opcode. - iscsi_scsi_cdb cdb; + uint8_t opcode; /// Flags. uint8_t flags; @@ -666,7 +641,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_12 { */ typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_16 { /// SCSI opcode. - iscsi_scsi_cdb cdb; + uint8_t opcode; /// Flags. uint8_t flags; @@ -702,7 +677,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_16 { */ typedef struct __attribute__((packed)) iscsi_scsi_cdb_report_luns { /// SCSI opcode. - iscsi_scsi_cdb cdb; + uint8_t opcode; /// Reserved for future usage (always MUST be 0 for now). uint8_t reserved; @@ -756,7 +731,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_report_luns { */ typedef struct __attribute__((packed)) iscsi_scsi_cdb_service_action_in_16 { /// SCSI opcode. - iscsi_scsi_cdb cdb; + uint8_t opcode; /// Service action. uint8_t action; @@ -829,7 +804,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_service_action_in_16 { */ typedef struct __attribute__((packed)) iscsi_scsi_cdb_mode_sense_6 { /// SCSI opcode. - iscsi_scsi_cdb cdb; + uint8_t opcode; /// Flags. uint8_t flags; @@ -905,7 +880,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_mode_sense_6 { */ typedef struct __attribute__((packed)) iscsi_scsi_cdb_mode_sense_10 { /// SCSI opcode. - iscsi_scsi_cdb cdb; + uint8_t opcode; /// Flags. uint8_t flags; @@ -946,10 +921,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_ds_cmd_data { uint16_t len; /// The Sense Data contains detailed information about a CHECK CONDITION. SPC3 specifies the format and content of the Sense Data. - uint8_t sense_data[0]; - - /// Response Data. - uint8_t res_data[0]; + uint8_t sense_data[]; } iscsi_scsi_ds_cmd_data; @@ -1452,7 +1424,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_inquiry_data_packet { uint16_t alloc_len; /// Parameters. - uint8_t params[0]; + uint8_t params[]; } iscsi_scsi_vpd_page_inquiry_data_packet; @@ -1590,7 +1562,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_design_desc_inquiry_d uint8_t len; /// Designation descriptor. - uint8_t desc[0]; + uint8_t desc[]; } iscsi_scsi_vpd_page_design_desc_inquiry_data_packet; @@ -2675,16 +2647,13 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_long_lba_parameter_ * * This returns mode page specific data. */ -typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_mode_page_data_packet { +typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_mode_page_data_header { /// Page code and flags. - int8_t page_code_flags; + uint8_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; +} iscsi_scsi_mode_sense_mode_page_data_header; /** @@ -2692,19 +2661,16 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_mode_page_data_pack * * This returns mode sub page specific data. */ -typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_mode_sub_page_data_packet { +typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_mode_sub_page_data_header { /// Page code and flags. - int8_t page_code_flags; + uint8_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; +} iscsi_scsi_mode_sense_mode_sub_page_data_header; /** @@ -2714,7 +2680,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_mode_sub_page_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; + iscsi_scsi_mode_sense_mode_page_data_header mode_page; /// Flags. uint8_t flags; @@ -2746,7 +2712,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_read_write_err_reco */ 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; + iscsi_scsi_mode_sense_mode_page_data_header mode_page; /// Reserved for future usage (always MUST be 0 for now). uint16_t reserved; @@ -2781,7 +2747,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_disconnect_reconnec */ 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; + iscsi_scsi_mode_sense_mode_page_data_header mode_page; /// Flags. uint8_t flags; @@ -2844,7 +2810,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_verify_err_recovery */ 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; + iscsi_scsi_mode_sense_mode_page_data_header mode_page; /// Flags. uint8_t flags; @@ -2888,7 +2854,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_caching_mode_page_d */ 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; + iscsi_scsi_mode_sense_mode_page_data_header mode_page; /// Flags. uint8_t flags; @@ -2920,7 +2886,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_control_mode_page_d */ 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; + iscsi_scsi_mode_sense_mode_sub_page_data_header mode_sub_page; /// Flags. uint8_t flags; @@ -2946,7 +2912,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_control_ext_mode_pa */ 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; + iscsi_scsi_mode_sense_mode_page_data_header mode_page; /// Flags. uint8_t flags; @@ -2981,7 +2947,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_xor_ext_mode_page_d */ 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; + iscsi_scsi_mode_sense_mode_page_data_header mode_page; /// Flags. uint8_t flags; @@ -3028,7 +2994,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_power_cond_mode_pag */ 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; + iscsi_scsi_mode_sense_mode_page_data_header mode_page; /// Flags. uint8_t flags; @@ -3338,12 +3304,6 @@ typedef struct __attribute__((packed)) iscsi_scsi_cmd_packet { * MUST be used to contain the CDB spillover. */ iscsi_scsi_cdb scsi_cdb; - - /// Optional AHS packet data. - iscsi_ahs_packet ahs; - - /// Optional data segment, command data. - iscsi_scsi_ds_cmd_data ds_cmd_data; } iscsi_scsi_cmd_packet; @@ -3662,9 +3622,6 @@ typedef struct __attribute__((packed)) iscsi_scsi_response_packet { * transferred. */ uint32_t res_cnt; - - /// Optional data segment, command data. - iscsi_scsi_ds_cmd_data ds_cmd_data; } iscsi_scsi_response_packet; /// SCSI data out / in flags: Immediately process transfer. @@ -3871,9 +3828,6 @@ typedef struct __attribute__((packed)) iscsi_scsi_data_in_response_packet { /// Residual Count or Reserved. uint32_t res_cnt; - - /// Data segment. - iscsi_scsi_ds_cmd_data ds_cmd_data; } iscsi_scsi_data_in_response_packet; /** @@ -3985,26 +3939,6 @@ typedef struct __attribute__((packed)) iscsi_text_req_packet { /// Reserved for future usage, always MUST be 0. uint64_t reserved2[2]; - - /** - * @brief Data segment. - * - * The data lengths of a Text Request MUST NOT exceed the iSCSI target - * MaxRecvDataSegmentLength (a parameter that is negotiated per - * connection and per direction).\n - * A key=value pair can span Text Request or Text Response boundaries. - * A key=value pair can start in one PDU and continue on the next. In - * other words, the end of a PDU does not necessarily signal the end of - * a key=value pair.\n - * The target responds by sending its response back to the initiator. - * The response text format is similar to the request text format. The - * Text Response MAY refer to key=value pairs presented in an earlier - * Text Request, and the text in the request may refer to earlier - * responses.\n - * Text operations are usually meant for parameter setting/negotiations - * but can also be used to perform some long-lasting operations. - */ - iscsi_scsi_ds_cmd_data ds_cmd_data; } iscsi_text_req_packet; @@ -4106,21 +4040,6 @@ typedef struct __attribute__((packed)) iscsi_text_response_packet { /// Reserved for future usage, always MUST be 0. uint64_t reserved2[2]; - - /** - * @brief Data segment. - * - * The data lengths of a Text Response MUST NOT exceed the iSCSI - * initiator MaxRecvDataSegmentLength (a parameter that is negotiated - * per connection and per direction).\n - * The text in the Text Response Data is governed by the same rules as - * the text in the Text Request Data.\n - * Although the initiator is the requesting party and controls the - * request-response initiation and termination, the target can offer - * key=value pairs of its own as part of a sequence and not only in - * response to the initiator. - */ - iscsi_scsi_ds_cmd_data ds_cmd_data; } iscsi_text_response_packet; @@ -4773,17 +4692,6 @@ typedef struct __attribute__((packed)) iscsi_login_response_packet { /// Reserved for future usage, always MUST be 0. uint64_t reserved3; - - /** - * @brief Data segment - Login Parameters in Text Request Format. - * - * The target MUST provide some basic parameters in order to enable the - * initiator to determine if it is connected to the correct port and the - * initial text parameters for the security exchange.\n - * All the rules specified for Text Responses also hold for Login - * Responses. - */ - iscsi_scsi_ds_cmd_data ds_cmd_data; } iscsi_login_response_packet; @@ -5303,16 +5211,6 @@ typedef struct __attribute__((packed)) iscsi_nop_out_packet { /// Reserved for future usage, always MUST be 0. uint64_t reserved2[2]; - - /** - * @brief DataSegment - Ping Data (optional). - * - * Ping data is reflected in the NOP-In Response. The length of the - * reflected data is limited to MaxRecvDataSegmentLength. The length of - * ping data is indicated by the DataSegmentLength. 0 is a valid value - * for the DataSegmentLength and indicates the absence of ping data. - */ - iscsi_scsi_ds_cmd_data ds_ping_data; } iscsi_nop_out_packet; @@ -5396,9 +5294,6 @@ typedef struct __attribute__((packed)) iscsi_nop_in_packet { /// Reserved for future usage, always MUST be 0. uint64_t reserved3; - - /// DataSegment - Return Ping Data. - iscsi_scsi_ds_cmd_data ds_ping_data; } iscsi_nop_in_packet; @@ -5462,29 +5357,6 @@ typedef struct iscsi_key_value_pair { typedef struct iscsi_connection iscsi_connection; -/** - * @brief iSCSI Text / Login key=value packet data construction helper. - * - * This structure is used to store the key=value plus NUL terminator - * pairs for sending as DataSegment packet data to the client. - */ -typedef struct iscsi_key_value_pair_packet { - /// Associated iSCSI connection. - iscsi_connection *conn; - - /// Current text buffer containing multiple key=value + NUL terminator pairs. - uint8_t *buf; - - /// Position of output buffer for next write. - uint32_t pos; - - /// Current length of buffer including final NUL terminator without iSCSI zero padding. - uint32_t len; - - /// Discovery mode. - int discovery; -} iscsi_key_value_pair_packet; - /// 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; @@ -5665,7 +5537,7 @@ typedef struct iscsi_key_value_pair_packet { /// iSCSI SCSI task run: Unknown. -#define ISCSI_SCSI_TASK_RUN_UNKNOWN -1 +#define ISCSI_SCSI_TASK_RUN_UNKNOWN (-1) /// iSCSI SCSI task run: Completed. #define ISCSI_SCSI_TASK_RUN_COMPLETE 0 @@ -5738,6 +5610,9 @@ typedef struct iscsi_scsi_task { /// Block shift difference between dnbd3 (4k) and iSCSI (512b) #define ISCSI_SCSI_EMU_BLOCK_DIFF_SHIFT (3) +_Static_assert( (ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE << ISCSI_SCSI_EMU_BLOCK_DIFF_SHIFT) == ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE, + "Block size parameters are inconsistent" ); + /// iSCSI SCSI emulation I/O type: Removable. #define ISCSI_SCSI_EMU_IO_TYPE_REMOVABLE (1 << 0) @@ -5996,7 +5871,7 @@ typedef struct iscsi_pdu { iscsi_ahs_packet *ahs_pkt; /// iSCSI DataSegment (DS) packet data for fast access and is straight after BHS, AHS and header digest packet in memory. - iscsi_scsi_ds_cmd_data *ds_cmd_data; + void *ds_cmd_data; /// Flags. int flags; @@ -6034,18 +5909,9 @@ typedef struct iscsi_task { /// Underlying SCSI task structure. iscsi_scsi_task scsi_task; - /// Associated iSCSI connection. - iscsi_connection *conn; - - /// Buffer position in bytes. - uint32_t pos; - /// Buffer length in bytes. uint32_t len; - /// Unique identifier for this task. - uint64_t id; - /// LUN identifier associated with this task (always MUST be between 0 and 7), used for hot removal tracking. int lun_id; @@ -6054,9 +5920,6 @@ typedef struct iscsi_task { /// Target Transfer Tag (TTT). uint32_t target_xfer_tag; - - /// SCSI Data In Data Sequence Number (DataSN). - uint32_t data_sn; } iscsi_task; void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *request, const int len); // Handles an iSCSI connection until connection is closed -- cgit v1.2.3-55-g7522 From feebe5d5f76bf42e8694dc065bf4ef2f1285db31 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 6 Nov 2025 11:33:43 +0100 Subject: [SERVER] iscsi: Reply OK to any task management function Since we process everything sequentially, by the time we receive any task management function, the task referenced in the request has already been rejected (or processed), so we just reply OK for now, so the SNs don't get messed up. --- src/server/iscsi.c | 37 +++++++++- src/server/iscsi.h | 199 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 235 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index b0b171e..892172e 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -109,6 +109,8 @@ static iscsi_bhs_packet *iscsi_connection_pdu_resize(iscsi_pdu *pdu, const uint static bool iscsi_connection_pdu_write(iscsi_connection *conn, iscsi_pdu *pdu); +static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu, int reason_code); + /** * @brief Copies a string with additional padding character to fill in a specified size. @@ -3066,6 +3068,38 @@ static int iscsi_connection_handle_logout_req(iscsi_connection *conn, iscsi_pdu return ret ? ISCSI_CONNECT_PDU_READ_OK : ISCSI_CONNECT_PDU_READ_ERR_FATAL; } +/** + * @brief Handles an iSCSI task management function request and generates an appropriate response. + * + * This function processes an incoming iSCSI task management function request PDU, + * constructs a corresponding response PDU, and sends it back to the initiator. + * + * @param[in] conn Pointer to the iSCSI connection structure. Must not be NULL. + * This represents the connection for which the request is being handled. + * @param[in] request_pdu Pointer to the incoming iSCSI task management function + * request PDU. Must not be NULL. + * + * @return 0 on successful PDU write, or -1 on failure. + */ +static int iscsi_connection_handle_task_func_req(iscsi_connection *conn, iscsi_pdu *request_pdu) +{ + iscsi_pdu CLEANUP_PDU response_pdu; + if ( !iscsi_connection_pdu_init( &response_pdu, 0, false ) ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + iscsi_task_mgmt_func_response_packet *mgmt_resp = (iscsi_task_mgmt_func_response_packet *) response_pdu.bhs_pkt; + iscsi_task_mgmt_func_req_packet *mgmt_req = (iscsi_task_mgmt_func_req_packet *) request_pdu->bhs_pkt; + + mgmt_resp->opcode = ISCSI_OPCODE_SERVER_TASK_FUNC_RES; + mgmt_resp->response = ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_COMPLETE; + mgmt_resp->flags = 0x80; + mgmt_resp->init_task_tag = mgmt_req->init_task_tag; // Copying over doesn't change endianess. + iscsi_put_be32( (uint8_t *) &mgmt_resp->stat_sn, conn->stat_sn++ ); + iscsi_put_be32( (uint8_t *) &mgmt_resp->exp_cmd_sn, conn->session->exp_cmd_sn ); + iscsi_put_be32( (uint8_t *) &mgmt_resp->max_cmd_sn, conn->session->max_cmd_sn ); + + return iscsi_connection_pdu_write( conn, &response_pdu ) ? 0 : -1; +} + /** * @brief Handles an incoming iSCSI payload data NOP-Out request PDU. * @@ -3624,7 +3658,8 @@ static int iscsi_connection_pdu_handle(iscsi_connection *conn, iscsi_pdu *reques break; } case ISCSI_OPCODE_CLIENT_TASK_FUNC_REQ : { - // TODO: Send OK if a task is requested to be cancelled + rc = iscsi_connection_handle_task_func_req( conn, request_pdu ); + break; } default : { diff --git a/src/server/iscsi.h b/src/server/iscsi.h index ff33acd..a806530 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -3624,6 +3624,205 @@ typedef struct __attribute__((packed)) iscsi_scsi_response_packet { uint32_t res_cnt; } iscsi_scsi_response_packet; + +/** + * @brief iSCSI Task Management Function Request packet data. + * + * This structure is used to explicity control the execution of one + * or more tasks (iSCSI and SCSI). + */ +typedef struct __attribute__((packed)) iscsi_task_mgmt_func_req_packet { + /// Always 2 according to iSCSI specification. + uint8_t opcode; + + /** + * @brief Function. + * + * The task management functions provide an initiator with a way to + * explicitly control the execution of one or more tasks (SCSI and iSCSI + * tasks). The task management function codes are listed below. For a + * more detailed description of SCSI task management, see SAM2. + */ + int8_t func; + + /// Reserved fot future usage, always MUST be 0. + uint16_t reserved; + + /// TotalAHSLength (MUST be 0 for this PDU). + uint8_t total_ahs_len; + + /// DataSegmentLength (MUST be 0 for this PDU). + uint8_t ds_len[3]; + + /** + * @brief Logical Unit Number (LUN) or Reserved. + * + * This field is required for functions that address a specific LU + * (ABORT TASK, CLEAR TASK SET, ABORT TASK SET, CLEAR ACA, LOGICAL UNIT + * RESET) and is reserved in all others + */ + uint64_t lun; + + /** + * @brief Initiator Task Tag (ITT). + * + * This is the Initiator Task Tag of the task to be aborted for the + * ABORT TASK function or reassigned for the TASK REASSIGN function. + * For all the other functions, this field MUST be set to the reserved + * value 0xFFFFFFFF. + */ + uint32_t init_task_tag; + + /// Referenced task tag or 0xFFFFFFFF. + uint32_t ref_task_tag; + + /// CmdSN. + uint32_t cmd_sn; + + /// ExpStatSN + uint32_t exp_stat_sn; + + /** + * @brief RefCmdSN or Reserved. + * + * If an ABORT TASK is issued for a task created by an immediate + * command, then the RefCmdSN MUST be that of the task management + * request itself (i.e., the CmdSN and RefCmdSN are equal).\n + * For an ABORT TASK of a task created by a non-immediate command, the + * RefCmdSN MUST be set to the CmdSN of the task identified by the + * Referenced Task Tag field. Targets must use this field when the task + * identified by the Referenced Task Tag field is not with the target. + * Otherwise, this field is reserved. + */ + uint32_t ref_cmd_sn; + + /** + * @brief ExpDataSN or Reserved. + * + * For recovery purposes, the iSCSI target and initiator maintain a data + * acknowledgment reference number - the first input DataSN number + * unacknowledged by the initiator. When issuing a new command, this + * number is set to 0. If the function is TASK REASSIGN, which + * establishes a new connection allegiance for a previously issued read + * or bidirectional command, the ExpDataSN will contain an updated data + * acknowledgment reference number or the value 0; the latter indicates + * that the data acknowledgment reference number is unchanged. The + * initiator MUST discard any data PDUs from the previous execution that + * it did not acknowledge, and the target MUST transmit all Data-In PDUs + * (if any) starting with the data acknowledgment reference number. The + * number of retransmitted PDUs may or may not be the same as the + * original transmission, depending on if there was a change in + * MaxRecvDataSegmentLength in the reassignment. The target MAY also + * send no more Data-In PDUs if all data has been acknowledged. + * The value of ExpDataSN MUST be 0 or higher than the DataSN of the + * last acknowledged Data-In PDU, but not larger than DataSN + 1 of the + * last Data-IN PDU sent by the target. Any other value MUST be ignored + * by the target. + * For other functions, this field is reserved + */ + uint32_t exp_data_sn; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2; +} iscsi_task_mgmt_func_req_packet; + + +/// Task management function response: Function complete. +#define ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_COMPLETE 0x00 + +/** + * @brief iSCSI Task Management Function Response packet data. + * + * For the functions ABORT TASK, ABORT TASK SET, CLEAR ACA, CLEAR TASK + * SET, LOGICAL UNIT RESET, TARGET COLD RESET, TARGET WARM RESET, and + * TASK REASSIGN, the target performs the requested task management + * function and sends a task management response back to the initiator. + * For TASK REASSIGN, the new connection allegiance MUST ONLY become + * effective at the target after the target issues the task management + * response. + */ +typedef struct __attribute__((packed)) iscsi_task_mgmt_func_response_packet { + /// Always 0x22 according to specification. + uint8_t opcode; + + /// Reserved for future usage (always MUST be 0x80 for now). + uint8_t flags; + + /** + * @brief Function response. + * + * For the TARGET COLD RESET and TARGET WARM RESET functions, the target + * cancels all pending operations across all LUs known to the issuing + * initiator. For the TARGET COLD RESET function, the target MUST then + * close all of its TCP connections to all initiators (terminates all + * sessions).\n + * The mapping of the response code into a SCSI service response code + * value, if needed, is outside the scope of this document. However, in + * symbolic terms, Response values 0 and 1 map to the SCSI service + * response of FUNCTION COMPLETE. Response value 2 maps to the SCSI + * service response of INCORRECT LOGICAL UNIT NUMBER. All other + * Response values map to the SCSI service response of FUNCTION + * REJECTED. If a Task Management Function Response PDU does not arrive + * before the session is terminated, the SCSI service response is + * SERVICE DELIVERY OR TARGET FAILURE.\n + * The response to ABORT TASK SET and CLEAR TASK SET MUST only be issued + * by the target after all of the commands affected have been received + * by the target, the corresponding task management functions have been + * executed by the SCSI target, and the delivery of all responses + * delivered until the task management function completion has been + * confirmed (acknowledged through the ExpStatSN) by the initiator on + * all connections of this session.\n + * For the ABORT TASK function,\n + * -# if the Referenced Task Tag identifies a valid task leading to a + * successful termination, then targets must return the "Function + * complete" response. + * -# if the Referenced Task Tag does not identify an existing task + * but the CmdSN indicated by the RefCmdSN field in the Task + * Management Function Request is within the valid CmdSN window + * and less than the CmdSN of the Task Management Function Request + * itself, then targets must consider the CmdSN as received and + * return the "Function complete" response. + * -# if the Referenced Task Tag does not identify an existing task + * and the CmdSN indicated by the RefCmdSN field in the Task + * Management Function Request is outside the valid CmdSN window, + * then targets must return the "Task does not exist" response + */ + uint8_t response; + + /// Reserved for future usage, always MUST be 0. + uint8_t reserved; + + /// TotalAHSLength (MUST be 0 for this PDU). + uint8_t total_ahs_len; + + /// DataSegmentLength (MUST be 0 for this PDU). + uint8_t ds_len[3]; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved2; + + /// Initiator Task Tag (ITT). + uint32_t init_task_tag; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved3; + + /// StatSN. + uint32_t stat_sn; + + /// ExpCmdSN. + uint32_t exp_cmd_sn; + + /// MaxCmdSN. + uint32_t max_cmd_sn; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved4; + + /// Reserved for future usage, always MUST be 0. + uint64_t reserved5; +} iscsi_task_mgmt_func_response_packet; + /// SCSI data out / in flags: Immediately process transfer. #define ISCSI_SCSI_DATA_OUT_DATA_IN_FLAGS_IMMEDIATE (1 << 7) -- cgit v1.2.3-55-g7522 From 7af9c82192a1360a08c731c2d001e40c2c7eca55 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 6 Nov 2025 12:11:39 +0100 Subject: [SERVER] iscsi: Minor clang-tidy cleanups (mostly) - Remove redundant case block - Remove redundant const/struct prefix - Fix a couple missing params in Doxygen blocks --- src/server/iscsi.c | 344 ++++++++++++++++++----------------------------------- 1 file changed, 114 insertions(+), 230 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 892172e..5657710 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -67,7 +67,7 @@ //#define malloc(x) (rand() % 100 == 0 ? NULL : malloc(x)) -// Use for stack-allocated iscsi_pdu +/// Use for stack-allocated iscsi_pdu #define CLEANUP_PDU __attribute__((cleanup(iscsi_connection_pdu_destroy))) static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task); @@ -77,39 +77,35 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task); static void iscsi_scsi_task_init(iscsi_scsi_task *scsi_task); // Initializes a SCSI task -static void iscsi_scsi_task_xfer_complete(iscsi_connection *conn, iscsi_scsi_task *scsi_task, iscsi_pdu *request_pdu); // Callback function when an iSCSI SCSI task completed the data transfer +static void iscsi_scsi_task_xfer_complete(iscsi_connection *conn, iscsi_scsi_task *scsi_task, const iscsi_pdu *request_pdu); // Callback function when an iSCSI SCSI task completed the data transfer -static void iscsi_scsi_task_lun_process_none(iscsi_scsi_task *scsi_task); // Processes a iSCSI SCSI task with no LUN identifier +static uint64_t iscsi_scsi_lun_get_from_scsi(int lun_id); // Converts an internal representation of a LUN identifier to an iSCSI LUN required for packet data +static int iscsi_scsi_lun_get_from_iscsi(uint64_t lun); // Converts an iSCSI LUN from packet data to internal SCSI LUN identifier +static int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, dnbd3_image_t *image, uint64_t offset_blocks, uint64_t num_blocks); // Reads a number of blocks from a block offset of a DNBD3 image to a specified buffer -static 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 -static int iscsi_scsi_lun_get_from_iscsi(const uint64_t lun); // Converts an iSCSI LUN from packet data to internal SCSI LUN identifier - -static int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, dnbd3_image_t *image, const uint64_t offset_blocks, const uint64_t num_blocks); // Reads a number of blocks from a block offset of a DNBD3 image to a specified buffer - -static 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 +static void iscsi_strcpy_pad(char *dst, const char *src, size_t size, int pad); // Copies a string with additional padding character to fill in a specified size static iscsi_task *iscsi_task_create(iscsi_connection *conn); // Allocates and initializes an iSCSI task structure static void iscsi_task_destroy(iscsi_task *task); // Deallocates resources acquired by iscsi_task_create static uint64_t iscsi_target_node_wwn_get(const uint8_t *name); // Calculates the WWN using 64-bit IEEE Extended NAA for a name -static iscsi_session *iscsi_session_create(const int type); // Creates and initializes an iSCSI session +static iscsi_session *iscsi_session_create(int type); // Creates and initializes an iSCSI session static void iscsi_session_destroy(iscsi_session *session); // Deallocates all resources acquired by iscsi_session_create static iscsi_connection *iscsi_connection_create(dnbd3_client_t *client); // Creates data structure for an iSCSI connection from iSCSI portal and TCP/IP socket static void iscsi_connection_destroy(iscsi_connection *conn); // Deallocates all resources acquired by iscsi_connection_create -static void iscsi_connection_login_response_reject(iscsi_pdu *login_response_pdu, const iscsi_pdu *pdu); // Initializes a rejecting login response packet -static bool iscsi_connection_pdu_init(iscsi_pdu *pdu, const uint32_t ds_len, bool no_ds_alloc); -static void iscsi_connection_pdu_destroy(iscsi_pdu *pdu); +static bool iscsi_connection_pdu_init(iscsi_pdu *pdu, uint32_t ds_len, bool no_ds_alloc); +static void iscsi_connection_pdu_destroy(const iscsi_pdu *pdu); -static iscsi_bhs_packet *iscsi_connection_pdu_resize(iscsi_pdu *pdu, const uint ahs_len, const uint32_t ds_len); // Appends packet data to an iSCSI PDU structure used by connections +static iscsi_bhs_packet *iscsi_connection_pdu_resize(iscsi_pdu *pdu, uint ahs_len, uint32_t ds_len); // Appends packet data to an iSCSI PDU structure used by connections -static bool iscsi_connection_pdu_write(iscsi_connection *conn, iscsi_pdu *pdu); +static bool iscsi_connection_pdu_write(iscsi_connection *conn, const iscsi_pdu *pdu); -static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu, int reason_code); +static int iscsi_connection_handle_reject(iscsi_connection *conn, const iscsi_pdu *pdu, int reason_code); /** @@ -348,7 +344,7 @@ static int iscsi_parse_login_key_value_pairs(iscsi_negotiation_kvp *pairs, const */ static iscsi_task *iscsi_task_create(iscsi_connection *conn) { - iscsi_task *task = malloc( sizeof(struct iscsi_task) ); + iscsi_task *task = malloc( sizeof(iscsi_task) ); if ( task == NULL ) { logadd( LOG_ERROR, "iscsi_task_create: Out of memory while allocating iSCSI task" ); @@ -408,8 +404,8 @@ static void iscsi_task_destroy(iscsi_task *task) * @param[in] immediate whether immediate bit was set in this request * @return true success, false error */ -static bool 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, bool immediate) +static bool iscsi_scsi_data_in_send(iscsi_connection *conn, const iscsi_task *task, + const uint32_t pos, const uint32_t len, const uint32_t res_cnt, const uint32_t data_sn, const uint8_t flags, bool immediate) { iscsi_pdu CLEANUP_PDU response_pdu; if ( !iscsi_connection_pdu_init( &response_pdu, len, true ) ) @@ -503,7 +499,7 @@ static bool iscsi_scsi_data_in_send(iscsi_connection *conn, iscsi_task *task, * @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, bool immediate) +static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, const iscsi_task *task, bool immediate) { if ( task->scsi_task.status != ISCSI_SCSI_STATUS_GOOD ) return 0; @@ -511,7 +507,7 @@ static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, iscsi_task *task const uint32_t expected_len = task->scsi_task.exp_xfer_len; uint32_t xfer_len = task->scsi_task.len; uint32_t res_cnt = 0UL; - int8_t flags = 0; + uint8_t flags = 0; if ( expected_len < xfer_len ) { res_cnt = (xfer_len - expected_len); @@ -531,7 +527,7 @@ static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, iscsi_task *task // Max recv segment length = total length of one individual PDU const uint32_t seg_len = conn->session->opts.MaxRecvDataSegmentLength; const uint32_t data_in_seq_count = ((xfer_len - 1) / max_burst_len) + 1; - int8_t status = 0; + uint8_t status = 0; for ( uint32_t i = 0UL; i < data_in_seq_count; i++ ) { uint32_t seq_end = (max_burst_offset + max_burst_len); @@ -595,12 +591,13 @@ static void iscsi_scsi_task_init(iscsi_scsi_task *scsi_task) * This function post-processes a task upon * finish of data transfer. * + * @param[in] conn Current connection * @param[in] scsi_task Pointer to iSCSI SCSI task which finished * the data transfer and may NOT be NULL, * so be careful. * @param request_pdu */ -static void iscsi_scsi_task_xfer_complete(iscsi_connection *conn, iscsi_scsi_task *scsi_task, iscsi_pdu *request_pdu) +static void iscsi_scsi_task_xfer_complete(iscsi_connection *conn, iscsi_scsi_task *scsi_task, const iscsi_pdu *request_pdu) { iscsi_task *task = container_of( scsi_task, iscsi_task, scsi_task ); @@ -697,7 +694,7 @@ static void iscsi_scsi_task_sense_data_build(iscsi_scsi_task *scsi_task, const u 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) ); + sense_data = malloc( sizeof(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" ); @@ -712,7 +709,7 @@ static void iscsi_scsi_task_sense_data_build(iscsi_scsi_task *scsi_task, const u 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->sense_data.add_len = (sizeof(iscsi_scsi_sense_data_check_cond_packet) - sizeof(iscsi_scsi_sense_data_packet)); sense_data->cmd_spec_info = 0UL; // Zero does not require endianess conversion sense_data->asc = asc; @@ -721,7 +718,7 @@ static void iscsi_scsi_task_sense_data_build(iscsi_scsi_task *scsi_task, const u 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); + scsi_task->sense_data_len = sizeof(iscsi_scsi_sense_data_check_cond_packet); } /** @@ -748,24 +745,6 @@ static void iscsi_scsi_task_status_set(iscsi_scsi_task *scsi_task, const uint8_t scsi_task->status = status; } -/** - * @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. - */ -static void iscsi_scsi_task_lun_process_none(iscsi_scsi_task *scsi_task) -{ - 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 ); -} - /** * @brief Converts an internal representation of a LUN identifier to an iSCSI LUN required for packet data. * @@ -1072,7 +1051,7 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) return iscsi_scsi_emu_block_read( image, scsi_task, lba, xfer_len ); } case ISCSI_SCSI_OPCODE_READCAPACITY10 : { - iscsi_scsi_read_capacity_10_parameter_data_packet *buf = malloc( sizeof(struct iscsi_scsi_read_capacity_10_parameter_data_packet) ); + iscsi_scsi_read_capacity_10_parameter_data_packet *buf = malloc( sizeof(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 ); @@ -1102,7 +1081,7 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) != ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_ACTION_READ_CAPACITY_16 ) { return ISCSI_SCSI_TASK_RUN_UNKNOWN; } - iscsi_scsi_service_action_in_16_parameter_data_packet *buf = malloc( sizeof(struct iscsi_scsi_service_action_in_16_parameter_data_packet) ); + iscsi_scsi_service_action_in_16_parameter_data_packet *buf = malloc( sizeof(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, @@ -1225,9 +1204,9 @@ static size_t iscsi_scsi_emu_pad_scsi_name(uint8_t *buf, const uint8_t *name) * @return length of data 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) +static int iscsi_scsi_emu_primary_inquiry(const 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) ) { + if ( len < sizeof(iscsi_scsi_std_inquiry_data_packet) ) { 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 ); @@ -1273,11 +1252,11 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task 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); + if ( alloc_len >= (len - sizeof(iscsi_scsi_vpd_page_inquiry_data_packet)) ) + alloc_len = (uint) ((len - sizeof(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)) ); + memset( (vpd_page_inquiry_data_pkt->params + alloc_len), '\0', (len - alloc_len - sizeof(iscsi_scsi_vpd_page_inquiry_data_packet)) ); alloc_len++; @@ -1290,15 +1269,15 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task const uint dev_name_len = (uint) (strlen( image->name ) + 1U); const uint port_name_len = (uint) (strlen( 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 + alloc_len = (sizeof(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(iscsi_scsi_vpd_page_design_desc_ieee_naa_ext_inquiry_data_packet)); // 64-bit IEEE NAA Extended + alloc_len += (sizeof(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(iscsi_scsi_vpd_page_design_desc_t10_vendor_id_inquiry_data_packet)); // T10 Vendor ID + alloc_len += (uint) (sizeof(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + ISCSI_ALIGN(dev_name_len, ISCSI_ALIGN_SIZE)); // SCSI Device Name + alloc_len += (uint) (sizeof(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + ISCSI_ALIGN(port_name_len, ISCSI_ALIGN_SIZE)); // SCSI Target Port Name + alloc_len += (sizeof(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(iscsi_scsi_vpd_page_design_desc_rel_target_port_inquiry_data_packet)); // Relative Target Port + alloc_len += (sizeof(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(iscsi_scsi_vpd_page_design_desc_target_port_group_inquiry_data_packet)); // Target Port Group + alloc_len += (sizeof(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(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)) ) { + if ( len < (alloc_len + sizeof(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; @@ -1309,17 +1288,17 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task 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(ISCSI_DEFAULT_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); + vpd_page_design_desc_inquiry_data_pkt->len = sizeof(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)); + alloc_len = (sizeof(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(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(ISCSI_DEFAULT_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); + vpd_page_design_desc_inquiry_data_pkt->len = sizeof(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; @@ -1327,62 +1306,62 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task 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->name, 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)); + alloc_len += (sizeof(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(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 = (iscsi_scsi_vpd_page_design_desc_inquiry_data_packet *) (((uint8_t *) vpd_page_design_desc_inquiry_data_pkt) + (sizeof(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(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(ISCSI_DEFAULT_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, (const uint8_t*)image->name ); - alloc_len += (uint) (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + vpd_page_design_desc_inquiry_data_pkt->len); + alloc_len += (uint) (sizeof(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 = (iscsi_scsi_vpd_page_design_desc_inquiry_data_packet *) (((uint8_t *) vpd_page_design_desc_inquiry_data_pkt) + (sizeof(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(ISCSI_DEFAULT_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, (const uint8_t*)port_name ); - alloc_len += (uint) (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + vpd_page_design_desc_inquiry_data_pkt->len); + alloc_len += (uint) (sizeof(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 = (iscsi_scsi_vpd_page_design_desc_inquiry_data_packet *) (((uint8_t *) vpd_page_design_desc_inquiry_data_pkt) + (sizeof(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(ISCSI_DEFAULT_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); + vpd_page_design_desc_inquiry_data_pkt->len = sizeof(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, 1 ); - 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)); + alloc_len += (sizeof(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(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 = (iscsi_scsi_vpd_page_design_desc_inquiry_data_packet *) (((uint8_t *) vpd_page_design_desc_inquiry_data_pkt) + (sizeof(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(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(ISCSI_DEFAULT_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); + vpd_page_design_desc_inquiry_data_pkt->len = sizeof(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)); + alloc_len += (sizeof(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(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 = (iscsi_scsi_vpd_page_design_desc_inquiry_data_packet *) (((uint8_t *) vpd_page_design_desc_inquiry_data_pkt) + (sizeof(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(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(ISCSI_DEFAULT_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); + vpd_page_design_desc_inquiry_data_pkt->len = sizeof(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) ISCSI_DEFAULT_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)); + alloc_len += (sizeof(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(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 ); @@ -1393,7 +1372,7 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task 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)) ) { + if ( len < (alloc_len + sizeof(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; @@ -1423,13 +1402,13 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task 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)) ) { + if ( len < (sizeof(iscsi_scsi_vpd_page_inquiry_data_packet) + sizeof(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); + alloc_len = sizeof(iscsi_scsi_vpd_page_block_limits_inquiry_data_packet); vpd_page_block_limits_inquiry_data_pkt->flags = 0; @@ -1463,13 +1442,13 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task 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)) ) { + if ( len < (sizeof(iscsi_scsi_vpd_page_inquiry_data_packet) + sizeof(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); + alloc_len = sizeof(iscsi_scsi_vpd_page_block_dev_chars_inquiry_data_packet); vpd_page_block_dev_chars_inquiry_data_pkt->medium_rotation_rate = ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_MEDIUM_ROTATION_RATE_NONE; vpd_page_block_dev_chars_inquiry_data_pkt->product_type = ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_PRODUCT_TYPE_NOT_INDICATED; @@ -1498,7 +1477,7 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task } } - return (int) (alloc_len + sizeof(struct iscsi_scsi_vpd_page_inquiry_data_packet)); + return (int) (alloc_len + sizeof(iscsi_scsi_vpd_page_inquiry_data_packet)); } // Normal INQUIRY, no VPD @@ -1522,7 +1501,7 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task 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)); + uint add_len = (sizeof(iscsi_scsi_std_inquiry_data_packet) - sizeof(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) ) { @@ -1570,8 +1549,8 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task 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])); + if ( alloc_len > (sizeof(iscsi_scsi_ext_inquiry_data_packet) - offsetof(iscsi_scsi_ext_inquiry_data_packet, version_desc[4])) ) + alloc_len = (sizeof(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; @@ -1579,7 +1558,7 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task std_inquiry_data_pkt->basic_inquiry.add_len = (uint8_t) add_len; - return (int) (add_len + sizeof(struct iscsi_scsi_basic_inquiry_data_packet)); + return (int) (add_len + sizeof(iscsi_scsi_basic_inquiry_data_packet)); } /** @@ -1602,7 +1581,7 @@ static int iscsi_scsi_emu_primary_report_luns( iscsi_scsi_report_luns_parameter_ { const uint64_t lun = iscsi_scsi_lun_get_from_scsi( ISCSI_DEFAULT_LUN ); - if ( len < sizeof(struct iscsi_scsi_report_luns_parameter_data_lun_list_packet) + sizeof(lun) ) + if ( len < sizeof(iscsi_scsi_report_luns_parameter_data_lun_list_packet) + sizeof(lun) ) return -1; switch ( select_report ) { @@ -1620,7 +1599,7 @@ static int iscsi_scsi_emu_primary_report_luns( iscsi_scsi_report_luns_parameter_ iscsi_put_be32( (uint8_t *) &report_luns_parameter_data_pkt->lun_list_len, sizeof(lun) ); iscsi_put_be64( (uint8_t *) (report_luns_parameter_data_pkt + 1), lun ); - return (int) (sizeof(lun) + sizeof(struct iscsi_scsi_report_luns_parameter_data_lun_list_packet)); + return (int) (sizeof(lun) + sizeof(iscsi_scsi_report_luns_parameter_data_lun_list_packet)); } /** @@ -1708,63 +1687,6 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc } 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; @@ -1832,7 +1754,7 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc 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); + page_len = sizeof(iscsi_scsi_mode_sense_control_ext_mode_page_data_packet); iscsi_scsi_emu_primary_mode_sense_page_init( buffer, page_len, page, sub_page ); @@ -1863,7 +1785,7 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc if ( sub_page != 0U ) break; - page_len = sizeof(struct iscsi_scsi_mode_sense_xor_ext_mode_page_data_packet); + page_len = sizeof(iscsi_scsi_mode_sense_xor_ext_mode_page_data_packet); iscsi_scsi_emu_primary_mode_sense_page_init( buffer, page_len, page, sub_page ); @@ -1875,7 +1797,7 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc if ( sub_page != 0U ) break; - page_len = sizeof(struct iscsi_scsi_mode_sense_power_cond_mode_page_data_packet); + page_len = sizeof(iscsi_scsi_mode_sense_power_cond_mode_page_data_packet); iscsi_scsi_emu_primary_mode_sense_page_init( buffer, page_len, page, sub_page ); @@ -1887,7 +1809,7 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc if ( sub_page != 0U ) break; - page_len = sizeof(struct iscsi_scsi_mode_sense_info_exceptions_control_mode_page_data_packet); + page_len = sizeof(iscsi_scsi_mode_sense_info_exceptions_control_mode_page_data_packet); iscsi_scsi_emu_primary_mode_sense_page_init( buffer, page_len, page, sub_page ); @@ -2144,12 +2066,12 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) const iscsi_scsi_cdb_mode_sense_6 *cdb_mode_sense_6 = (iscsi_scsi_cdb_mode_sense_6 *) scsi_task->cdb; const uint 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 block_desc_len = ((cdb_mode_sense_6->flags & ISCSI_SCSI_CDB_MODE_SENSE_6_FLAGS_DBD) == 0) ? sizeof(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( scsi_task->connection->client->image, scsi_task, NULL, sizeof(struct iscsi_scsi_mode_sense_6_parameter_header_data_packet), block_desc_len, 0U, pc, page, sub_page ); + rc = iscsi_scsi_emu_primary_mode_sense( scsi_task->connection->client->image, scsi_task, NULL, sizeof(iscsi_scsi_mode_sense_6_parameter_header_data_packet), block_desc_len, 0U, pc, page, sub_page ); if ( rc < 0 ) break; @@ -2164,7 +2086,7 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) break; } - rc = iscsi_scsi_emu_primary_mode_sense( scsi_task->connection->client->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 ); + rc = iscsi_scsi_emu_primary_mode_sense( scsi_task->connection->client->image, scsi_task, mode_sense_6_parameter_hdr_data_pkt, sizeof(iscsi_scsi_mode_sense_6_parameter_header_data_packet), block_desc_len, 0U, pc, page, sub_page ); if ( rc >= 0 ) { scsi_task->buf = mode_sense_6_parameter_hdr_data_pkt; @@ -2183,7 +2105,7 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) const uint 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 block_desc_len = (((cdb_mode_sense_10->flags & ISCSI_SCSI_CDB_MODE_SENSE_10_FLAGS_DBD) == 0) ? ((long_lba != 0) ? sizeof(iscsi_scsi_mode_sense_long_lba_parameter_block_desc_data_packet) : sizeof(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; @@ -2203,7 +2125,7 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) break; } - rc = iscsi_scsi_emu_primary_mode_sense( scsi_task->connection->client->image, scsi_task, 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 ); + rc = iscsi_scsi_emu_primary_mode_sense( scsi_task->connection->client->image, scsi_task, mode_sense_10_parameter_hdr_data_pkt, sizeof(iscsi_scsi_mode_sense_10_parameter_header_data_packet), block_desc_len, long_lba, pc10, page10, sub_page10 ); if ( rc >= 0 ) { scsi_task->buf = mode_sense_10_parameter_hdr_data_pkt; @@ -2266,14 +2188,13 @@ static uint64_t iscsi_target_node_wwn_get(const uint8_t *name) * assigned before they are negotiated at the * login phase. * - * @param[in] conn Pointer to iSCSI connection to associate with the session. * @param[in] type Session type to initialize the session with. * @return Pointer to initialized iSCSI session or NULL in case an error * occured (usually due to memory exhaustion). */ static iscsi_session *iscsi_session_create(const int type) { - iscsi_session *session = malloc( sizeof(struct iscsi_session) ); + iscsi_session *session = malloc( sizeof(iscsi_session) ); if ( session == NULL ) { logadd( LOG_ERROR, "iscsi_session_create: Out of memory allocating iSCSI session" ); @@ -2316,7 +2237,7 @@ static void iscsi_session_destroy(iscsi_session *session) */ static iscsi_connection *iscsi_connection_create(dnbd3_client_t *client) { - iscsi_connection *conn = malloc( sizeof(struct iscsi_connection) ); + iscsi_connection *conn = malloc( sizeof(iscsi_connection) ); if ( conn == NULL ) { logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI connection" ); @@ -2413,11 +2334,9 @@ static int iscsi_append_key_value_pair_packet(const bool number, const char *key * * @param[in] conn Pointer to ISCSI connection which should * be updated. - * @retval -1 An error occured, e.g. socket is already closed. - * @retval 0 All values have been updated successfully and - * the socket is still alive. + @param[in] pairs Set of readily parsed key-value-pairs to handle */ -static void iscsi_connection_update_key_value_pairs(iscsi_connection *conn, iscsi_negotiation_kvp *pairs) +static void iscsi_connection_update_key_value_pairs(const iscsi_connection *conn, const iscsi_negotiation_kvp *pairs) { conn->session->opts.MaxBurstLength = CLAMP(pairs->MaxBurstLength, 512, ISCSI_MAX_DS_SIZE); conn->session->opts.FirstBurstLength = CLAMP(pairs->FirstBurstLength, 512, pairs->MaxBurstLength); @@ -2536,11 +2455,13 @@ static int iscsi_connection_pdu_login_response_init(iscsi_pdu *login_response_pd * @param[in] type_str Pointer to key and value pairs which * contain the session type parameter to be evaluated, * which may NOT be NULL, so take caution. + * @param[in] type Write session type constant to this int. + * Must not be null. * @return 0 on successful operation, a negative error code * otherwise. The output session 'type' is unchanged, if * an invalid session type value was retrieved. */ -static int iscsi_login_parse_session_type(iscsi_pdu *login_response_pdu, const char *type_str, int *type) +static int iscsi_login_parse_session_type(const iscsi_pdu *login_response_pdu, const char *type_str, int *type) { iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; @@ -2577,7 +2498,7 @@ static int iscsi_login_parse_session_type(iscsi_pdu *login_response_pdu, const c * @return 0 if the check was successful or a negative * error code otherwise. */ -static int iscsi_image_from_target(iscsi_connection *conn, iscsi_pdu *login_response_pdu, const char *target_name) +static int iscsi_image_from_target(const iscsi_connection *conn, const iscsi_pdu *login_response_pdu, const char *target_name) { iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; @@ -2643,38 +2564,6 @@ static int iscsi_image_from_target(iscsi_connection *conn, iscsi_pdu *login_resp return ISCSI_CONNECT_PDU_READ_OK; } -/** - * @brief Initializes a rejecting login response packet. - * - * The login response structure has status detail - * invalid login request type set. - * - * @param[in] login_response_pdu Pointer to iSCSI login response PDU, - * NULL is an invalid value here, so take caution. - * @param[in] pdu Pointer to iSCSI login request PDU, may NOT - * be NULL, so be careful. - */ -static 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_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->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; -} - /** * @brief Initializes an iSCSI Protocol Data Unit (PDU) object for use in iSCSI communication. * @@ -2702,7 +2591,7 @@ static bool iscsi_connection_pdu_init(iscsi_pdu *pdu, const uint32_t ds_len, boo } const uint32_t pkt_ds_len = no_ds_alloc ? 0 : ISCSI_ALIGN( ds_len, ISCSI_ALIGN_SIZE ); - const uint32_t alloc_len = (uint32_t) ( sizeof(struct iscsi_bhs_packet) + pkt_ds_len ); + const uint32_t alloc_len = (uint32_t) ( sizeof(iscsi_bhs_packet) + pkt_ds_len ); if ( alloc_len > ISCSI_INTERNAL_BUFFER_SIZE ) { pdu->bhs_pkt = pdu->big_alloc = malloc( alloc_len ); @@ -2716,7 +2605,7 @@ static bool iscsi_connection_pdu_init(iscsi_pdu *pdu, const uint32_t ds_len, boo pdu->ahs_pkt = NULL; pdu->ds_cmd_data = (pkt_ds_len != 0UL) - ? (iscsi_scsi_ds_cmd_data *) (((uint8_t *) pdu->bhs_pkt) + sizeof(struct iscsi_bhs_packet)) + ? (iscsi_scsi_ds_cmd_data *) (((uint8_t *) pdu->bhs_pkt) + sizeof(iscsi_bhs_packet)) : NULL; pdu->flags = 0; pdu->bhs_pos = 0U; @@ -2743,7 +2632,7 @@ static bool iscsi_connection_pdu_init(iscsi_pdu *pdu, const uint32_t ds_len, boo * @param[in] pdu Pointer to the iSCSI PDU structure to be destroyed. * If NULL, the function has no effect. */ -static void iscsi_connection_pdu_destroy(iscsi_pdu *pdu) +static void iscsi_connection_pdu_destroy(const iscsi_pdu *pdu) { if ( pdu == NULL ) return; @@ -2787,8 +2676,8 @@ static iscsi_bhs_packet *iscsi_connection_pdu_resize(iscsi_pdu *pdu, const uint iscsi_bhs_packet *bhs_pkt; const uint32_t pkt_ds_len = ISCSI_ALIGN(ds_len, ISCSI_ALIGN_SIZE); - const size_t old_len = (sizeof(struct iscsi_bhs_packet) + (uint32_t) pdu->ahs_len + ISCSI_ALIGN(pdu->ds_len, ISCSI_ALIGN_SIZE)); - const size_t new_len = (sizeof(struct iscsi_bhs_packet) + (uint32_t) ahs_len + pkt_ds_len); + const size_t old_len = (sizeof(iscsi_bhs_packet) + (uint32_t) pdu->ahs_len + ISCSI_ALIGN(pdu->ds_len, ISCSI_ALIGN_SIZE)); + const size_t new_len = (sizeof(iscsi_bhs_packet) + (uint32_t) ahs_len + pkt_ds_len); const bool old_alloced = pdu->big_alloc != NULL; const bool new_alloced = new_len > ISCSI_INTERNAL_BUFFER_SIZE; @@ -2818,8 +2707,8 @@ static iscsi_bhs_packet *iscsi_connection_pdu_resize(iscsi_pdu *pdu, const uint } } - pdu->ahs_pkt = (ahs_len != 0U) ? (iscsi_ahs_packet *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet)) : 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) : NULL; + pdu->ahs_pkt = (ahs_len != 0U) ? (iscsi_ahs_packet *) (((uint8_t *) bhs_pkt) + sizeof(iscsi_bhs_packet)) : NULL; + pdu->ds_cmd_data = (pkt_ds_len != 0UL) ? (iscsi_scsi_ds_cmd_data *) (((uint8_t *) bhs_pkt) + sizeof(iscsi_bhs_packet) + ahs_len) : NULL; pdu->ahs_len = ahs_len; pdu->ds_len = ds_len; @@ -2845,7 +2734,7 @@ static iscsi_bhs_packet *iscsi_connection_pdu_resize(iscsi_pdu *pdu, const uint * @param[in] pdu Pointer to iSCSI server response PDU to send. * May NOT be NULL, so be careful. */ -static bool iscsi_connection_pdu_write(iscsi_connection *conn, iscsi_pdu *pdu) +static bool iscsi_connection_pdu_write(iscsi_connection *conn, const iscsi_pdu *pdu) { if ( conn->state >= ISCSI_CONNECT_STATE_EXITING ) { return false; @@ -2853,7 +2742,7 @@ static bool iscsi_connection_pdu_write(iscsi_connection *conn, iscsi_pdu *pdu) // During allocation we already round up to ISCSI_ALIGN_SIZE, but store the requested size in the ds_len // member, so it's safe to round up here before sending, the accessed memory will be valid and zeroed - const size_t len = (sizeof(struct iscsi_bhs_packet) + pdu->ahs_len + const size_t len = (sizeof(iscsi_bhs_packet) + pdu->ahs_len + (pdu->ds_cmd_data == NULL ? 0 : ISCSI_ALIGN(pdu->ds_len, ISCSI_ALIGN_SIZE))); const ssize_t rc = sock_sendAll( conn->client->sock, pdu->bhs_pkt, len, ISCSI_CONNECT_SOCKET_WRITE_RETRIES ); @@ -2917,9 +2806,9 @@ static inline int iscsi_seq_num_cmp_gt(const uint32_t seq_num, const uint32_t se * currently only happens on memory exhaustion. * @retval 0 Reject packet and PDU constructed and sent successfully to the client. */ -static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu, const int reason_code) +static int iscsi_connection_handle_reject(iscsi_connection *conn, const iscsi_pdu *pdu, const int reason_code) { - const uint32_t ds_len = (uint32_t) sizeof(struct iscsi_bhs_packet) + (uint32_t) (pdu->bhs_pkt->total_ahs_len * ISCSI_ALIGN_SIZE); + const uint32_t ds_len = (uint32_t) sizeof(iscsi_bhs_packet) + (uint32_t) (pdu->bhs_pkt->total_ahs_len * ISCSI_ALIGN_SIZE); iscsi_pdu CLEANUP_PDU response_pdu; if ( !iscsi_connection_pdu_init( &response_pdu, ds_len, false ) ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; @@ -2966,7 +2855,7 @@ static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu * @return Returns `ISCSI_CONNECT_PDU_READ_OK` (0) on success or * `ISCSI_CONNECT_PDU_READ_ERR_FATAL` (-1) if sequence numbers or other data are invalid. */ -static int iscsi_connection_handle_cmd_sn(iscsi_connection *conn, iscsi_pdu *request_pdu) +static int iscsi_connection_handle_cmd_sn(const iscsi_connection *conn, iscsi_pdu *request_pdu) { iscsi_session *session = conn->session; @@ -3013,7 +2902,7 @@ static int iscsi_connection_handle_cmd_sn(iscsi_connection *conn, iscsi_pdu *req * @return 0 on success. A negative value indicates * an error. A positive value a warning. */ -static int iscsi_connection_handle_logout_req(iscsi_connection *conn, iscsi_pdu *request_pdu) +static int iscsi_connection_handle_logout_req(iscsi_connection *conn, const iscsi_pdu *request_pdu) { iscsi_logout_req_packet *logout_req_pkt = (iscsi_logout_req_packet *) request_pdu->bhs_pkt; @@ -3081,7 +2970,7 @@ static int iscsi_connection_handle_logout_req(iscsi_connection *conn, iscsi_pdu * * @return 0 on successful PDU write, or -1 on failure. */ -static int iscsi_connection_handle_task_func_req(iscsi_connection *conn, iscsi_pdu *request_pdu) +static int iscsi_connection_handle_task_func_req(iscsi_connection *conn, const iscsi_pdu *request_pdu) { iscsi_pdu CLEANUP_PDU response_pdu; if ( !iscsi_connection_pdu_init( &response_pdu, 0, false ) ) @@ -3112,11 +3001,10 @@ static int iscsi_connection_handle_task_func_req(iscsi_connection *conn, iscsi_p * NOT be NULL, so take caution. * @param[in] request_pdu Pointer to iSCSI client request PDU to handle. * May be NULL in which case an error is returned. - * @param response_pdu * @return 0 on success. A negative value indicates * an error. A positive value a warning. */ -static int iscsi_connection_handle_nop_out(iscsi_connection *conn, iscsi_pdu *request_pdu) +static int iscsi_connection_handle_nop_out(iscsi_connection *conn, const iscsi_pdu *request_pdu) { if ( conn->session->type == ISCSI_SESSION_TYPE_DISCOVERY ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; @@ -3168,9 +3056,7 @@ static int iscsi_connection_handle_nop_out(iscsi_connection *conn, iscsi_pdu *re memcpy( response_pdu.ds_cmd_data, request_pdu->ds_cmd_data, ds_len ); } - iscsi_connection_pdu_write( conn, &response_pdu ); - - return ISCSI_CONNECT_PDU_READ_OK; + return iscsi_connection_pdu_write( conn, &response_pdu ) ? 0 : -1; } /** @@ -3188,7 +3074,7 @@ static int iscsi_connection_handle_nop_out(iscsi_connection *conn, iscsi_pdu *re * @return 0 on success. A negative value indicates * an error. A positive value a warning. */ -static int iscsi_connection_handle_scsi_cmd(iscsi_connection *conn, iscsi_pdu *request_pdu) +static int iscsi_connection_handle_scsi_cmd(iscsi_connection *conn, const iscsi_pdu *request_pdu) { iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) request_pdu->bhs_pkt; @@ -3231,8 +3117,9 @@ static int iscsi_connection_handle_scsi_cmd(iscsi_connection *conn, iscsi_pdu *r if ( task->lun_id != ISCSI_DEFAULT_LUN ) { logadd( LOG_WARNING, "Received SCSI command for unknown LUN %d", task->lun_id ); - iscsi_scsi_task_lun_process_none( &task->scsi_task ); - rc = ISCSI_CONNECT_PDU_READ_OK; + iscsi_scsi_task_status_set( &task->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 ); + rc = ISCSI_SCSI_TASK_RUN_COMPLETE; } else { task->scsi_task.status = ISCSI_SCSI_STATUS_GOOD; @@ -3271,12 +3158,11 @@ static int iscsi_connection_handle_scsi_cmd(iscsi_connection *conn, iscsi_pdu *r * which may NOT be NULL, so take caution. * @return 0 on success, a negative error code otherwise. */ -static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_negotiation_kvp *kvpairs) +static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, const iscsi_pdu *login_response_pdu, const iscsi_negotiation_kvp *kvpairs) { - int type, rc; + int type; iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - - rc = iscsi_login_parse_session_type( login_response_pdu, kvpairs->SessionType, &type ); + int rc = iscsi_login_parse_session_type( login_response_pdu, kvpairs->SessionType, &type ); if ( rc < 0 ) return rc; @@ -3333,7 +3219,7 @@ static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscs * @return The updated payload length of the response PDU if successful. * Returns -1 if an error occurs during key-value pair appending. */ -static int iscsi_write_login_options_to_pdu( iscsi_connection *conn, iscsi_negotiation_kvp *pairs, iscsi_pdu *response_pdu ) +static int iscsi_write_login_options_to_pdu(const iscsi_connection *conn, const iscsi_negotiation_kvp *pairs, iscsi_pdu *response_pdu) { uint payload_len = response_pdu->ds_write_pos; @@ -3383,9 +3269,10 @@ if ( pairs->key != NULL ) ADD_KV_INTERNAL( false, #key, value ); \ * may NOT be NULL, so be careful. * @param[in] login_response_pdu Pointer to login response PDU. * NULL is not allowed here, so take caution. + * @param[in] pairs Readily parsed key-value-pairs from according request * @return 0 on success, a negative error code otherwise. */ -static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_negotiation_kvp *pairs) +static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi_pdu *login_response_pdu, const iscsi_negotiation_kvp *pairs) { if ( iscsi_connection_pdu_resize( login_response_pdu, 0, ISCSI_DEFAULT_RECV_DS_LEN ) == NULL ) { return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; @@ -3467,14 +3354,11 @@ static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi * NOT be NULL, so take caution. * @param[in] request_pdu Pointer to iSCSI client request PDU to handle. * May be NULL in which case an error is returned. - * @param login_response_pdu * @return 0 on success. A negative value indicates * an error. A positive value a warning. */ static int iscsi_connection_handle_login_req(iscsi_connection *conn, iscsi_pdu *request_pdu) { - int rc; - if ( request_pdu->ds_len > ISCSI_DEFAULT_RECV_DS_LEN || conn->state != ISCSI_CONNECT_STATE_NEW ) return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); @@ -3486,7 +3370,7 @@ static int iscsi_connection_handle_login_req(iscsi_connection *conn, iscsi_pdu * if ( !iscsi_connection_pdu_init( &login_response_pdu, 0, false ) ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - rc = iscsi_connection_pdu_login_response_init( &login_response_pdu, request_pdu ); + int rc = iscsi_connection_pdu_login_response_init( &login_response_pdu, request_pdu ); if ( rc < 0 ) { // response_init set an error code in the response pdu, send it right away and bail out @@ -3529,7 +3413,7 @@ static int iscsi_connection_handle_login_req(iscsi_connection *conn, iscsi_pdu * * @return 0 on success. A negative value indicates * an error. A positive value a warning. */ -static int iscsi_connection_handle_text_req(iscsi_connection *conn, iscsi_pdu *request_pdu) +static int iscsi_connection_handle_text_req(iscsi_connection *conn, const iscsi_pdu *request_pdu) { iscsi_text_req_packet *text_req_pkt = (iscsi_text_req_packet *) request_pdu->bhs_pkt; @@ -3701,7 +3585,7 @@ static void iscsi_connection_pdu_read_loop(iscsi_connection *conn, const dnbd3_r // 1) Receive BHS (partially already received, in "request", merge and finish) memcpy( request_pdu.bhs_pkt, request, len ); - if ( sock_recv( conn->client->sock, ((uint8_t *)request_pdu.bhs_pkt) + len, sizeof(iscsi_bhs_packet) - len ) + if ( (size_t)sock_recv( conn->client->sock, ((uint8_t *)request_pdu.bhs_pkt) + len, sizeof(iscsi_bhs_packet) - len ) != sizeof(iscsi_bhs_packet) - len ) { logadd( LOG_INFO, "Cannot receive first BHS for client %s", conn->client->hostName ); return; @@ -3778,7 +3662,7 @@ static void iscsi_connection_pdu_read_loop(iscsi_connection *conn, const dnbd3_r */ 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), + _Static_assert( sizeof(dnbd3_request_t) <= sizeof(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 -- cgit v1.2.3-55-g7522 From af451c397c267298ea2396f5e66cbd174188c02a Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 6 Nov 2025 13:59:06 +0100 Subject: [SERVER] iscsi: Add bytesSent accounting, set thread name --- src/server/iscsi.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 5657710..21980bf 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -561,6 +561,8 @@ static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, const iscsi_task max_burst_offset += max_burst_len; } + conn->client->bytesSent += xfer_len; + return (status & ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS); } @@ -3327,6 +3329,9 @@ static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; } + char tname[50]; + snprintf( tname, sizeof(tname), "i%s", conn->client->hostName ); + setThreadName( tname ); break; } -- cgit v1.2.3-55-g7522 From f8dc80b2552feef91743c0237117908be9711390 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 6 Nov 2025 19:54:03 +0100 Subject: [SERVER] iscsi: Overhaul sending of SCSI response, and DATA-In loop --- src/server/iscsi.c | 312 ++++++++++++++++++++++------------------------------- src/server/iscsi.h | 3 - 2 files changed, 131 insertions(+), 184 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 21980bf..8593c8c 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "sendfile.h" #include "globals.h" @@ -70,14 +71,11 @@ /// Use for stack-allocated iscsi_pdu #define CLEANUP_PDU __attribute__((cleanup(iscsi_connection_pdu_destroy))) -static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task); +static bool iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task); -static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task); +static bool iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task); - -static void iscsi_scsi_task_init(iscsi_scsi_task *scsi_task); // Initializes a SCSI task - -static void iscsi_scsi_task_xfer_complete(iscsi_connection *conn, iscsi_scsi_task *scsi_task, const iscsi_pdu *request_pdu); // Callback function when an iSCSI SCSI task completed the data transfer +static void iscsi_scsi_task_send_reply(iscsi_connection *conn, iscsi_scsi_task *scsi_task, const iscsi_pdu *request_pdu); static uint64_t iscsi_scsi_lun_get_from_scsi(int lun_id); // Converts an internal representation of a LUN identifier to an iSCSI LUN required for packet data static int iscsi_scsi_lun_get_from_iscsi(uint64_t lun); // Converts an iSCSI LUN from packet data to internal SCSI LUN identifier @@ -331,10 +329,7 @@ static int iscsi_parse_login_key_value_pairs(iscsi_negotiation_kvp *pairs, const * @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. + * SCSI task structure.\n * * @param[in] conn Pointer to iSCSI connection to associate * the task with. May NOT be NULL, so take @@ -357,7 +352,17 @@ static iscsi_task *iscsi_task_create(iscsi_connection *conn) task->init_task_tag = 0UL; task->target_xfer_tag = 0UL; - iscsi_scsi_task_init( &task->scsi_task ); + task->scsi_task.cdb = NULL; + task->scsi_task.sense_data = NULL; + task->scsi_task.buf = NULL; + task->scsi_task.len = 0UL; + task->scsi_task.id = 0ULL; + task->scsi_task.is_read = false; + task->scsi_task.is_write = false; + task->scsi_task.exp_xfer_len = 0UL; + task->scsi_task.sense_data_len = 0U; + task->scsi_task.status = ISCSI_SCSI_STATUS_GOOD; + task->scsi_task.connection = conn; return task; @@ -376,9 +381,7 @@ static void iscsi_task_destroy(iscsi_task *task) if ( task == NULL ) return; - if ( task->scsi_task.must_free ) { - free( task->scsi_task.buf ); - } + free( task->scsi_task.buf ); free( task->scsi_task.sense_data ); free( task ); } @@ -414,32 +417,25 @@ static bool iscsi_scsi_data_in_send(iscsi_connection *conn, const iscsi_task *ta 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->flags = flags; scsi_data_in_pkt->reserved = 0U; 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 ( !immediate ) { - 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; + if ( (flags & ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_FINAL) != 0 && !immediate ) { + conn->session->max_cmd_sn++; } - scsi_data_in_pkt->status = task->scsi_task.status; iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->stat_sn, conn->stat_sn++ ); + iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->res_cnt, res_cnt ); } else { + // these don't carry any meaning if S bit is unset - saves us from doing the endian-conversion 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; + scsi_data_in_pkt->lun = 0ULL; // Not used if we don't set the A bit (we never do) 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 ); @@ -448,19 +444,19 @@ static bool iscsi_scsi_data_in_send(iscsi_connection *conn, const iscsi_task *ta iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->buf_offset, pos ); - iscsi_connection_pdu_write( conn, &response_pdu ); + if ( !iscsi_connection_pdu_write( conn, &response_pdu ) ) + return false; + size_t padding; if ( task->scsi_task.buf != NULL ) { if ( !sock_sendAll( conn->client->sock, (task->scsi_task.buf + pos), len, ISCSI_CONNECT_SOCKET_WRITE_RETRIES ) ) return false; - const size_t padding = ISCSI_ALIGN( len, ISCSI_ALIGN_SIZE ) - len; - if ( padding != 0 ) { - if ( !sock_sendPadding( conn->client->sock, padding ) ) - return false; - } + padding = ISCSI_ALIGN( len, ISCSI_ALIGN_SIZE ) - len; } else { + // sendfile - it must be a DATA-In, in which case len should be a multiple of 4, as it was given in number of + // blocks, which is not just a multiple of 4 but usually a power of two. + assert( len % 4 == 0 ); const uint64_t off = task->scsi_task.file_offset + pos; - size_t padding = 0; size_t realBytes = len; if ( off >= conn->client->image->realFilesize ) { padding = len; @@ -468,15 +464,17 @@ static bool iscsi_scsi_data_in_send(iscsi_connection *conn, const iscsi_task *ta } else if ( off + len > conn->client->image->realFilesize ) { padding = ( off + len ) - conn->client->image->realFilesize; realBytes -= padding; + } else { + padding = 0; } bool ret = sendfile_all( conn->client->image->readFd, conn->client->sock, (off_t)off, realBytes ); if ( !ret ) return false; - if ( padding > 0 ) { - if ( !sock_sendPadding( conn->client->sock, padding ) ) - return false; - } + } + if ( padding != 0 ) { + if ( !sock_sendPadding( conn->client->sock, padding ) ) + return false; } return true; @@ -496,14 +494,10 @@ static bool iscsi_scsi_data_in_send(iscsi_connection *conn, const iscsi_task *ta * the incoming data. NULL is NOT allowed here, * take caution. * @param immediate - * @return 0 on successful incoming transfer handling, - * a negative error code otherwise. + * @return true on success, false on error */ -static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, const iscsi_task *task, bool immediate) +static bool iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, const iscsi_task *task, bool immediate) { - if ( task->scsi_task.status != ISCSI_SCSI_STATUS_GOOD ) - return 0; - const uint32_t expected_len = task->scsi_task.exp_xfer_len; uint32_t xfer_len = task->scsi_task.len; uint32_t res_cnt = 0UL; @@ -517,99 +511,68 @@ static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, const iscsi_task res_cnt = (expected_len - xfer_len); flags |= ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_UNDERFLOW; } - if ( xfer_len == 0UL ) - return 0; + if ( xfer_len == 0UL ) { + // Can this even happen? Send empty data-in response... + flags |= ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_FINAL | ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS; + return iscsi_scsi_data_in_send( conn, task, 0, 0, res_cnt, 0, flags, immediate ); + } uint32_t data_sn = 0; - uint32_t max_burst_offset = 0UL; - // Max burst length = total length of payload in all PDUs + // Max burst length = total cobined length of payload in all PDUs of one sequence const uint32_t max_burst_len = conn->session->opts.MaxBurstLength; // Max recv segment length = total length of one individual PDU - const uint32_t seg_len = conn->session->opts.MaxRecvDataSegmentLength; - const uint32_t data_in_seq_count = ((xfer_len - 1) / max_burst_len) + 1; - uint8_t status = 0; + const uint32_t max_seg_len = conn->session->opts.MaxRecvDataSegmentLength; - for ( uint32_t i = 0UL; i < data_in_seq_count; i++ ) { - uint32_t seq_end = (max_burst_offset + max_burst_len); + for ( uint32_t current_burst_start = 0; current_burst_start < xfer_len; current_burst_start += max_burst_len ) { + const uint32_t current_burst_end = MIN(xfer_len, current_burst_start + max_burst_len); - if ( seq_end > xfer_len ) - seq_end = xfer_len; + for ( uint32_t offset = current_burst_start; offset < current_burst_end; offset += max_seg_len ) { + const uint32_t current_seg_len = MIN(max_seg_len, current_burst_end - offset); - for ( uint32_t offset = max_burst_offset; offset < seq_end; offset += seg_len ) { - uint32_t len = (seq_end - offset); + flags &= ~(ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS | ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_FINAL); - if ( len > seg_len ) - len = seg_len; + if ( (offset + current_seg_len) == current_burst_end ) { + // This segment ends the current sequence - set F bit + flags |= ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_FINAL; - 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) ) { - flags |= (int8_t) ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS; - status |= flags; + if ( (offset + current_seg_len) == xfer_len ) { + // This segment ends the entire transfer - set S bit and include status + flags |= ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS; } } - if ( !iscsi_scsi_data_in_send( conn, task, offset, len, res_cnt, data_sn, flags, immediate ) ) - return -1; + if ( !iscsi_scsi_data_in_send( conn, task, offset, current_seg_len, res_cnt, data_sn, flags, immediate ) ) + return false; data_sn++; } - - max_burst_offset += max_burst_len; } conn->client->bytesSent += xfer_len; - return (status & ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS); -} - -/** - * @brief Initializes a SCSI task. - * - * @param[in] scsi_task Pointer to SCSI task. This - * may NOT be NULL, so be careful. - */ -static void iscsi_scsi_task_init(iscsi_scsi_task *scsi_task) -{ - scsi_task->cdb = NULL; - scsi_task->sense_data = NULL; - scsi_task->buf = NULL; - scsi_task->must_free = true; - scsi_task->len = 0UL; - scsi_task->id = 0ULL; - scsi_task->is_read = false; - scsi_task->is_write = false; - scsi_task->exp_xfer_len = 0UL; - scsi_task->sense_data_len = 0U; - scsi_task->status = ISCSI_SCSI_STATUS_GOOD; + return true; } /** - * @brief Callback function when an iSCSI SCSI task completed the data transfer. - * - * This function post-processes a task upon - * finish of data transfer. + * @brief Send reply to a n iSCSI SCSI op. + * Called when the request has been handled and the task is set up properly + * with the according data to reply with. This is either payload data, for + * a transfer, or sense data. * * @param[in] conn Current connection - * @param[in] scsi_task Pointer to iSCSI SCSI task which finished - * the data transfer and may NOT be NULL, - * so be careful. - * @param request_pdu + * @param[in] scsi_task Pointer to iSCSI SCSI task to send out a response for + * @param request_pdu The request belonging to the response to send */ -static void iscsi_scsi_task_xfer_complete(iscsi_connection *conn, iscsi_scsi_task *scsi_task, const iscsi_pdu *request_pdu) +static void iscsi_scsi_task_send_reply(iscsi_connection *conn, iscsi_scsi_task *scsi_task, const iscsi_pdu *request_pdu) { iscsi_task *task = container_of( scsi_task, iscsi_task, scsi_task ); iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) request_pdu->bhs_pkt; - if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_READ) != 0 ) { - const int rc = iscsi_task_xfer_scsi_data_in( conn, task, (scsi_cmd_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) != 0 ); + if ( task->scsi_task.status == ISCSI_SCSI_STATUS_GOOD && scsi_task->sense_data_len == 0 && scsi_task->is_read ) { + iscsi_task_xfer_scsi_data_in( conn, task, (scsi_cmd_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) != 0 ); - if ( rc > 0 ) - return; + return; } const uint32_t ds_len = (scsi_task->sense_data_len != 0U) @@ -634,9 +597,9 @@ static void iscsi_scsi_task_xfer_complete(iscsi_connection *conn, iscsi_scsi_tas } scsi_response_pkt->opcode = ISCSI_OPCODE_SERVER_SCSI_RESPONSE; - scsi_response_pkt->flags = -0x80; + scsi_response_pkt->flags = 0x80; scsi_response_pkt->response = ISCSI_SCSI_RESPONSE_CODE_OK; - const uint32_t exp_xfer_len = scsi_task->exp_xfer_len; + const uint32_t exp_xfer_len = scsi_task->exp_xfer_len; if ( (exp_xfer_len != 0UL) && (scsi_task->status == ISCSI_SCSI_STATUS_GOOD) ) { const uint32_t resp_len = ds_len; @@ -953,49 +916,45 @@ static int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, dnbd3_imag * @param[in] lba Logical Block Address (LBA) to start * reading from or writing to. * @param[in] xfer_len Transfer length in logical blocks. - * @return 0 on successful operation, a negative - * error code otherwise. */ -static int iscsi_scsi_emu_block_read(dnbd3_image_t *image, iscsi_scsi_task *scsi_task, const uint64_t lba, const uint32_t xfer_len) +static void iscsi_scsi_emu_block_read(dnbd3_image_t *image, iscsi_scsi_task *scsi_task, const uint64_t lba, const uint32_t xfer_len) { - if ( xfer_len == 0UL ) { - scsi_task->status = ISCSI_SCSI_STATUS_GOOD; - - return ISCSI_SCSI_TASK_RUN_COMPLETE; - } - const uint32_t max_xfer_len = ISCSI_MAX_DS_SIZE / ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE; if ( xfer_len > max_xfer_len || !scsi_task->is_read || scsi_task->is_write ) { 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; + return; + } + + if ( xfer_len == 0UL ) { + scsi_task->status = ISCSI_SCSI_STATUS_GOOD; + + return; } int rc = iscsi_scsi_emu_io_blocks_read( scsi_task, image, lba, xfer_len ); if ( rc == 0 ) - return ISCSI_SCSI_TASK_RUN_COMPLETE; + return; if ( rc == -ENOMEM ) { iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_HARDWARE_ERR, ISCSI_SCSI_ASC_INTERNAL_TARGET_FAIL, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE ); - return ISCSI_SCSI_TASK_RUN_COMPLETE; + return; } if ( rc == -ERANGE ) { 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; + return; } 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; } /** @@ -1007,14 +966,13 @@ static int iscsi_scsi_emu_block_read(dnbd3_image_t *image, iscsi_scsi_task *scsi * @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. + * @return true on successful operation, false otherwise. */ -static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) +static bool iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) { uint64_t lba; uint32_t xfer_len; - dnbd3_image_t *image = scsi_task->connection->client->image; + const dnbd3_image_t *image = scsi_task->connection->client->image; switch ( scsi_task->cdb->opcode ) { case ISCSI_SCSI_OPCODE_READ6 : { @@ -1026,7 +984,9 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) if ( xfer_len == 0UL ) xfer_len = 256UL; - return iscsi_scsi_emu_block_read( image, scsi_task, lba, xfer_len ); + iscsi_scsi_emu_block_read( image, scsi_task, lba, xfer_len ); + + 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; @@ -1034,7 +994,9 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) 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( image, scsi_task, lba, xfer_len ); + iscsi_scsi_emu_block_read( image, scsi_task, lba, xfer_len ); + + 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; @@ -1042,7 +1004,9 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) 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( image, scsi_task, lba, xfer_len ); + iscsi_scsi_emu_block_read( image, scsi_task, lba, xfer_len ); + + 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; @@ -1050,15 +1014,18 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) 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( image, scsi_task, lba, xfer_len ); + iscsi_scsi_emu_block_read( image, scsi_task, lba, xfer_len ); + + break; } case ISCSI_SCSI_OPCODE_READCAPACITY10 : { iscsi_scsi_read_capacity_10_parameter_data_packet *buf = malloc( sizeof(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 ); + 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; + break; } lba = iscsi_scsi_emu_block_get_count( image ) - 1ULL; @@ -1081,7 +1048,7 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) if ( ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_GET_ACTION(cdb_servce_in_action_16->action) != ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_ACTION_READ_CAPACITY_16 ) { - return ISCSI_SCSI_TASK_RUN_UNKNOWN; + return false; } iscsi_scsi_service_action_in_16_parameter_data_packet *buf = malloc( sizeof(iscsi_scsi_service_action_in_16_parameter_data_packet) ); @@ -1089,7 +1056,7 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) 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; + break; } lba = iscsi_scsi_emu_block_get_count( image ) - 1ULL; @@ -1131,13 +1098,11 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) break; } default : { - return ISCSI_SCSI_TASK_RUN_UNKNOWN; - - break; + return false; } } - return ISCSI_SCSI_TASK_RUN_COMPLETE; + return true; } /** @@ -1995,10 +1960,9 @@ static uint32_t iscsi_get_temporary_allocation_size(iscsi_scsi_task *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. + * @return true on successful operation, false otherwise. */ -static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) +static bool iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) { uint len; int rc; @@ -2148,13 +2112,11 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) break; } default : { - return ISCSI_SCSI_TASK_RUN_UNKNOWN; - - break; + return false; } } - return ISCSI_SCSI_TASK_RUN_COMPLETE; + return true; } /** @@ -3078,15 +3040,8 @@ static int iscsi_connection_handle_nop_out(iscsi_connection *conn, const iscsi_p */ static int iscsi_connection_handle_scsi_cmd(iscsi_connection *conn, const iscsi_pdu *request_pdu) { + bool handled = false; iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) request_pdu->bhs_pkt; - - if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_WRITE) != 0 ) { // Bidirectional transfer is not supported - logadd( LOG_DEBUG1, "Received SCSI write command from %s", conn->client->hostName ); - // Should really return a write protect error on SCSI layer, but a well-behaving client shouldn't ever - // send a write command anyways, since we declare the device read only. - return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_COMMAND_NOT_SUPPORTED ); - } - iscsi_task *task = iscsi_task_create( conn ); if ( task == NULL ) { @@ -3102,45 +3057,40 @@ static int iscsi_connection_handle_scsi_cmd(iscsi_connection *conn, const iscsi_ const uint64_t lun = iscsi_get_be64(scsi_cmd_pkt->lun); task->lun_id = iscsi_scsi_lun_get_from_iscsi( lun ); - if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_READ) == 0 ) { + if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_READ) != 0 ) { + task->scsi_task.is_read = true; + } else { if ( exp_xfer_len != 0UL ) { // Not a read request, but expecting data - not valid - iscsi_scsi_task_status_set( &task->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 ); - iscsi_task_destroy( task ); - - return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_INVALID_PDU_FIELD ); + iscsi_scsi_task_status_set( &task->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 ); + handled = true; } - } else { - task->scsi_task.is_read = true; } - task->scsi_task.is_write = (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_WRITE) != 0; - int rc; - - if ( task->lun_id != ISCSI_DEFAULT_LUN ) { - logadd( LOG_WARNING, "Received SCSI command for unknown LUN %d", task->lun_id ); - iscsi_scsi_task_status_set( &task->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 ); - rc = ISCSI_SCSI_TASK_RUN_COMPLETE; - } else { - task->scsi_task.status = ISCSI_SCSI_STATUS_GOOD; + if ( !handled ) { + task->scsi_task.is_write = (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_WRITE) != 0; - rc = iscsi_scsi_emu_block_process( &task->scsi_task ); + if ( task->lun_id != ISCSI_DEFAULT_LUN ) { + logadd( LOG_WARNING, "Received SCSI command for unknown LUN %d", task->lun_id ); + iscsi_scsi_task_status_set( &task->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 ); + handled = true; + } else { + task->scsi_task.status = ISCSI_SCSI_STATUS_GOOD; - if ( rc == ISCSI_SCSI_TASK_RUN_UNKNOWN ) { - rc = iscsi_scsi_emu_primary_process( &task->scsi_task ); + handled = iscsi_scsi_emu_block_process( &task->scsi_task ) + || iscsi_scsi_emu_primary_process( &task->scsi_task ); - if ( rc == ISCSI_SCSI_TASK_RUN_UNKNOWN ) { - iscsi_scsi_task_status_set( &task->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 ); - rc = ISCSI_SCSI_TASK_RUN_COMPLETE; + if ( !handled ) { + iscsi_scsi_task_status_set( &task->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 ); } } } - if ( rc == ISCSI_SCSI_TASK_RUN_COMPLETE ) { - iscsi_scsi_task_xfer_complete( conn, &task->scsi_task, request_pdu ); - } - + iscsi_scsi_task_send_reply( conn, &task->scsi_task, request_pdu ); iscsi_task_destroy( task ); return ISCSI_CONNECT_PDU_READ_OK; diff --git a/src/server/iscsi.h b/src/server/iscsi.h index a806530..4e4f485 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -5765,9 +5765,6 @@ typedef struct iscsi_scsi_task { /// Output buffer. uint8_t *buf; - /// Whether output buffer os owned by this struct and must be freed on destroy - bool must_free; - /// Offset in bytes in image for DATA-in command. size_t file_offset; -- cgit v1.2.3-55-g7522 From 1dd80ab2cae549be1cefa955d9ab55647979c8ea Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 6 Nov 2025 20:09:45 +0100 Subject: [SERVER] iscsi: Make iscsi_task stack-allocated Saves another round of malloc/free calls on every request. --- src/server/iscsi.c | 112 +++++++++++------------------------------------------ src/server/iscsi.h | 4 +- 2 files changed, 25 insertions(+), 91 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 8593c8c..400c90c 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -84,9 +84,6 @@ static int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, dnbd3_imag static void iscsi_strcpy_pad(char *dst, const char *src, size_t size, int pad); // Copies a string with additional padding character to fill in a specified size -static iscsi_task *iscsi_task_create(iscsi_connection *conn); // Allocates and initializes an iSCSI task structure -static void iscsi_task_destroy(iscsi_task *task); // Deallocates resources acquired by iscsi_task_create - static uint64_t iscsi_target_node_wwn_get(const uint8_t *name); // Calculates the WWN using 64-bit IEEE Extended NAA for a name static iscsi_session *iscsi_session_create(int type); // Creates and initializes an iSCSI session @@ -325,67 +322,6 @@ static int iscsi_parse_login_key_value_pairs(iscsi_negotiation_kvp *pairs, const return 0; } -/** - * @brief Allocates and initializes an iSCSI task structure. - * - * This function also initializes the underlying - * SCSI task structure.\n - * - * @param[in] conn Pointer to iSCSI connection to associate - * the task with. May NOT be NULL, so take - * caution. - * @return Pointer to iSCSI task structure or NULL - * in case of an error (memory exhaustion). - */ -static iscsi_task *iscsi_task_create(iscsi_connection *conn) -{ - iscsi_task *task = malloc( sizeof(iscsi_task) ); - - if ( task == NULL ) { - logadd( LOG_ERROR, "iscsi_task_create: Out of memory while allocating iSCSI task" ); - - return NULL; - } - - task->len = 0UL; - task->lun_id = 0; - task->init_task_tag = 0UL; - task->target_xfer_tag = 0UL; - - task->scsi_task.cdb = NULL; - task->scsi_task.sense_data = NULL; - task->scsi_task.buf = NULL; - task->scsi_task.len = 0UL; - task->scsi_task.id = 0ULL; - task->scsi_task.is_read = false; - task->scsi_task.is_write = false; - task->scsi_task.exp_xfer_len = 0UL; - task->scsi_task.sense_data_len = 0U; - task->scsi_task.status = ISCSI_SCSI_STATUS_GOOD; - - task->scsi_task.connection = conn; - - return 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. - */ -static void iscsi_task_destroy(iscsi_task *task) -{ - if ( task == NULL ) - return; - - free( task->scsi_task.buf ); - free( task->scsi_task.sense_data ); - free( task ); -} - /** * @brief Sends a single iSCSI SCSI Data In packet to the client. * @@ -972,7 +908,7 @@ static bool iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) { uint64_t lba; uint32_t xfer_len; - const dnbd3_image_t *image = scsi_task->connection->client->image; + dnbd3_image_t *image = scsi_task->connection->client->image; switch ( scsi_task->cdb->opcode ) { case ISCSI_SCSI_OPCODE_READ6 : { @@ -3042,56 +2978,54 @@ static int iscsi_connection_handle_scsi_cmd(iscsi_connection *conn, const iscsi_ { bool handled = false; iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) request_pdu->bhs_pkt; - iscsi_task *task = iscsi_task_create( conn ); - - if ( task == NULL ) { - return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_OUT_OF_RESOURCES ); - } - uint32_t exp_xfer_len = iscsi_get_be32(scsi_cmd_pkt->exp_xfer_len); + iscsi_task task = { + .lun_id = iscsi_scsi_lun_get_from_iscsi( iscsi_get_be64(scsi_cmd_pkt->lun) ), + .init_task_tag = iscsi_get_be32(scsi_cmd_pkt->init_task_tag), - task->scsi_task.cdb = &scsi_cmd_pkt->scsi_cdb; - task->scsi_task.exp_xfer_len = exp_xfer_len; - task->init_task_tag = iscsi_get_be32(scsi_cmd_pkt->init_task_tag); - - const uint64_t lun = iscsi_get_be64(scsi_cmd_pkt->lun); - task->lun_id = iscsi_scsi_lun_get_from_iscsi( lun ); + .scsi_task.cdb = &scsi_cmd_pkt->scsi_cdb, + .scsi_task.exp_xfer_len = exp_xfer_len, + .scsi_task.status = ISCSI_SCSI_STATUS_GOOD, + .scsi_task.connection = conn, + }; if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_READ) != 0 ) { - task->scsi_task.is_read = true; + task.scsi_task.is_read = true; } else { if ( exp_xfer_len != 0UL ) { // Not a read request, but expecting data - not valid - iscsi_scsi_task_status_set( &task->scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, + iscsi_scsi_task_status_set( &task.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 ); handled = true; } } if ( !handled ) { - task->scsi_task.is_write = (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_WRITE) != 0; + task.scsi_task.is_write = (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_WRITE) != 0; - if ( task->lun_id != ISCSI_DEFAULT_LUN ) { - logadd( LOG_WARNING, "Received SCSI command for unknown LUN %d", task->lun_id ); - iscsi_scsi_task_status_set( &task->scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, + if ( task.lun_id != ISCSI_DEFAULT_LUN ) { + logadd( LOG_WARNING, "Received SCSI command for unknown LUN %d", task.lun_id ); + iscsi_scsi_task_status_set( &task.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 ); handled = true; } else { - task->scsi_task.status = ISCSI_SCSI_STATUS_GOOD; + task.scsi_task.status = ISCSI_SCSI_STATUS_GOOD; - handled = iscsi_scsi_emu_block_process( &task->scsi_task ) - || iscsi_scsi_emu_primary_process( &task->scsi_task ); + handled = iscsi_scsi_emu_block_process( &task.scsi_task ) + || iscsi_scsi_emu_primary_process( &task.scsi_task ); if ( !handled ) { - iscsi_scsi_task_status_set( &task->scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, + iscsi_scsi_task_status_set( &task.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 ); } } } - iscsi_scsi_task_send_reply( conn, &task->scsi_task, request_pdu ); - iscsi_task_destroy( task ); + iscsi_scsi_task_send_reply( conn, &task.scsi_task, request_pdu ); + // Free any buffers that were allocated for this task + free( task.scsi_task.buf ); + free( task.scsi_task.sense_data ); return ISCSI_CONNECT_PDU_READ_OK; } diff --git a/src/server/iscsi.h b/src/server/iscsi.h index 4e4f485..809c141 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -5759,10 +5759,10 @@ typedef struct iscsi_scsi_task { /// SCSI Command Descriptor Block (CDB). iscsi_scsi_cdb *cdb; - /// SCSI sense data. + /// SCSI sense data. If set, owned by this struct. iscsi_scsi_sense_data_packet *sense_data; - /// Output buffer. + /// Output buffer. If set, owned by this struct. uint8_t *buf; /// Offset in bytes in image for DATA-in command. -- cgit v1.2.3-55-g7522 From 79e9fedc148d9287ab5267e5d83938e0555440bf Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Fri, 7 Nov 2025 14:28:56 +0100 Subject: [SERVER] iscsi: Make kernel fast again --- src/server/iscsi.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 400c90c..45f5ef2 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -1315,12 +1315,27 @@ static int iscsi_scsi_emu_primary_inquiry(const dnbd3_image_t *image, iscsi_scsi vpd_page_block_limits_inquiry_data_pkt->flags = 0; - // Calculate maximum number of logical blocks that would fit into a maximum-size transfer (16MiB), - // but make sure it is a multiple of the physical block size - const uint32_t blocks = ((ISCSI_MAX_DS_SIZE / ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE) - * ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE) / ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE; - - vpd_page_block_limits_inquiry_data_pkt->max_cmp_write_len = (uint8_t) blocks; + // So, this has caused some headache, nice. With the kernel's iscsi implementation, we have to limit our + // reported maximum supported transfer length to the client's maximum supported DS size. If you just do + // ISCSI_MAX_DS_SIZE / ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE + // the kernel will complain that the maximum transfer size is not a multiple of the physical block size, + // so you might be tempted to use + // ((ISCSI_MAX_DS_SIZE / ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE) + // * ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE) / ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE + // to make sure it is. But then *surprise*, maximum transfer speeds drop from ~1.5GB/s to ~250MB/s. + // OK, then you revert back to the simple formula and accept that annoying warning in dmesg, only to + // realize that while "pv < /dev/sda > /dev/null" and dd with bs=256k are fast, a dd with bs=1M ends up + // at about 25MB/s (!!!!) + // So what finally, hopefully, seems to work properly is limiting the reported maximum transfer length to + // the client's MaxRecvDataSegmentLength, which coincidentally is the same as its FirstBurstLength, so + // let's hope picking MaxRecvDataSegmentLength is the right choice here. You'd think the client would + // automatically pick a suitable transfer length that it can handle efficiently; the kernel however just + // goes for the maximum supported by the server. Even just lowering the reported *optimal* length is not + // sufficient. But maybe I'm just not good with computers. + const uint32_t blocks = (scsi_task->connection->session->opts.MaxRecvDataSegmentLength + / ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE); + + vpd_page_block_limits_inquiry_data_pkt->max_cmp_write_len = 0; iscsi_put_be16( (uint8_t *) &vpd_page_block_limits_inquiry_data_pkt->optimal_granularity_xfer_len, (uint16_t) ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE / ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE ); @@ -1333,7 +1348,7 @@ static int iscsi_scsi_emu_primary_inquiry(const dnbd3_image_t *image, iscsi_scsi 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->max_write_same_len = 0; 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; -- cgit v1.2.3-55-g7522 From 98f0a4c103ff24cfbfaef534cff0e7af8b657fe2 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Fri, 31 Oct 2025 14:00:31 +0100 Subject: [SERVER] iscsi: Fix endianness bugs in ACTION(16) and rotation rate --- src/server/iscsi.c | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 45f5ef2..87c0a74 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -1010,14 +1010,10 @@ static bool iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) 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 - } + const uint alloc_len = iscsi_get_be32( cdb_servce_in_action_16->alloc_len ); scsi_task->buf = (uint8_t *) buf; - scsi_task->len = len; + scsi_task->len = MIN( alloc_len, sizeof(*buf) ); scsi_task->status = ISCSI_SCSI_STATUS_GOOD; break; @@ -1358,7 +1354,7 @@ static int iscsi_scsi_emu_primary_inquiry(const dnbd3_image_t *image, iscsi_scsi 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; + iscsi_scsi_vpd_page_block_dev_chars_inquiry_data_packet *chars_resp = (iscsi_scsi_vpd_page_block_dev_chars_inquiry_data_packet *) vpd_page_inquiry_data_pkt->params; if ( len < (sizeof(iscsi_scsi_vpd_page_inquiry_data_packet) + sizeof(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 ); @@ -1366,21 +1362,21 @@ static int iscsi_scsi_emu_primary_inquiry(const dnbd3_image_t *image, iscsi_scsi return -1; } - alloc_len = sizeof(iscsi_scsi_vpd_page_block_dev_chars_inquiry_data_packet); - - vpd_page_block_dev_chars_inquiry_data_pkt->medium_rotation_rate = ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_MEDIUM_ROTATION_RATE_NONE; - 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; + alloc_len = sizeof(*chars_resp); + + iscsi_put_be16( (uint8_t *)&chars_resp->medium_rotation_rate, ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_MEDIUM_ROTATION_RATE_NONE ); + chars_resp->product_type = ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_PRODUCT_TYPE_NOT_INDICATED; + chars_resp->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); + chars_resp->support_flags = 0U; + chars_resp->reserved[0] = 0ULL; + chars_resp->reserved[1] = 0ULL; + chars_resp->reserved[2] = 0ULL; + chars_resp->reserved[3] = 0ULL; + chars_resp->reserved[4] = 0ULL; + chars_resp->reserved[5] = 0ULL; + chars_resp->reserved2 = 0UL; + chars_resp->reserved3 = 0U; + chars_resp->reserved4 = 0U; iscsi_put_be16( (uint8_t *) &vpd_page_inquiry_data_pkt->alloc_len, (uint16_t) alloc_len ); -- cgit v1.2.3-55-g7522 From 8fe443a5d82568d33d27f2bf9fc946e6324d46aa Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 11 Nov 2025 09:49:45 +0100 Subject: [SERVER] iscsi: Report read cache enabled; report maximum prefetch ctld is also reporting maximum prefetch values, so let's just do the same. --- src/server/iscsi.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 87c0a74..f1199d6 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -1641,14 +1641,20 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc 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 *) buffer; + iscsi_scsi_mode_sense_caching_mode_page_data_packet *cache_page = (iscsi_scsi_mode_sense_caching_mode_page_data_packet *) buffer; - page_len = sizeof(iscsi_scsi_mode_sense_caching_mode_page_data_packet); + page_len = sizeof(*cache_page); iscsi_scsi_emu_primary_mode_sense_page_init( buffer, page_len, page, sub_page ); - if ( (buffer != 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; + if ( cache_page != NULL ) { + cache_page->flags |= ISCSI_SCSI_MODE_SENSE_CACHING_MODE_PAGE_FLAGS_DISC; + // 0xffff is endian-agnostic, don't need to convert + cache_page->disable_prefetch_xfer_len = 0xffff; + cache_page->min_prefetch = 0xffff; + cache_page->max_prefetch = 0xffff; + cache_page->max_prefetch_ceil = 0xffff; + } len += page_len; -- cgit v1.2.3-55-g7522 From 33edafd62e759e944e7d01ed23ccbb45cc0b904e Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 11 Nov 2025 13:42:16 +0100 Subject: [SERVER] iscsi: Remove unused defines, move session into connection --- src/server/iscsi.c | 183 ++++------ src/server/iscsi.h | 959 +---------------------------------------------------- 2 files changed, 70 insertions(+), 1072 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index f1199d6..18fefe1 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -86,10 +86,6 @@ static void iscsi_strcpy_pad(char *dst, const char *src, size_t size, int pad); static uint64_t iscsi_target_node_wwn_get(const uint8_t *name); // Calculates the WWN using 64-bit IEEE Extended NAA for a name -static iscsi_session *iscsi_session_create(int type); // Creates and initializes an iSCSI session -static void iscsi_session_destroy(iscsi_session *session); // Deallocates all resources acquired by iscsi_session_create - - static iscsi_connection *iscsi_connection_create(dnbd3_client_t *client); // Creates data structure for an iSCSI connection from iSCSI portal and TCP/IP socket static void iscsi_connection_destroy(iscsi_connection *conn); // Deallocates all resources acquired by iscsi_connection_create @@ -358,7 +354,7 @@ static bool iscsi_scsi_data_in_send(iscsi_connection *conn, const iscsi_task *ta if ( (flags & ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS) != 0 ) { if ( (flags & ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_FINAL) != 0 && !immediate ) { - conn->session->max_cmd_sn++; + conn->max_cmd_sn++; } scsi_data_in_pkt->status = task->scsi_task.status; iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->stat_sn, conn->stat_sn++ ); @@ -374,8 +370,8 @@ static bool iscsi_scsi_data_in_send(iscsi_connection *conn, const iscsi_task *ta scsi_data_in_pkt->lun = 0ULL; // Not used if we don't set the A bit (we never do) 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->exp_cmd_sn, conn->exp_cmd_sn ); + iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->max_cmd_sn, conn->max_cmd_sn ); iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->data_sn, data_sn ); iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->buf_offset, pos ); @@ -455,9 +451,9 @@ static bool iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, const iscsi_tas uint32_t data_sn = 0; // Max burst length = total cobined length of payload in all PDUs of one sequence - const uint32_t max_burst_len = conn->session->opts.MaxBurstLength; + const uint32_t max_burst_len = conn->opts.MaxBurstLength; // Max recv segment length = total length of one individual PDU - const uint32_t max_seg_len = conn->session->opts.MaxRecvDataSegmentLength; + const uint32_t max_seg_len = conn->opts.MaxRecvDataSegmentLength; for ( uint32_t current_burst_start = 0; current_burst_start < xfer_len; current_burst_start += max_burst_len ) { const uint32_t current_burst_end = MIN(xfer_len, current_burst_start + max_burst_len); @@ -564,10 +560,10 @@ static void iscsi_scsi_task_send_reply(iscsi_connection *conn, iscsi_scsi_task * 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++; + conn->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 ); + iscsi_put_be32( (uint8_t *) &scsi_response_pkt->exp_cmd_sn, conn->exp_cmd_sn ); + iscsi_put_be32( (uint8_t *) &scsi_response_pkt->max_cmd_sn, conn->max_cmd_sn ); scsi_response_pkt->exp_data_sn = 0UL; scsi_response_pkt->bidi_read_res_cnt = 0UL; @@ -1328,7 +1324,7 @@ static int iscsi_scsi_emu_primary_inquiry(const dnbd3_image_t *image, iscsi_scsi // automatically pick a suitable transfer length that it can handle efficiently; the kernel however just // goes for the maximum supported by the server. Even just lowering the reported *optimal* length is not // sufficient. But maybe I'm just not good with computers. - const uint32_t blocks = (scsi_task->connection->session->opts.MaxRecvDataSegmentLength + const uint32_t blocks = (scsi_task->connection->opts.MaxRecvDataSegmentLength / ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE); vpd_page_block_limits_inquiry_data_pkt->max_cmp_write_len = 0; @@ -2096,52 +2092,6 @@ static uint64_t iscsi_target_node_wwn_get(const uint8_t *name) return ((value & 0xFFFFFFULL) | 0x2000000347000000ULL | id_a); } -/** - * @brief Creates and initializes an iSCSI session. - * - * This function creates and initializes all relevant - * data structures of an ISCSI session.\n - * Default key and value pairs are created and - * assigned before they are negotiated at the - * login phase. - * - * @param[in] type Session type to initialize the session with. - * @return Pointer to initialized iSCSI session or NULL in case an error - * occured (usually due to memory exhaustion). - */ -static iscsi_session *iscsi_session_create(const int type) -{ - iscsi_session *session = malloc( sizeof(iscsi_session) ); - - if ( session == NULL ) { - logadd( LOG_ERROR, "iscsi_session_create: Out of memory allocating iSCSI session" ); - - return NULL; - } - - session->tsih = 0ULL; - session->type = type; - session->exp_cmd_sn = 0UL; - session->max_cmd_sn = 0UL; - - return session; -} - -/** - * @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 initiator - * port. - * - * @param[in] session Pointer to iSCSI session to be freed. - * May be NULL in which case this function does nothing at all. - */ -static void iscsi_session_destroy(iscsi_session *session) -{ - free( session ); -} - /** * @brief Creates data structure for an iSCSI connection from iSCSI portal and TCP/IP socket. * @@ -2162,19 +2112,15 @@ static iscsi_connection *iscsi_connection_create(dnbd3_client_t *client) return NULL; } - conn->session = NULL; conn->id = 0; conn->client = client; conn->flags = 0; conn->state = ISCSI_CONNECT_STATE_NEW; conn->login_phase = ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION; - 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->stat_sn = 0UL; + conn->exp_cmd_sn = 0UL; + conn->max_cmd_sn = 0UL; return conn; } @@ -2194,10 +2140,7 @@ static iscsi_connection *iscsi_connection_create(dnbd3_client_t *client) */ static void iscsi_connection_destroy(iscsi_connection *conn) { - if ( conn != NULL ) { - iscsi_session_destroy( conn->session ); - free( conn ); - } + free( conn ); } /** @@ -2253,11 +2196,11 @@ static int iscsi_append_key_value_pair_packet(const bool number, const char *key * be updated. @param[in] pairs Set of readily parsed key-value-pairs to handle */ -static void iscsi_connection_update_key_value_pairs(const iscsi_connection *conn, const iscsi_negotiation_kvp *pairs) +static void iscsi_connection_update_key_value_pairs(iscsi_connection *conn, const iscsi_negotiation_kvp *pairs) { - conn->session->opts.MaxBurstLength = CLAMP(pairs->MaxBurstLength, 512, ISCSI_MAX_DS_SIZE); - conn->session->opts.FirstBurstLength = CLAMP(pairs->FirstBurstLength, 512, pairs->MaxBurstLength); - conn->session->opts.MaxRecvDataSegmentLength = CLAMP(pairs->MaxRecvDataSegmentLength, 512, ISCSI_MAX_DS_SIZE); + conn->opts.MaxBurstLength = CLAMP(pairs->MaxBurstLength, 512, ISCSI_MAX_DS_SIZE); + conn->opts.FirstBurstLength = CLAMP(pairs->FirstBurstLength, 512, pairs->MaxBurstLength); + conn->opts.MaxRecvDataSegmentLength = CLAMP(pairs->MaxRecvDataSegmentLength, 512, ISCSI_MAX_DS_SIZE); } /** @@ -2286,9 +2229,9 @@ static int iscsi_send_login_response_pdu(iscsi_connection *conn, iscsi_pdu *resp iscsi_put_be32( (uint8_t *) &login_response_pkt->total_ahs_len, resp_pdu->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 ) { // TODO: Needed? MC/S? - iscsi_put_be32( (uint8_t *) &login_response_pkt->exp_cmd_sn, conn->session->exp_cmd_sn ); - iscsi_put_be32( (uint8_t *) &login_response_pkt->max_cmd_sn, conn->session->max_cmd_sn ); + if ( conn->state != ISCSI_CONNECT_STATE_NEW ) { + iscsi_put_be32( (uint8_t *) &login_response_pkt->exp_cmd_sn, conn->exp_cmd_sn ); + iscsi_put_be32( (uint8_t *) &login_response_pkt->max_cmd_sn, conn->max_cmd_sn ); } else { iscsi_put_be32( (uint8_t *) &login_response_pkt->exp_cmd_sn, resp_pdu->cmd_sn ); iscsi_put_be32( (uint8_t *) &login_response_pkt->max_cmd_sn, resp_pdu->cmd_sn ); @@ -2383,11 +2326,11 @@ static int iscsi_login_parse_session_type(const iscsi_pdu *login_response_pdu, c iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; if ( type_str != NULL && strcasecmp( type_str, "Normal" ) == 0 ) { - *type = ISCSI_SESSION_TYPE_NORMAL; + *type = ISCSI_CONNECT_STATE_NORMAL_SESSION; return ISCSI_CONNECT_PDU_READ_OK; } - *type = ISCSI_SESSION_TYPE_INVALID; + *type = ISCSI_CONNECT_STATE_INVALID; logadd( LOG_DEBUG1, "Unsupported session type: %s", type_str ); 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; @@ -2742,9 +2685,9 @@ static int iscsi_connection_handle_reject(iscsi_connection *conn, const iscsi_pd 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 ); + if ( conn->state != ISCSI_CONNECT_STATE_NEW ) { + iscsi_put_be32( (uint8_t *) &reject_pkt->exp_cmd_sn, conn->exp_cmd_sn ); + iscsi_put_be32( (uint8_t *) &reject_pkt->max_cmd_sn, conn->max_cmd_sn ); } else { iscsi_put_be32( (uint8_t *) &reject_pkt->exp_cmd_sn, 1UL ); iscsi_put_be32( (uint8_t *) &reject_pkt->max_cmd_sn, 1UL ); @@ -2772,11 +2715,9 @@ static int iscsi_connection_handle_reject(iscsi_connection *conn, const iscsi_pd * @return Returns `ISCSI_CONNECT_PDU_READ_OK` (0) on success or * `ISCSI_CONNECT_PDU_READ_ERR_FATAL` (-1) if sequence numbers or other data are invalid. */ -static int iscsi_connection_handle_cmd_sn(const iscsi_connection *conn, iscsi_pdu *request_pdu) +static int iscsi_connection_handle_cmd_sn(iscsi_connection *conn, iscsi_pdu *request_pdu) { - iscsi_session *session = conn->session; - - if ( session == NULL ) + if ( conn->state == ISCSI_CONNECT_STATE_NEW ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) request_pdu->bhs_pkt; @@ -2785,21 +2726,21 @@ static int iscsi_connection_handle_cmd_sn(const iscsi_connection *conn, iscsi_pd request_pdu->cmd_sn = iscsi_get_be32(scsi_cmd_pkt->cmd_sn); if ( (scsi_cmd_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) { - if ( (iscsi_seq_num_cmp_lt( request_pdu->cmd_sn, session->exp_cmd_sn ) - || iscsi_seq_num_cmp_gt( request_pdu->cmd_sn, session->max_cmd_sn )) - && ((session->type == ISCSI_SESSION_TYPE_NORMAL) && (opcode != ISCSI_OPCODE_CLIENT_SCSI_DATA_OUT)) ) { + if ( (iscsi_seq_num_cmp_lt( request_pdu->cmd_sn, conn->exp_cmd_sn ) + || iscsi_seq_num_cmp_gt( request_pdu->cmd_sn, conn->max_cmd_sn )) + && ((conn->state == ISCSI_CONNECT_STATE_NORMAL_SESSION) && (opcode != ISCSI_OPCODE_CLIENT_SCSI_DATA_OUT)) ) { logadd( LOG_WARNING, "Seqnum messup. Is: %u, want >= %u, < %u", - request_pdu->cmd_sn, session->exp_cmd_sn, session->max_cmd_sn ); + request_pdu->cmd_sn, conn->exp_cmd_sn, conn->max_cmd_sn ); return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } - } else if ( (request_pdu->cmd_sn != session->exp_cmd_sn) && (opcode != ISCSI_OPCODE_CLIENT_NOP_OUT) ) { + } else if ( (request_pdu->cmd_sn != conn->exp_cmd_sn) && (opcode != ISCSI_OPCODE_CLIENT_NOP_OUT) ) { logadd( LOG_WARNING, "Seqnum messup. Is: %u, want: %u", - request_pdu->cmd_sn, session->exp_cmd_sn ); + request_pdu->cmd_sn, conn->exp_cmd_sn ); return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } if ( ((scsi_cmd_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0) && (opcode != ISCSI_OPCODE_CLIENT_SCSI_DATA_OUT) ) - session->exp_cmd_sn++; + conn->exp_cmd_sn++; return ISCSI_CONNECT_PDU_READ_OK; } @@ -2823,7 +2764,7 @@ static int iscsi_connection_handle_logout_req(iscsi_connection *conn, const iscs { iscsi_logout_req_packet *logout_req_pkt = (iscsi_logout_req_packet *) request_pdu->bhs_pkt; - if ( (conn->session != NULL) && (conn->session->type == ISCSI_SESSION_TYPE_DISCOVERY) && (logout_req_pkt->reason_code != ISCSI_LOGOUT_REQ_REASON_CODE_CLOSE_SESSION) ) + if ( (conn->state == ISCSI_CONNECT_STATE_NEW) || (logout_req_pkt->reason_code != ISCSI_LOGOUT_REQ_REASON_CODE_CLOSE_SESSION) ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; iscsi_pdu CLEANUP_PDU response_pdu; @@ -2850,11 +2791,11 @@ static int iscsi_connection_handle_logout_req(iscsi_connection *conn, const iscs logout_response_pkt->reserved3 = 0UL; iscsi_put_be32( (uint8_t *) &logout_response_pkt->stat_sn, conn->stat_sn++ ); - if ( conn->session != NULL ) { - conn->session->max_cmd_sn++; + if ( conn->state != ISCSI_CONNECT_STATE_NEW ) { + conn->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 ); + iscsi_put_be32( (uint8_t *) &logout_response_pkt->exp_cmd_sn, conn->exp_cmd_sn ); + iscsi_put_be32( (uint8_t *) &logout_response_pkt->max_cmd_sn, conn->max_cmd_sn ); } else { iscsi_put_be32( (uint8_t *) &logout_response_pkt->exp_cmd_sn, request_pdu->cmd_sn ); iscsi_put_be32( (uint8_t *) &logout_response_pkt->max_cmd_sn, request_pdu->cmd_sn ); @@ -2900,8 +2841,8 @@ static int iscsi_connection_handle_task_func_req(iscsi_connection *conn, const i mgmt_resp->flags = 0x80; mgmt_resp->init_task_tag = mgmt_req->init_task_tag; // Copying over doesn't change endianess. iscsi_put_be32( (uint8_t *) &mgmt_resp->stat_sn, conn->stat_sn++ ); - iscsi_put_be32( (uint8_t *) &mgmt_resp->exp_cmd_sn, conn->session->exp_cmd_sn ); - iscsi_put_be32( (uint8_t *) &mgmt_resp->max_cmd_sn, conn->session->max_cmd_sn ); + iscsi_put_be32( (uint8_t *) &mgmt_resp->exp_cmd_sn, conn->exp_cmd_sn ); + iscsi_put_be32( (uint8_t *) &mgmt_resp->max_cmd_sn, conn->max_cmd_sn ); return iscsi_connection_pdu_write( conn, &response_pdu ) ? 0 : -1; } @@ -2923,7 +2864,7 @@ static int iscsi_connection_handle_task_func_req(iscsi_connection *conn, const i */ static int iscsi_connection_handle_nop_out(iscsi_connection *conn, const iscsi_pdu *request_pdu) { - if ( conn->session->type == ISCSI_SESSION_TYPE_DISCOVERY ) + if ( conn->state != ISCSI_CONNECT_STATE_NORMAL_SESSION ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; if ( request_pdu->ds_len > ISCSI_DEFAULT_MAX_RECV_DS_LEN ) @@ -2943,8 +2884,8 @@ static int iscsi_connection_handle_nop_out(iscsi_connection *conn, const iscsi_p if ( (nop_out_pkt->init_task_tag == 0xFFFFFFFFUL) && (nop_out_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - if ( ds_len > (uint32_t)conn->session->opts.MaxRecvDataSegmentLength ) - ds_len = conn->session->opts.MaxRecvDataSegmentLength; + if ( ds_len > (uint32_t)conn->opts.MaxRecvDataSegmentLength ) + ds_len = conn->opts.MaxRecvDataSegmentLength; iscsi_pdu CLEANUP_PDU response_pdu; if ( !iscsi_connection_pdu_init( &response_pdu, ds_len, false ) ) @@ -2962,10 +2903,10 @@ static int iscsi_connection_handle_nop_out(iscsi_connection *conn, const iscsi_p iscsi_put_be32( (uint8_t *) &nop_in_pkt->stat_sn, conn->stat_sn++ ); if ( (nop_out_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) - conn->session->max_cmd_sn++; + conn->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 ); + iscsi_put_be32( (uint8_t *) &nop_in_pkt->exp_cmd_sn, conn->exp_cmd_sn ); + iscsi_put_be32( (uint8_t *) &nop_in_pkt->max_cmd_sn, conn->max_cmd_sn ); nop_in_pkt->reserved2 = 0UL; nop_in_pkt->reserved3 = 0ULL; @@ -3070,7 +3011,7 @@ static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, cons if ( rc < 0 ) return rc; - if ( type != ISCSI_SESSION_TYPE_NORMAL ) { + if ( type != ISCSI_CONNECT_STATE_NORMAL_SESSION ) { login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_SESSION_NO_SUPPORT; rc = ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; @@ -3085,20 +3026,11 @@ static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, cons if ( rc < 0 ) return rc; - if ( conn->session == NULL ) { - conn->session = iscsi_session_create( type ); - - if ( conn->session == NULL ) { - 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_LOGIN_RESPONSE; - } - + if ( conn->state == ISCSI_CONNECT_STATE_NEW ) { conn->stat_sn = iscsi_get_be32(login_response_pkt->stat_sn); - conn->session->exp_cmd_sn = login_response_pdu->cmd_sn; - conn->session->max_cmd_sn = (uint32_t) (login_response_pdu->cmd_sn + ISCSI_DEFAULT_QUEUE_DEPTH - 1UL); + conn->exp_cmd_sn = login_response_pdu->cmd_sn; + conn->max_cmd_sn = (uint32_t) (login_response_pdu->cmd_sn + ISCSI_DEFAULT_QUEUE_DEPTH - 1UL); } return ISCSI_CONNECT_PDU_READ_OK; @@ -3132,10 +3064,10 @@ if ( rc < 0 ) return -1; \ payload_len += rc; \ } while (0) # define ADD_KV_OPTION_INT(key) do { \ -if ( pairs->key != -1 ) ADD_KV_INTERNAL( true, #key, (const char *)(size_t)conn->session->opts.key ); \ +if ( pairs->key != -1 ) ADD_KV_INTERNAL( true, #key, (const char *)(size_t)conn->opts.key ); \ } while (0) # define ADD_KV_OPTION_STR(key) do { \ -if ( pairs->key != NULL ) ADD_KV_INTERNAL( false, #key, conn->session->opts.key ); \ +if ( pairs->key != NULL ) ADD_KV_INTERNAL( false, #key, conn->opts.key ); \ } while (0) # define ADD_KV_PLAIN_INT(key, value) do { \ if ( pairs->key != -1 ) ADD_KV_INTERNAL( true, #key, (const char *)(size_t)(value) ); \ @@ -3301,6 +3233,9 @@ static int iscsi_connection_handle_login_req(iscsi_connection *conn, iscsi_pdu * } iscsi_connecction_handle_login_response( conn, &login_response_pdu, &pairs ); + if ( conn->state == ISCSI_CONNECT_STATE_NORMAL_SESSION ) { + conn->cid = iscsi_get_be16(login_req_pkt->cid); + } return iscsi_send_login_response_pdu( conn, &login_response_pdu ); } @@ -3353,7 +3288,7 @@ static int iscsi_connection_handle_text_req(iscsi_connection *conn, const iscsi_ } iscsi_pdu CLEANUP_PDU response_pdu; - if ( !iscsi_connection_pdu_init( &response_pdu, MIN( 8192, conn->session->opts.MaxRecvDataSegmentLength ), false ) ) + if ( !iscsi_connection_pdu_init( &response_pdu, MIN( 8192, conn->opts.MaxRecvDataSegmentLength ), false ) ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; iscsi_connection_update_key_value_pairs( conn, &pairs ); @@ -3381,10 +3316,10 @@ static int iscsi_connection_handle_text_req(iscsi_connection *conn, const iscsi_ iscsi_put_be32( (uint8_t *) &text_response_pkt->stat_sn, conn->stat_sn++ ); - conn->session->max_cmd_sn++; + conn->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 ); + iscsi_put_be32( (uint8_t *) &text_response_pkt->exp_cmd_sn, conn->exp_cmd_sn ); + iscsi_put_be32( (uint8_t *) &text_response_pkt->max_cmd_sn, conn->max_cmd_sn ); text_response_pkt->reserved2[0] = 0ULL; text_response_pkt->reserved2[1] = 0ULL; diff --git a/src/server/iscsi.h b/src/server/iscsi.h index 809c141..53c4b0e 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -89,53 +89,6 @@ static inline void iscsi_put_be64(uint8_t *data, const uint64_t value) (*(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__) // GCC or CLang @@ -185,32 +138,6 @@ static inline void iscsi_put_be64(uint8_t *data, const uint64_t value) (*(uint64_t *) data) = iscsi_get_be64(value); } -#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) -{ - (*(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_le32(uint8_t *data, const uint32_t value) -{ - (*(uint32_t *) data) = value; -} - -static inline void iscsi_put_le64(uint8_t *data, const uint64_t value) -{ - (*(uint64_t *) data) = value; -} #else #error "Unknown CPU endianness" #endif @@ -226,15 +153,6 @@ static inline void iscsi_put_le64(uint8_t *data, const uint64_t value) /// 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)) @@ -242,28 +160,6 @@ static inline void iscsi_put_le64(uint8_t *data, const uint64_t value) #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)) @@ -287,9 +183,6 @@ static inline void iscsi_put_le64(uint8_t *data, const uint64_t value) /// iSCSI packet data alignment (BHS, AHS and DataSegment). #define ISCSI_ALIGN_SIZE 4UL -/// iSCSI header and data digest size (CRC32C). -#define ISCSI_DIGEST_SIZE 4UL - /// iSCSI Default receive DataSegment (DS) size in bytes. #define ISCSI_DEFAULT_RECV_DS_LEN 16384UL @@ -305,13 +198,6 @@ static inline void iscsi_put_le64(uint8_t *data, const uint64_t value) #define ISCSI_VERSION_MAX 0 -/// CRC32C initial constant for header and data digest. -#define ISCSI_CRC32C_INITIAL 0xFFFFFFFFUL - -/// CRC32C initial constant for header and data digest. -#define ISCSI_CRC32C_XOR 0xFFFFFFFFUL - - /// iSCSI initiator (client) command opcode: NOP-Out. #define ISCSI_OPCODE_CLIENT_NOP_OUT 0x00 @@ -448,14 +334,6 @@ typedef struct __attribute__((packed)) iscsi_bhs_packet { ASSERT_IS_BHS( iscsi_bhs_packet ); - -/// iSCSI AHS type: Extended Command Descriptor Block (CDB). -#define ISCSI_AHS_TYPE_EXT_CDB_PACKET 0x01 - -/// iSCSI AHS type: Bidirectional Read Expected Data Transfer Length. -#define ISCSI_AHS_TYPE_BIDI_READ_EXP_XFER_AHS_PACKET 0x02 - - /** * @brief iSCSI Advanced Header Segment packet data. * @@ -475,46 +353,6 @@ typedef struct __attribute__((packed)) iscsi_ahs_packet { uint8_t specific; } iscsi_ahs_packet; -/** - * @brief DataSegment Error: Unexpected unsolicited data. - * - * Certain iSCSI conditions result in the command being terminated at - * the target (response code of Command Completed at Target) with a SCSI - * CHECK CONDITION Status as outlined in the following definitions - * (Sense key: Aborted Command 0x0B). - */ -#define ISCSI_DS_ERROR_UNEXPECTED_UNSOLICITED_DATA_ASC 0x0C - -/** - * @brief DataSegment Error: Unexpected unsolicited data. - * - * Certain iSCSI conditions result in the command being terminated at - * the target (response code of Command Completed at Target) with a SCSI - * CHECK CONDITION Status as outlined in the following definitions - * (Sense key: Aborted Command 0x0B). - */ -#define ISCSI_DS_ERROR_UNEXPECTED_UNSOLICITED_DATA_ASCQ 0x0C - - -/** - * @brief DataSegment Error: Incorrect amount of data. - * - * Certain iSCSI conditions result in the command being terminated at - * the target (response code of Command Completed at Target) with a SCSI - * CHECK CONDITION Status as outlined in the following definitions - * (Sense key: Aborted Command 0x0B). - */ -#define ISCSI_DS_ERROR_INCORRECT_AMOUNT_OF_DATA_ASC 0x0C - -/** - * @brief DataSegment Error: Incorrect amount of data. - * - * Certain iSCSI conditions result in the command being terminated at - * the target (response code of Command Completed at Target) with a SCSI - * CHECK CONDITION Status as outlined in the following definitions - * (Sense key: Aborted Command 0x0B). - */ -#define ISCSI_DS_ERROR_INCORRECT_AMOUNT_OF_DATA_ASCQ 0x0D /** * @brief iSCSI SCSI CDB packet data structure. @@ -928,36 +766,6 @@ typedef struct __attribute__((packed)) 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 @@ -1053,36 +861,6 @@ typedef struct __attribute__((packed)) iscsi_scsi_ds_cmd_data { /// 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 @@ -1150,34 +928,6 @@ typedef struct __attribute__((packed)) iscsi_scsi_basic_inquiry_data_packet { #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) @@ -1295,36 +1045,6 @@ typedef struct __attribute__((packed)) 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 @@ -1402,10 +1122,6 @@ typedef struct __attribute__((packed)) iscsi_scsi_ext_inquiry_data_packet { #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. * @@ -1646,141 +1362,12 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_design_desc_logical_u } 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. @@ -1830,26 +1417,6 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_ext_inquiry_data_pack uint16_t reserved4; } iscsi_scsi_vpd_page_ext_inquiry_data_packet; - -/// 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. * @@ -1908,31 +1475,10 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_block_limits_inquiry_ /// 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 @@ -1948,36 +1494,6 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_block_limits_inquiry_ /// 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) @@ -2094,25 +1610,6 @@ typedef struct __attribute__((packed)) iscsi_scsi_sense_data_packet { #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. * @@ -2158,40 +1655,6 @@ typedef struct __attribute__((packed)) iscsi_scsi_read_capacity_10_parameter_dat } 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 @@ -2207,43 +1670,6 @@ typedef struct __attribute__((packed)) iscsi_scsi_read_capacity_10_parameter_dat /// 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. @@ -2287,28 +1713,6 @@ typedef struct __attribute__((packed)) iscsi_scsi_report_luns_parameter_data_lun } iscsi_scsi_report_luns_parameter_data_lun_list_packet; -/** - * @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) @@ -2431,18 +1835,12 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_long_lba_parameter_ /// 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 @@ -2452,39 +1850,12 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_long_lba_parameter_ /// 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 @@ -2494,114 +1865,9 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_long_lba_parameter_ /// 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 @@ -3210,15 +2476,6 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_info_exceptions_con /// SCSI command flags task attribute: ACA. #define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_ACA 0x4 -/// SCSI command flags task attribute: Reserved. -#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_RESERVED_1 0x5 - -/// SCSI command flags task attribute: Reserved. -#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_RESERVED_2 0x6 - -/// SCSI command flags task attribute: Reserved. -#define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_RESERVED_3 0x7 - /// SCSI command flags Task Attributes (ATTR) are encoded in the first three LSBs. #define ISCSI_SCSI_CMD_FLAGS_TASK_ATTR_MASK 0x7 @@ -3489,12 +2746,6 @@ typedef struct __attribute__((packed)) iscsi_scsi_cmd_packet { /// SCSI response code: Target Failure. #define ISCSI_SCSI_RESPONSE_CODE_FAIL 0x01 -/// SCSI response code: First vendor specific response code. -#define ISCSI_SCSI_RESPONSE_CODE_VENDOR_FIRST 0x80 - -/// SCSI response code: Last vendor specific response code. -#define ISCSI_SCSI_RESPONSE_CODE_VENDOR_LAST 0xFF - /** * @brief iSCSI SCSI command response packet data. * @@ -3823,9 +3074,6 @@ typedef struct __attribute__((packed)) iscsi_task_mgmt_func_response_packet { uint64_t reserved5; } iscsi_task_mgmt_func_response_packet; -/// SCSI data out / in flags: Immediately process transfer. -#define ISCSI_SCSI_DATA_OUT_DATA_IN_FLAGS_IMMEDIATE (1 << 7) - /** * @brief SCSI Data In reponse flags: Status. * @@ -4242,49 +3490,6 @@ typedef struct __attribute__((packed)) iscsi_text_response_packet { } iscsi_text_response_packet; -/** - * @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 - -/** - * @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 - -/** - * @brief iSCSI Initiator Session ID (ISID) type: Random. - * - * A: Reserved - * B and C: Random - * D: Qualifier - */ -#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. * @@ -4335,9 +3540,6 @@ typedef struct __attribute__((packed)) iscsi_isid { /// Login request Next Stage (NSG) flags: LoginOperationalNegotiation. #define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 -/// Login request Next Stage (NSG) flags: Reserved for future usage, may NOT be used. -#define ISCSI_LOGIN_REQ_FLAGS_BEXT_STAGE_RESERVED 0x2 - /// Login request Next Stage (NSG) flags: FullFeaturePhase. #define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_FULL_FEATURE_PHASE 0x3 @@ -5506,54 +4708,6 @@ typedef struct __attribute__((packed)) iscsi_nop_in_packet { #define ISCSI_TEXT_VALUE_MAX_LEN 8192U -/** - * @brief iSCSI connection and session lookup table entry, used for allowed key values and determining key type. - * - * This structure is shared by the iSCSI session - * and the iSCSI connection lookup table. - */ -typedef struct iscsi_key_value_pair_lut_entry { - /// Name of key. - const uint8_t *key; - - /// Default value of the key, always in string representation. - uint8_t *value; - - /// NUL separated list of allowed string values. If key type is numeric: NUL separated minimum and maximum integer range. End is marked with another NUL. - uint8_t *list_range; - - /// Type of key and value pair. - const int type; - - /// Flags indicating special key attributes. - const int flags; -} iscsi_key_value_pair_lut_entry; - - -/** - * @brief iSCSI Text / Login extracted key=value pair. - * - * This structure is used for accessing key and value - * pairs which have been extracted from either the - * Text or Login packet data. - */ -typedef struct iscsi_key_value_pair { - /// Value of the key which is stored in the hash map. - uint8_t *value; - - /// NUL separated list of allowed string values. If key type is numeric: NUL separated minimum and maximum integer range. End is marked with another NUL. - uint8_t *list_range; - - /// Type of key and value pair. - int type; - - /// Flags indicating special key attributes. - int flags; - - /// State bit mask. - uint state_mask; -} iscsi_key_value_pair; - typedef struct iscsi_connection iscsi_connection; @@ -5582,9 +4736,6 @@ typedef struct iscsi_connection iscsi_connection; /// 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 @@ -5809,29 +4960,6 @@ typedef struct iscsi_scsi_task { _Static_assert( (ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE << ISCSI_SCSI_EMU_BLOCK_DIFF_SHIFT) == ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE, "Block size parameters are inconsistent" ); -/// 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 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) - - /// iSCSI target node WWN identifier prefix string. #define ISCSI_TARGET_NODE_WWN_NAME_PREFIX "wwn-0x" @@ -5840,15 +4968,6 @@ _Static_assert( (ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE << ISCSI_SCSI_EMU_BLOCK_DIFF_ #define ISCSI_TARGET_NODE_MAX_NAME_LEN 223U -/// iSCSI session type: Invalid. -#define ISCSI_SESSION_TYPE_INVALID 0 - -/// iSCSI session type: Normal. -#define ISCSI_SESSION_TYPE_NORMAL 1 - -/// iSCSI session type: Discovery. -#define ISCSI_SESSION_TYPE_DISCOVERY 2 - /** * All mandatory fields in login process. * Set to -1 or NULL if not sent by client. @@ -5906,36 +5025,6 @@ typedef struct iscsi_session_options int FirstBurstLength; } iscsi_session_options; -/** - * @brief iSCSI session. - * - * This structure manages an iSCSI session and - * stores the key / value pairs from the - * login phase. - */ -typedef struct iscsi_session { - /// Initiator Session ID (ISID). - uint64_t isid; - - /// Target Session Identifying Handle (TSIH). - uint64_t tsih; - - /// Flags (extracted from key and value pairs). - int flags; - - /// iSCSI session type. - int type; - - /// ExpCmdSN. - uint32_t exp_cmd_sn; - - /// MaxCmdSN. - uint32_t max_cmd_sn; - - /// Session options client sent in login request. - iscsi_session_options opts; -} iscsi_session; - typedef struct iscsi_pdu iscsi_pdu; @@ -5943,9 +5032,6 @@ 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 0 -/// iSCSI connection read packet data return code from iscsi_connection_pdu_read function: Packet processed successfully. -#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 (-1) @@ -5959,26 +5045,6 @@ typedef struct iscsi_pdu iscsi_pdu; #define ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER_XCHG_NOT_ONCE (-4) -/// iSCSI connection flags: Full feature. -#define ISCSI_CONNECT_FLAGS_FULL_FEATURE (1 << 3) - -/// iSCSI connection flags: Oustanding NOP. -#define ISCSI_CONNECT_FLAGS_NOP_OUTSTANDING (1 << 8) - - -/// Ready to wait for PDU. -#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 1 - -/// Active connection waiting for data. -#define ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_DATA 2 - -/// Active connection does not wait for data. -#define ISCSI_CONNECT_PDU_RECV_STATE_ERR 3 - - /// iSCSI connection state: Fresh connection, no login yet. #define ISCSI_CONNECT_STATE_NEW 0 @@ -5988,6 +5054,9 @@ typedef struct iscsi_pdu iscsi_pdu; /// iSCSI connection state: Exiting, teardown of connection imminent. #define ISCSI_CONNECT_STATE_EXITING 2 +/// iSCSI connection state: Invalid. +#define ISCSI_CONNECT_STATE_INVALID 3 + /// Number of attempts for writing to iSCSI connection socket. #define ISCSI_CONNECT_SOCKET_WRITE_RETRIES 3 @@ -6002,9 +5071,6 @@ typedef struct iscsi_pdu iscsi_pdu; * and iSCSI portals. */ typedef struct iscsi_connection { - /// iSCSI session associated with this connection. - iscsi_session *session; - /// Associated dnbd3 client dnbd3_client_t *client; @@ -6029,20 +5095,17 @@ typedef struct iscsi_connection { /// Connection ID (CID). uint16_t cid; - /// Bit mask for connection state key negotiation. - uint16_t state_negotiated; - - /// Bit mask for session state key negotiation. - uint32_t session_state_negotiated; + /// StatSN. + uint32_t stat_sn; - /// Initiator Task Tag (ITT). - uint32_t init_task_tag; + /// ExpCmdSN. + uint32_t exp_cmd_sn; - /// Targer Transfer Tag (TTT). - uint32_t target_xfer_tag; + /// MaxCmdSN. + uint32_t max_cmd_sn; - /// StatSN. - uint32_t stat_sn; + /// Session options client sent in login request. + iscsi_session_options opts; } iscsi_connection; -- cgit v1.2.3-55-g7522 From 7d6ae6b1fcbf77d70ab65c3e747037eb457fe2bd Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 11 Nov 2025 14:31:18 +0100 Subject: [SERVER] iscsi: fix typo --- src/server/iscsi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 18fefe1..1b29dbe 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -3107,7 +3107,7 @@ if ( pairs->key != NULL ) ADD_KV_INTERNAL( false, #key, value ); \ * @param[in] pairs Readily parsed key-value-pairs from according request * @return 0 on success, a negative error code otherwise. */ -static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi_pdu *login_response_pdu, const iscsi_negotiation_kvp *pairs) +static int iscsi_connection_handle_login_response(iscsi_connection *conn, iscsi_pdu *login_response_pdu, const iscsi_negotiation_kvp *pairs) { if ( iscsi_connection_pdu_resize( login_response_pdu, 0, ISCSI_DEFAULT_RECV_DS_LEN ) == NULL ) { return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; @@ -3232,7 +3232,7 @@ static int iscsi_connection_handle_login_req(iscsi_connection *conn, iscsi_pdu * return iscsi_send_login_response_pdu( conn, &login_response_pdu ); } - iscsi_connecction_handle_login_response( conn, &login_response_pdu, &pairs ); + iscsi_connection_handle_login_response( conn, &login_response_pdu, &pairs ); if ( conn->state == ISCSI_CONNECT_STATE_NORMAL_SESSION ) { conn->cid = iscsi_get_be16(login_req_pkt->cid); } -- cgit v1.2.3-55-g7522 From 518c2855d51c4c2e79ff32a25d7efd78abf57cf8 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 12 Nov 2025 14:41:52 +0100 Subject: [SERVER] iscsi: Fix handling of reason_code in logout request Only the lower 7 bits carry the reason code, mask away highest bit. --- src/server/iscsi.c | 5 ++++- src/server/iscsi.h | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 1b29dbe..b7aacec 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -2764,8 +2764,11 @@ static int iscsi_connection_handle_logout_req(iscsi_connection *conn, const iscs { iscsi_logout_req_packet *logout_req_pkt = (iscsi_logout_req_packet *) request_pdu->bhs_pkt; - if ( (conn->state == ISCSI_CONNECT_STATE_NEW) || (logout_req_pkt->reason_code != ISCSI_LOGOUT_REQ_REASON_CODE_CLOSE_SESSION) ) + if ( (conn->state == ISCSI_CONNECT_STATE_NEW) + || ((logout_req_pkt->reason_code & ISCSI_LOGOUT_REQ_REASON_CODE_MASK) != ISCSI_LOGOUT_REQ_REASON_CODE_CLOSE_SESSION) ) { + logadd( LOG_DEBUG1, "Invalid logout request in state %d, reason_code %d", conn->state, logout_req_pkt->reason_code ); return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + } iscsi_pdu CLEANUP_PDU response_pdu; if ( !iscsi_connection_pdu_init( &response_pdu, 0, false ) ) diff --git a/src/server/iscsi.h b/src/server/iscsi.h index 53c4b0e..8c5831c 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -4105,6 +4105,8 @@ typedef struct __attribute__((packed)) iscsi_login_response_packet { /// Logout request reason code: Remove the connection for recovery. The connection is closed, and all commands associated with it, if any, are to be prepared for a new allegiance. #define ISCSI_LOGOUT_REQ_REASON_CODE_REMOVE_CONNECTION_RECOVERY 0x02 +/// Mask to get the logout reason from the reason_code field (lower 7 bits) +#define ISCSI_LOGOUT_REQ_REASON_CODE_MASK 0x7f /** * @brief Logout request implicit reason code: Session reinstatement. @@ -4236,7 +4238,7 @@ typedef struct __attribute__((packed)) iscsi_logout_req_packet { * I_T_L nexus with the status of CHECK CONDITION, the ASC/ASCQ value * of 0x47 / 0x7F ("SOME COMMANDS CLEARED BY ISCSI PROTOCOL EVENT"), etc. */ - int8_t reason_code; + uint8_t reason_code; /// Reserved for future usage, always MUST be 0. uint16_t reserved; -- cgit v1.2.3-55-g7522 From 0a5abfc590ee3e7c2d228e330cf12715471a2064 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 12 Nov 2025 14:45:39 +0100 Subject: [SERVER] iscsi: Implement NOP-In on idle timeout If the socket timeout triggers while we wait for the next iSCSI PDU, send a NOP-In with the transfer tag set, so that the initiator is required to send a NOP-Out in response. If the following reveive times out once again, tear down the connection. --- src/server/iscsi.c | 74 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index b7aacec..8546284 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -2851,41 +2851,40 @@ static int iscsi_connection_handle_task_func_req(iscsi_connection *conn, const i } /** - * @brief Handles an incoming iSCSI payload data NOP-Out request PDU. + * @brief Handles receiving and sending of NOP in/out packets. * - * This function handles NOP-Out request payload - * data sent by the client.\n - * If a response needs to be sent, this will - * be done as well. + * This method can handle a received NOP-Out request and send + * an according NOP-In response if applicable, i.e. the NOP-Out + * wasn't sent as a reply to a NOP-In by us.\n + * This method can also send an unsolicited NOP-In to the client + * if we want to check whether the connection is still good. * * @param[in] conn Pointer to iSCSI connection to handle. May * NOT be NULL, so take caution. - * @param[in] request_pdu Pointer to iSCSI client request PDU to handle. - * May be NULL in which case an error is returned. + * @param[in] request_pdu Pointer to iSCSI client request PDU to handle, + * or NULL for sending a connection alive check. * @return 0 on success. A negative value indicates - * an error. A positive value a warning. + * an error. */ -static int iscsi_connection_handle_nop_out(iscsi_connection *conn, const iscsi_pdu *request_pdu) +static int iscsi_connection_handle_nop(iscsi_connection *conn, const iscsi_pdu *request_pdu) { if ( conn->state != ISCSI_CONNECT_STATE_NORMAL_SESSION ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - if ( request_pdu->ds_len > ISCSI_DEFAULT_MAX_RECV_DS_LEN ) + if ( request_pdu != NULL && request_pdu->ds_len > ISCSI_DEFAULT_MAX_RECV_DS_LEN ) return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); - iscsi_nop_out_packet *nop_out_pkt = (iscsi_nop_out_packet *) request_pdu->bhs_pkt; - const uint32_t target_xfer_tag = iscsi_get_be32(nop_out_pkt->target_xfer_tag); - uint32_t ds_len = request_pdu->ds_len; - const uint64_t lun = iscsi_get_be64(nop_out_pkt->lun); + iscsi_nop_out_packet *nop_out_pkt = request_pdu == NULL ? NULL : (iscsi_nop_out_packet *) request_pdu->bhs_pkt; + const uint32_t target_xfer_tag = nop_out_pkt == NULL ? 0xFFFFFFFFUL : iscsi_get_be32(nop_out_pkt->target_xfer_tag); + const uint32_t init_task_tag = nop_out_pkt == NULL ? 0 : iscsi_get_be32(nop_out_pkt->init_task_tag); + uint32_t ds_len = request_pdu == NULL ? 0 : request_pdu->ds_len; + const uint64_t lun = nop_out_pkt == NULL ? 0 : iscsi_get_be64(nop_out_pkt->lun); - if ( nop_out_pkt->init_task_tag == 0xFFFFFFFFUL ) // Was response to a NOP by us - do not reply + if ( init_task_tag == 0xFFFFFFFFUL ) // Was response to a NOP by us, or no response desired - do not reply return ISCSI_CONNECT_PDU_READ_OK; - if ( (target_xfer_tag != 0xFFFFFFFFUL) && (target_xfer_tag != (uint32_t) conn->id) ) - return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_INVALID_PDU_FIELD ); // TODO: Check if this is the correct error code. - - if ( (nop_out_pkt->init_task_tag == 0xFFFFFFFFUL) && (nop_out_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + if ( target_xfer_tag != 0xFFFFFFFFUL ) // If the initiator tag is not the special value, the target tag has to be. + return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_INVALID_PDU_FIELD ); if ( ds_len > (uint32_t)conn->opts.MaxRecvDataSegmentLength ) ds_len = conn->opts.MaxRecvDataSegmentLength; @@ -2897,15 +2896,23 @@ static int iscsi_connection_handle_nop_out(iscsi_connection *conn, const iscsi_p iscsi_nop_in_packet *nop_in_pkt = (iscsi_nop_in_packet *) response_pdu.bhs_pkt; nop_in_pkt->opcode = ISCSI_OPCODE_SERVER_NOP_IN; - nop_in_pkt->flags = -0x80; + nop_in_pkt->flags = 0x80; 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; // Minus one does not require endianess conversion - nop_in_pkt->init_task_tag = nop_out_pkt->init_task_tag; // Copyed from request packet, no endian conversion required - iscsi_put_be32( (uint8_t *) &nop_in_pkt->stat_sn, conn->stat_sn++ ); + if ( nop_out_pkt == NULL ) { + // Send a request which needs a reply, set target tag to anything but the special value + nop_in_pkt->init_task_tag = 0xFFFFFFFFUL; + nop_in_pkt->target_xfer_tag = 0; + iscsi_put_be32( (uint8_t *) &nop_in_pkt->stat_sn, conn->stat_sn ); // Don't inc + } else { + // This is a reply, set target tag to special value to indicate we don't want a NOP-Out in response + iscsi_put_be32( (uint8_t *) &nop_in_pkt->init_task_tag, init_task_tag ); + nop_in_pkt->target_xfer_tag = 0xFFFFFFFFUL; + iscsi_put_be32( (uint8_t *) &nop_in_pkt->stat_sn, conn->stat_sn++ ); // Inc + } - if ( (nop_out_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) + if ( nop_out_pkt == NULL || (nop_out_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) conn->max_cmd_sn++; iscsi_put_be32( (uint8_t *) &nop_in_pkt->exp_cmd_sn, conn->exp_cmd_sn ); @@ -3366,7 +3373,7 @@ static int iscsi_connection_pdu_handle(iscsi_connection *conn, iscsi_pdu *reques switch ( opcode ) { case ISCSI_OPCODE_CLIENT_NOP_OUT : { - rc = iscsi_connection_handle_nop_out( conn, request_pdu ); + rc = iscsi_connection_handle_nop( conn, request_pdu ); break; } @@ -3422,6 +3429,7 @@ static int iscsi_connection_pdu_handle(iscsi_connection *conn, iscsi_pdu *reques */ static void iscsi_connection_pdu_read_loop(iscsi_connection *conn, const dnbd3_request_t *request, const int len) { + ssize_t ret; iscsi_pdu CLEANUP_PDU request_pdu; if ( !iscsi_connection_pdu_init( &request_pdu, 0, false ) ) @@ -3483,8 +3491,17 @@ static void iscsi_connection_pdu_read_loop(iscsi_connection *conn, const dnbd3_r // header before the loop - this saves us from accounting for this within the mainloop // 1) Receive entire BHS - if ( sock_recv( conn->client->sock, request_pdu.bhs_pkt, sizeof(iscsi_bhs_packet) ) != sizeof(iscsi_bhs_packet) ) { - logadd( LOG_INFO, "Cannot receive BHS for client %s", conn->client->hostName ); + ret = sock_recv( conn->client->sock, request_pdu.bhs_pkt, sizeof(iscsi_bhs_packet) ); + if ( ret == -1 && errno == EAGAIN ) { + // Receive timeout - send a NOP-In and try recv one more time + if ( iscsi_connection_handle_nop( conn, NULL ) != ISCSI_CONNECT_PDU_READ_OK ) { + logadd( LOG_DEBUG1, "Cannot send NOP-In to idle client %s - connection dead", conn->client->hostName ); + break; + } + ret = sock_recv( conn->client->sock, request_pdu.bhs_pkt, sizeof(iscsi_bhs_packet) ); + } + if ( ret != sizeof(iscsi_bhs_packet) ) { + logadd( LOG_DEBUG1, "Cannot receive BHS for client %s (%d/%d)", conn->client->hostName, (int)ret, (int)errno ); break; } } while ( !_shutdown ); @@ -3508,7 +3525,6 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ { _Static_assert( sizeof(dnbd3_request_t) <= sizeof(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 iscsi_connection *conn = iscsi_connection_create( client ); -- cgit v1.2.3-55-g7522 From d6eafd3faf59c4a46ebefaf5958a6cc00d4d9ac8 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 12 Nov 2025 15:00:25 +0100 Subject: [SERVER] iscsi: Replace int8 with uint8, remove unused login flags --- src/server/iscsi.c | 6 ++--- src/server/iscsi.h | 72 ++++++++++++++---------------------------------------- 2 files changed, 20 insertions(+), 58 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 8546284..256446a 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -2116,7 +2116,6 @@ static iscsi_connection *iscsi_connection_create(dnbd3_client_t *client) conn->client = client; conn->flags = 0; conn->state = ISCSI_CONNECT_STATE_NEW; - conn->login_phase = ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION; conn->cid = 0U; conn->stat_sn = 0UL; conn->exp_cmd_sn = 0UL; @@ -2676,7 +2675,7 @@ static int iscsi_connection_handle_reject(iscsi_connection *conn, const iscsi_pd iscsi_reject_packet *reject_pkt = (iscsi_reject_packet *) response_pdu.bhs_pkt; reject_pkt->opcode = ISCSI_OPCODE_SERVER_REJECT; - reject_pkt->flags = -0x80; + 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. @@ -2777,7 +2776,7 @@ static int iscsi_connection_handle_logout_req(iscsi_connection *conn, const iscs 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; + logout_response_pkt->flags = 0x80; const uint16_t cid = iscsi_get_be16(logout_req_pkt->cid); @@ -3155,7 +3154,6 @@ static int iscsi_connection_handle_login_response(iscsi_connection *conn, iscsi_ // Client set the transition bit - requests to move on to next stage switch ( ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) ) { 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, 42 ); diff --git a/src/server/iscsi.h b/src/server/iscsi.h index 8c5831c..a1582fe 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -436,7 +436,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_10 { uint32_t lba; /// Group number. - int8_t group_num; + uint8_t group_num; /// Transfer length in bytes. uint16_t xfer_len; @@ -465,7 +465,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_12 { uint32_t xfer_len; /// Restricted for MMC-6 and group number. - int8_t restrict_group_num; + uint8_t restrict_group_num; /// Control. uint8_t control; @@ -491,7 +491,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_16 { uint32_t xfer_len; /// Restricted for MMC-6 and group number. - int8_t restrict_group_num; + uint8_t restrict_group_num; /// Control. uint8_t control; @@ -911,13 +911,13 @@ typedef struct __attribute__((packed)) iscsi_scsi_basic_inquiry_data_packet { uint8_t peripheral_type_id; /// Peripheral device type modifier and removable media bit. - int8_t peripheral_type_mod_flags; + uint8_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; + uint8_t response_data_fmt_flags; /// Additional length in bytes. uint8_t add_len; @@ -975,7 +975,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_std_inquiry_data_packet { uint8_t tpgs_flags; /// MULTIP, VS and ENCSERV. - int8_t services_flags; + uint8_t services_flags; /// Flags. uint8_t flags; @@ -1390,13 +1390,13 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_ext_inquiry_data_pack uint8_t page_len; /// Check flags. - int8_t check_flags; + uint8_t check_flags; /// Support flags. - int8_t support_flags; + uint8_t support_flags; /// More support flags. - int8_t support_flags_2; + uint8_t support_flags_2; /// LUICLR. uint8_t luiclr; @@ -1591,13 +1591,13 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_block_dev_chars_inqui */ typedef struct __attribute__((packed)) iscsi_scsi_sense_data_packet { /// Response code. - int8_t response_code; + uint8_t response_code; /// Reserved for future usage (always MUST be 0). uint8_t reserved; /// Sense key and flags. - int8_t sense_key_flags; + uint8_t sense_key_flags; /// Information. uint32_t info; @@ -2097,7 +2097,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_caching_mode_page_d uint16_t max_prefetch_ceil; /// Cache flags. - int8_t cache_flags; + uint8_t cache_flags; /// Number of cache segments. uint8_t num_cache_segs; @@ -2126,13 +2126,13 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_control_mode_page_d uint8_t flags; /// Queue flags. - int8_t queue_flags; + uint8_t queue_flags; /// Control flags. - int8_t control_flags; + uint8_t control_flags; /// Application task flags. - int8_t app_task_flags; + uint8_t app_task_flags; /// Ready AER holdoff period. uint16_t ready_aer_holdoff_period; @@ -2219,7 +2219,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_power_cond_mode_pag uint8_t flags; /// Idle and standby flags. - int8_t idle_standby_flags; + uint8_t idle_standby_flags; /// idle_a condition timer. uint32_t idle_a_cond_timer; @@ -2249,7 +2249,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_power_cond_mode_pag uint8_t reserved4; /// Check Condition From (CCF) flags. - int8_t ccf_flags; + uint8_t ccf_flags; } iscsi_scsi_mode_sense_power_cond_mode_page_data_packet; @@ -2894,7 +2894,7 @@ typedef struct __attribute__((packed)) iscsi_task_mgmt_func_req_packet { * tasks). The task management function codes are listed below. For a * more detailed description of SCSI task management, see SAM2. */ - int8_t func; + uint8_t func; /// Reserved fot future usage, always MUST be 0. uint16_t reserved; @@ -3534,15 +3534,6 @@ typedef struct __attribute__((packed)) iscsi_isid { } iscsi_isid; -/// Login request Next Stage (NSG) flags: SecurityNegotiation. -#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION 0x0 - -/// Login request Next Stage (NSG) flags: LoginOperationalNegotiation. -#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 - -/// Login request Next Stage (NSG) flags: FullFeaturePhase. -#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_FULL_FEATURE_PHASE 0x3 - /** * @brief Login request flags: Next Stage (NSG): First bit of the two bits. * @@ -3568,24 +3559,6 @@ typedef struct __attribute__((packed)) iscsi_isid { /// Login request flags: Next Stage (NSG): Bit mask. #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) (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. -#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_SECURITY_NEGOTIATION 0x0 - -/// Login request Current Stage (CSG) flags: LoginOperationalNegotiation. -#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION 0x1 - -/// Login request Current Stage (CSG) flags: Reserved for future usage, may NOT be used. -#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_RESERVED 0x2 - -/// Login request Current Stage (CSG) flags: FullFeaturePhase. -#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_FULL_FEATURE_PHASE 0x3 /** * @brief Login request flags: Current Stage (CSG): First bit of the two bits. @@ -3610,12 +3583,6 @@ typedef struct __attribute__((packed)) iscsi_isid { /// Login request flags: Current Stage (CSG): Bit mask. #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) (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)) - /** * @brief Login request flags: Continue. @@ -5085,9 +5052,6 @@ typedef struct iscsi_connection { /// iSCSI connection state. int state; - /// iSCSI connection login phase. - int login_phase; - /// Initiator Session ID (ISID). iscsi_isid isid; -- cgit v1.2.3-55-g7522 From ea11bec0849f4b9093b835af567c7d877ee56534 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 12 Nov 2025 15:03:36 +0100 Subject: [SERVER] iscsi: May not -> Must not --- src/server/iscsi.c | 78 +++++++++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 256446a..0627de4 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -107,7 +107,7 @@ static int iscsi_connection_handle_reject(iscsi_connection *conn, const iscsi_pd * larger than the maximum allowed size. * * @param[in] dst Pointer to destination string to copy - * with padding and may NOT be NULL, so be + * with padding and must NOT be NULL, so be * careful. * @param[in] src Pointer to string for copying. NULL * is NOT allowed here, take caution. @@ -205,7 +205,7 @@ static void iscsi_copy_kvp_str(const char *name, const char **dest, const char * * the iSCSI implementation. * * @param[in] key_value_pairs Pointer to hash map containing all related keys and pairs. - * May NOT be NULL, so take caution. + * Must 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. * @param[in] len Length of the remaining packet data. @@ -282,7 +282,7 @@ static int iscsi_parse_text_key_value_pair(iscsi_negotiation_kvp *key_value_pair * the iSCSI implementation. * * @param[in] pairs struct to write all key-value-pair options from packet to - * extracted keys and pairs. May NOT be NULL, so take caution. + * extracted keys and pairs. Must 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. * @param[in] len Length of the remaining packet data. @@ -326,7 +326,7 @@ static int iscsi_parse_login_key_value_pairs(iscsi_negotiation_kvp *pairs, const * it to the initiator. * * @param[in] conn Pointer to iSCSI connection for which the - * packet should be sent for. May NOT be + * packet should be sent for. Must NOT be * NULL, so be careful. * @param[in] task Pointer to iSCSI task which handles the * actual SCSI packet data. NULL is NOT @@ -420,7 +420,7 @@ static bool iscsi_scsi_data_in_send(iscsi_connection *conn, const iscsi_task *ta * unprocessed tasks. * * @param[in] conn Pointer to iSCSI connection of which the - * incoming data should be handled, may NOT be + * incoming data should be handled, must NOT be * NULL, so be careful. * @param[in] task Pointer to iSCSI task for handling * the incoming data. NULL is NOT allowed here, @@ -580,7 +580,7 @@ static void iscsi_scsi_task_send_reply(iscsi_connection *conn, iscsi_scsi_task * * * @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 + * code sense data for. Must NOT be NULL, so * be careful. * @param[in] sense_key Sense Key (SK). * @param[in] asc Additional Sense Code (ASC). @@ -627,7 +627,7 @@ static void iscsi_scsi_task_sense_data_build(iscsi_scsi_task *scsi_task, const u * status code. * * @param[in] scsi_task Pointer to iSCSI SCSI task to set the - * SCSI status and additional details for. May + * SCSI status and additional details for. Must * NOT be NULL, so be careful. * @param[in] status SCSI status code to be set. * @param[in] sense_key Sense Key (SK). @@ -705,7 +705,7 @@ static int iscsi_scsi_lun_get_from_iscsi(const uint64_t lun) * properties. * * @param[in] image Pointer to DNBD3 image to retrieve - * the logical size from. May NOT be NULL, + * the logical size from. Must NOT be NULL, * so be careful. * @return The number of total logical blocks. */ @@ -722,7 +722,7 @@ static inline uint64_t iscsi_scsi_emu_block_get_count(const dnbd3_image_t *image * 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 + * in bytes. Must NOT be NULL, so be * careful. * @param[in] offset_blocks Offset in blocks. * @param[in] num_blocks Number of blocks. @@ -742,7 +742,7 @@ static uint64_t iscsi_scsi_emu_blocks_to_bytes(uint64_t *offset_bytes, const uin * block data which is NOT locally * available. * - * @param[in] data Pointer to related scsi_task. May NOT + * @param[in] data Pointer to related scsi_task. Must NOT * be NULL, so be careful. * @param[in] handle Pointer to destination buffer, as passed to * iscsi_scsi_emu_io_block_read(). @@ -771,10 +771,10 @@ static void iscsi_uplink_callback(void *data, uint64_t handle UNUSED, uint64_t s * been finished. * * @param[in] scsi_task Pointer to iSCSI SCSI task which - * executes the I/O read operation, may + * executes the I/O read operation, must * NOT be NULL, so be careful. * @param[in] image Pointer to DNBD3 image to read - * data from and may NOT be NULL, so + * data from and must 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. @@ -897,7 +897,7 @@ static void iscsi_scsi_emu_block_read(dnbd3_image_t *image, iscsi_scsi_task *scs * * @param[in] scsi_task Pointer to iSCSI SCSI task * to process the SCSI block operation - * for and may NOT be NULL, be careful. + * for and must NOT be NULL, be careful. * @return true on successful operation, false otherwise. */ static bool iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) @@ -1037,7 +1037,7 @@ static bool iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) * @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 + * storing the IEEE Extended NAA. Must * NOT be NULL, so be careful. * @param[in] name Pointer to string containing the * name to calculate the IEEE Extended @@ -1055,7 +1055,7 @@ static inline void iscsi_scsi_emu_naa_ieee_ext_set(uint64_t *buf, const uint8_t * @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 + * string to. Must NOT be NULL, so be * careful. * @param[in] name Pointer to string containing the * SCSI name to be copied. NULL is NOT @@ -1082,14 +1082,14 @@ static size_t iscsi_scsi_emu_pad_scsi_name(uint8_t *buf, const uint8_t *name) * status result code accordingly. * * @param[in] image Pointer to DNBD3 image to get - * the inquiry data from. May NOT be + * the inquiry data from. Must 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 + * Block (CDB) and must NOT be NULL, be * careful. * @param[in] std_inquiry_data_pkt Pointer to standard inquiry * data packet to fill the inquiry @@ -1558,7 +1558,7 @@ static void iscsi_scsi_emu_primary_mode_sense_page_init(uint8_t *buffer, const u * status result code accordingly. * * @param[in] image Pointer to DNBD3 image to get - * the mode sense data from. May NOT be + * the mode sense data from. Must NOT be * NULL, so be careful. * @param[in] scsi_task Pointer to iSCSI SCSI task * responsible for this mode sense @@ -1784,7 +1784,7 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc * status result code accordingly. * * @param[in] image Pointer to DNBD3 image to get - * the mode sense data from. May + * the mode sense data from. Must * NOT be NULL, so be careful. * @param[in] scsi_task Pointer to iSCSI SCSI task * responsible for this mode sense @@ -1907,7 +1907,7 @@ static uint32_t iscsi_get_temporary_allocation_size(iscsi_scsi_task *scsi_task, * * @param[in] scsi_task Pointer to iSCSI SCSI task * to process the SCSI non-block - * operation for and may NOT be NULL, + * operation for and must NOT be NULL, * be careful. * @return true on successful operation, false otherwise. */ @@ -2153,7 +2153,7 @@ static void iscsi_connection_destroy(iscsi_connection *conn) * @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 - * be written to output buffer which may + * be written to output buffer which must * NOT be NULL, so take caution. * @param[in] buf Pointer to output buffer to write the * key and value pair to. NULL is @@ -2209,7 +2209,7 @@ static void iscsi_connection_update_key_value_pairs(iscsi_connection *conn, cons * to be sent via TCP/IP. * * @param[in] conn Pointer to ISCSI connection to send the TCP/IP - * packet with. May NOT be NULL, so be + * packet with. Must NOT be NULL, so be * careful. * @param[in] resp_pdu Pointer to login response PDU to * be sent via TCP/IP. NULL is NOT @@ -2253,7 +2253,7 @@ static int iscsi_send_login_response_pdu(iscsi_connection *conn, iscsi_pdu *resp * @param[in] login_response_pdu Pointer to login response PDU, NULL * is not an allowed value here, so take caution. * @param[in] pdu Pointer to login request PDU from client, - * may NOT be NULL, so be careful. + * must NOT be NULL, so be careful. * @return 0 if initialization was successful, a negative error * code otherwise. */ @@ -2313,7 +2313,7 @@ static int iscsi_connection_pdu_login_response_init(iscsi_pdu *login_response_pd * NULL is not allowed, so take caution. * @param[in] type_str Pointer to key and value pairs which * contain the session type parameter to be evaluated, - * which may NOT be NULL, so take caution. + * which must NOT be NULL, so take caution. * @param[in] type Write session type constant to this int. * Must not be null. * @return 0 on successful operation, a negative error code @@ -2347,12 +2347,12 @@ static int iscsi_login_parse_session_type(const iscsi_pdu *login_response_pdu, c * THe accessibility of the target node is * also checked. * - * @param[in] conn Pointer to iSCSI connection which may NOT be + * @param[in] conn Pointer to iSCSI connection which must NOT be * NULL, so be careful. * @param[in] login_response_pdu Pointer to login response PDU * to set the parameters for. NULL is NOT allowed * here, so take caution. - * @param[in] target_name Pointer to target node name and may + * @param[in] target_name Pointer to target node name and must * NOT be NULL, be careful. * @return 0 if the check was successful or a negative * error code otherwise. @@ -2506,7 +2506,7 @@ static void iscsi_connection_pdu_destroy(const iscsi_pdu *pdu) * extended. * * @param[in] pdu Pointer to iSCSI PDU where to append - * the packet data to. May NOT be NULL, so + * the packet data to. Must NOT be NULL, so * be careful. * @param[in] ahs_len Length of AHS packet data to be appended. * @param[in] ds_len Length of DataSegment packet data to be appended. @@ -2587,11 +2587,11 @@ static iscsi_bhs_packet *iscsi_connection_pdu_resize(iscsi_pdu *pdu, const uint * If a header or data digest (CRC32C) needs to * be calculated, this is done as well. * - * @param[in] conn Pointer to iSCSI connection to handle. May + * @param[in] conn Pointer to iSCSI connection to handle. Must * NOT be NULL, so take caution. Will be freed after sending, * so don't access afterwards. * @param[in] pdu Pointer to iSCSI server response PDU to send. - * May NOT be NULL, so be careful. + * Must NOT be NULL, so be careful. */ static bool iscsi_connection_pdu_write(iscsi_connection *conn, const iscsi_pdu *pdu) { @@ -2752,7 +2752,7 @@ static int iscsi_connection_handle_cmd_sn(iscsi_connection *conn, iscsi_pdu *req * If a response needs to be sent, this will * be done as well. * - * @param[in] conn Pointer to iSCSI connection to handle. May + * @param[in] conn Pointer to iSCSI connection to handle. Must * NOT be NULL, so take caution. * @param[in] request_pdu Pointer to iSCSI client request PDU to handle. * May be NULL in which case an error is returned. @@ -2858,7 +2858,7 @@ static int iscsi_connection_handle_task_func_req(iscsi_connection *conn, const i * This method can also send an unsolicited NOP-In to the client * if we want to check whether the connection is still good. * - * @param[in] conn Pointer to iSCSI connection to handle. May + * @param[in] conn Pointer to iSCSI connection to handle. Must * NOT be NULL, so take caution. * @param[in] request_pdu Pointer to iSCSI client request PDU to handle, * or NULL for sending a connection alive check. @@ -2934,7 +2934,7 @@ static int iscsi_connection_handle_nop(iscsi_connection *conn, const iscsi_pdu * * If a response needs to be sent, this will * be done as well. * - * @param[in] conn Pointer to iSCSI connection to handle. May + * @param[in] conn Pointer to iSCSI connection to handle. Must * NOT be NULL, so take caution. * @param[in] request_pdu Pointer to iSCSI client request PDU to handle. * May be NULL in which case an error is returned. @@ -3004,11 +3004,11 @@ static int iscsi_connection_handle_scsi_cmd(iscsi_connection *conn, const iscsi_ * without a session. * * @param[in] conn Pointer to iSCSI connection, - * may NOT be NULL, so be careful. + * must NOT be NULL, so be careful. * @param[in] login_response_pdu Pointer to login response PDU. * NULL is not allowed here, so take caution. * @param[in] kvpairs Pointer to key and value pairs. - * which may NOT be NULL, so take caution. + * which must NOT be NULL, so take caution. * @return 0 on success, a negative error code otherwise. */ static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, const iscsi_pdu *login_response_pdu, const iscsi_negotiation_kvp *kvpairs) @@ -3110,7 +3110,7 @@ if ( pairs->key != NULL ) ADD_KV_INTERNAL( false, #key, value ); \ * and determines the authentication method. * * @param[in] conn Pointer to iSCSI connection, - * may NOT be NULL, so be careful. + * must NOT be NULL, so be careful. * @param[in] login_response_pdu Pointer to login response PDU. * NULL is not allowed here, so take caution. * @param[in] pairs Readily parsed key-value-pairs from according request @@ -3196,7 +3196,7 @@ static int iscsi_connection_handle_login_response(iscsi_connection *conn, iscsi_ * If a response needs to be sent, this will * be done as well. * - * @param[in] conn Pointer to iSCSI connection to handle. May + * @param[in] conn Pointer to iSCSI connection to handle. Must * NOT be NULL, so take caution. * @param[in] request_pdu Pointer to iSCSI client request PDU to handle. * May be NULL in which case an error is returned. @@ -3255,7 +3255,7 @@ static int iscsi_connection_handle_login_req(iscsi_connection *conn, iscsi_pdu * * If a response needs to be sent, this will * be done as well. * - * @param[in] conn Pointer to iSCSI connection to handle. May + * @param[in] conn Pointer to iSCSI connection to handle. Must * NOT be NULL, so take caution. * @param[in] request_pdu Pointer to iSCSI client request PDU to handle. * May be NULL in which case an error is returned. @@ -3340,7 +3340,7 @@ static int iscsi_connection_handle_text_req(iscsi_connection *conn, const iscsi_ * If a response needs to be sent, this will * be done as well. * - * @param[in] conn Pointer to iSCSI connection to handle. May + * @param[in] conn Pointer to iSCSI connection to handle. Must * NOT be NULL, so take caution. * @param[in] request_pdu Pointer to iSCSI client request PDU to handle. * May be NULL in which case an error is returned. @@ -3514,7 +3514,7 @@ static void iscsi_connection_pdu_read_loop(iscsi_connection *conn, const dnbd3_r * request data. * * @param[in] client Pointer to DNBD3 client structure, - * may NOT be NULL, so be careful. + * must 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. -- cgit v1.2.3-55-g7522 From b6abbdb600d33ae1e2fa2e3d1e9cb3c9fd429fe8 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 13 Nov 2025 14:01:10 +0100 Subject: [SERVER] iscsi: Make iscsi_connection stack-allocated --- src/server/iscsi.c | 70 +++++------------------------------------------------- 1 file changed, 6 insertions(+), 64 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 0627de4..54cc431 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -86,9 +86,6 @@ static void iscsi_strcpy_pad(char *dst, const char *src, size_t size, int pad); static uint64_t iscsi_target_node_wwn_get(const uint8_t *name); // Calculates the WWN using 64-bit IEEE Extended NAA for a name -static iscsi_connection *iscsi_connection_create(dnbd3_client_t *client); // Creates data structure for an iSCSI connection from iSCSI portal and TCP/IP socket -static void iscsi_connection_destroy(iscsi_connection *conn); // Deallocates all resources acquired by iscsi_connection_create - static bool iscsi_connection_pdu_init(iscsi_pdu *pdu, uint32_t ds_len, bool no_ds_alloc); static void iscsi_connection_pdu_destroy(const iscsi_pdu *pdu); @@ -2092,56 +2089,6 @@ static uint64_t iscsi_target_node_wwn_get(const uint8_t *name) return ((value & 0xFFFFFFULL) | 0x2000000347000000ULL | id_a); } -/** - * @brief Creates data structure for an iSCSI connection from iSCSI portal and TCP/IP socket. - * - * Creates a data structure for incoming iSCSI connection - * requests from iSCSI packet data. - * - * @param[in] client dnbd3 client to associate the connection with. - * @return Pointer to initialized iSCSI connection structure or NULL in - * case of an error (invalid iSCSI packet data or memory exhaustion). - */ -static iscsi_connection *iscsi_connection_create(dnbd3_client_t *client) -{ - iscsi_connection *conn = malloc( sizeof(iscsi_connection) ); - - if ( conn == NULL ) { - logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI connection" ); - - return NULL; - } - - conn->id = 0; - conn->client = client; - conn->flags = 0; - conn->state = ISCSI_CONNECT_STATE_NEW; - conn->cid = 0U; - conn->stat_sn = 0UL; - conn->exp_cmd_sn = 0UL; - conn->max_cmd_sn = 0UL; - - return conn; -} - -/** - * @brief Deallocates all resources acquired by iscsi_connection_create. - * - * Deallocates a data structure of an iSCSI connection - * request and all allocated hash maps which don't - * require closing of external resources like closing - * TCP/IP socket connections. - * - * @param[in] conn Pointer to iSCSI connection structure to be - * deallocated, TCP/IP connections are NOT closed by this - * function, use iscsi_connection_close for this. This may be - * NULL in which case this function does nothing. - */ -static void iscsi_connection_destroy(iscsi_connection *conn) -{ - free( conn ); -} - /** * @brief Appends a key and value pair to DataSegment packet data. * @@ -3524,23 +3471,18 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ _Static_assert( sizeof(dnbd3_request_t) <= sizeof(iscsi_bhs_packet), "DNBD3 request size larger than iSCSI BHS packet data size - Manual intervention required!" ); - iscsi_connection *conn = iscsi_connection_create( client ); - - if ( conn == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI connection" ); - - return; - } + iscsi_connection conn = { + .state = ISCSI_CONNECT_STATE_NEW, + .client = client, + }; static atomic_int CONN_ID = 0; - conn->id = ++CONN_ID; + conn.id = ++CONN_ID; - iscsi_connection_pdu_read_loop( conn, request, len ); + iscsi_connection_pdu_read_loop( &conn, request, len ); // Wait for the client to receive any pending outgoing PDUs shutdown( client->sock, SHUT_WR ); sock_setTimeout( client->sock, 100 ); while ( recv( client->sock, (void *)request, len, 0 ) > 0 ) {} - - iscsi_connection_destroy( conn ); } -- cgit v1.2.3-55-g7522 From 4a0fa5ea5f58ccfac98b33dd6161b9195b8bdcfb Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 13 Nov 2025 14:30:52 +0100 Subject: [SERVER] iscsi: Update a few doxygen blocks --- src/server/iscsi.c | 109 +++++++++++++++++++++++++---------------------------- 1 file changed, 52 insertions(+), 57 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 54cc431..04c177e 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -2128,19 +2128,14 @@ static int iscsi_append_key_value_pair_packet(const bool number, const char *key #define CLAMP(val, min, max) ((val) < (min) ? (min) : ((val) > (max) ? (max) : (val))) /** - * @brief Updates iSCSI connection and session values after being retrieved from the client. - * - * This function copies the key and value pairs into the - * internal connection and session structure and checks - * them for consistency.\n - * The TCP receive buffer will be adjusted to the new - * updated value but is never lower than 4KiB and never - * higher than 8KiB plus header overhead and a factor of - * 16 for receiving 16 packets at once. - * - * @param[in] conn Pointer to ISCSI connection which should - * be updated. - @param[in] pairs Set of readily parsed key-value-pairs to handle + * @brief Updates selected iSCSI connection options from negotiated key-value pairs. + * + * Copies and clamps a subset of negotiated options (MaxBurstLength, + * FirstBurstLength, MaxRecvDataSegmentLength) into the connection's + * options. + * + * @param[in] conn Pointer to ISCSI connection which should be updated. + * @param[in] pairs Set of readily parsed key-value pairs to apply. */ static void iscsi_connection_update_key_value_pairs(iscsi_connection *conn, const iscsi_negotiation_kvp *pairs) { @@ -2529,16 +2524,19 @@ static iscsi_bhs_packet *iscsi_connection_pdu_resize(iscsi_pdu *pdu, const uint /** * @brief Writes and sends a response PDU to the client. * - * This function sends a response PDU to the - * client after being processed by the server.\n - * If a header or data digest (CRC32C) needs to - * be calculated, this is done as well. + * Sends the provided response PDU over the TCP connection. On send failure, + * the connection state is set to ISCSI_CONNECT_STATE_EXITING. + * + * If a header or data segment is present, its size is rounded up to the iSCSI + * alignment before transmission, so the underlying buffer MUST include padding + * if neccesary. * * @param[in] conn Pointer to iSCSI connection to handle. Must - * NOT be NULL, so take caution. Will be freed after sending, - * so don't access afterwards. + * NOT be NULL, so take caution. * @param[in] pdu Pointer to iSCSI server response PDU to send. * Must NOT be NULL, so be careful. + * @retval true if the entire PDU was sent successfully. + * @retval false on failure (connection state will be set to EXITING). */ static bool iscsi_connection_pdu_write(iscsi_connection *conn, const iscsi_pdu *pdu) { @@ -3136,19 +3134,18 @@ static int iscsi_connection_handle_login_response(iscsi_connection *conn, iscsi_ } /** - * @brief Handles an incoming iSCSI payload data login request PDU. + * @brief Handles an incoming iSCSI login request PDU. * - * This function handles login request payload - * data sent by the client.\n - * If a response needs to be sent, this will - * be done as well. + * Parses the login request, builds a corresponding login response PDU and + * sends it. Performs basic validation (e.g., MaxRecvDataSegmentLength and + * login phase/state). On certain errors, a reject or error response is sent. * * @param[in] conn Pointer to iSCSI connection to handle. Must * NOT be NULL, so take caution. * @param[in] request_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. + * Must NOT be NULL. + * @return ISCSI_CONNECT_PDU_READ_OK on success; a negative + * ISCSI_CONNECT_PDU_READ_ERR_* code on error. */ static int iscsi_connection_handle_login_req(iscsi_connection *conn, iscsi_pdu *request_pdu) { @@ -3195,19 +3192,17 @@ static int iscsi_connection_handle_login_req(iscsi_connection *conn, iscsi_pdu * } /** - * @brief Handles an incoming iSCSI payload data text request PDU. + * @brief Handles an incoming iSCSI text request PDU. * - * This function handles text request payload - * data sent by the client.\n - * If a response needs to be sent, this will - * be done as well. + * Parses the key-value pairs in the text request and responds with a text + * response PDU containing the negotiated values. Multi-PDU continuation of + * text requests is not supported. * - * @param[in] conn Pointer to iSCSI connection to handle. Must - * NOT be NULL, so take caution. + * @param[in] conn Pointer to iSCSI connection to handle. Must NOT be NULL. * @param[in] request_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. + * Must NOT be NULL. + * @return ISCSI_CONNECT_PDU_READ_OK on success; a negative + * ISCSI_CONNECT_PDU_READ_ERR_* code on error or when a reject is sent. */ static int iscsi_connection_handle_text_req(iscsi_connection *conn, const iscsi_pdu *request_pdu) { @@ -3282,17 +3277,17 @@ static int iscsi_connection_handle_text_req(iscsi_connection *conn, const iscsi_ } /** - * @brief Handles an incoming iSCSI PDU. + * @brief Dispatches and handles a single incoming iSCSI request PDU. * - * If a response needs to be sent, this will - * be done as well. + * Parses the opcode from the request PDU and invokes the corresponding + * handler. On protocol errors, a REJECT may be sent. Fatal errors set the + * connection to exiting state via lower-level helpers. * - * @param[in] conn Pointer to iSCSI connection to handle. Must - * NOT be NULL, so take caution. + * @param[in] conn Pointer to iSCSI connection to handle. Must NOT be NULL. * @param[in] request_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. + * Must NOT be NULL. + * @return ISCSI_CONNECT_PDU_READ_OK on success; a negative + * ISCSI_CONNECT_PDU_READ_ERR_* code on error. */ static int iscsi_connection_pdu_handle(iscsi_connection *conn, iscsi_pdu *request_pdu) { @@ -3453,18 +3448,18 @@ static void iscsi_connection_pdu_read_loop(iscsi_connection *conn, const dnbd3_r } /** - * @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, - * must 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. + * @brief Handles an iSCSI connection until it is closed. + * + * Initializes a per-connection state object, processes incoming PDUs by + * delegating to the PDU read loop (which continues until an error, shutdown, + * or explicit exit state), and finally shuts down the write side of the socket + * while draining any remaining incoming data. + * + * @param[in] client Pointer to DNBD3 client structure. + * Must NOT be NULL. + * @param[in] request Pointer to the already-received initial bytes (partial + * Basic Header Segment) of the first iSCSI PDU. Must NOT be NULL. + * @param[in] len Length in bytes of the initial data in 'request'. */ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *request, const int len) { -- cgit v1.2.3-55-g7522 From f70b56df7cd882452e0c81377318fa203b64c3a4 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 9 Dec 2025 11:47:07 +0100 Subject: [SERVER] iscsi: Add braces --- src/server/iscsi.c | 60 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 04c177e..42912bf 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -99,9 +99,10 @@ static int iscsi_connection_handle_reject(iscsi_connection *conn, const iscsi_pd /** * @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. + * If the src string is shorter than the destination buffer, + * it will be padded with the given character. Otherwise, + * the string will be copied and truncated if necessary. + * In any case, the resulting string will NOT be null terminated. * * @param[in] dst Pointer to destination string to copy * with padding and must NOT be NULL, so be @@ -556,8 +557,9 @@ static void iscsi_scsi_task_send_reply(iscsi_connection *conn, iscsi_scsi_task * 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 ) + if ( (scsi_cmd_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) { conn->max_cmd_sn++; + } iscsi_put_be32( (uint8_t *) &scsi_response_pkt->exp_cmd_sn, conn->exp_cmd_sn ); iscsi_put_be32( (uint8_t *) &scsi_response_pkt->max_cmd_sn, conn->max_cmd_sn ); @@ -633,8 +635,9 @@ static void iscsi_scsi_task_sense_data_build(iscsi_scsi_task *scsi_task, const u */ 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 ) + if ( status == ISCSI_SCSI_STATUS_CHECK_COND ) { iscsi_scsi_task_sense_data_build( scsi_task, sense_key, asc, ascq ); + } scsi_task->status = status; } @@ -656,12 +659,13 @@ static uint64_t iscsi_scsi_lun_get_from_scsi(const int lun_id) { uint64_t iscsi_scsi_lun; - if ( lun_id < 0x100 ) + if ( lun_id < 0x100 ) { iscsi_scsi_lun = (uint64_t) (lun_id & 0xFF) << 48ULL; - else if ( lun_id < 0x4000 ) + } else if ( lun_id < 0x4000 ) { iscsi_scsi_lun = (1ULL << 62ULL) | (uint64_t) (lun_id & 0x3FFF) << 48ULL; - else + } else { iscsi_scsi_lun = 0ULL; + } return iscsi_scsi_lun; } @@ -685,12 +689,13 @@ static int iscsi_scsi_lun_get_from_iscsi(const uint64_t lun) { int lun_id = (int) (lun >> 62ULL) & 0x03; - if ( lun_id == 0x00 ) + if ( lun_id == 0x00 ) { lun_id = (int) (lun >> 48ULL) & 0xFF; - else if ( lun_id == 0x01 ) + } else if ( lun_id == 0x01 ) { lun_id = (int) (lun >> 48ULL) & 0x3FFF; - else + } else { lun_id = 0xFFFF; + } return lun_id; } @@ -910,8 +915,9 @@ static bool iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) lba = iscsi_get_be24(cdb_read_write_6->lba); xfer_len = cdb_read_write_6->xfer_len; - if ( xfer_len == 0UL ) + if ( xfer_len == 0UL ) { xfer_len = 256UL; + } iscsi_scsi_emu_block_read( image, scsi_task, lba, xfer_len ); @@ -959,10 +965,11 @@ static bool iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) lba = iscsi_scsi_emu_block_get_count( image ) - 1ULL; - if ( lba > 0xFFFFFFFFULL ) + if ( lba > 0xFFFFFFFFULL ) { buf->lba = 0xFFFFFFFFUL; // Minus one does not require endianess conversion - else + } else { iscsi_put_be32( (uint8_t *) &buf->lba, (uint32_t) lba ); + } iscsi_put_be32( (uint8_t *) &buf->block_len, ISCSI_SCSI_EMU_LOGICAL_BLOCK_SIZE ); @@ -1144,8 +1151,9 @@ static int iscsi_scsi_emu_primary_inquiry(const dnbd3_image_t *image, iscsi_scsi alloc_len = (uint) strlen( name ); - if ( alloc_len >= (len - sizeof(iscsi_scsi_vpd_page_inquiry_data_packet)) ) + if ( alloc_len >= (len - sizeof(iscsi_scsi_vpd_page_inquiry_data_packet)) ) { alloc_len = (uint) ((len - sizeof(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(iscsi_scsi_vpd_page_inquiry_data_packet)) ); @@ -1456,8 +1464,9 @@ static int iscsi_scsi_emu_primary_inquiry(const dnbd3_image_t *image, iscsi_scsi 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(iscsi_scsi_ext_inquiry_data_packet) - offsetof(iscsi_scsi_ext_inquiry_data_packet, version_desc[4])) ) + if ( alloc_len > (sizeof(iscsi_scsi_ext_inquiry_data_packet) - offsetof(iscsi_scsi_ext_inquiry_data_packet, version_desc[4])) ) { alloc_len = (sizeof(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; @@ -1844,10 +1853,11 @@ static int iscsi_scsi_emu_primary_mode_sense(dnbd3_image_t *image, iscsi_scsi_ta if ( block_desc_len == sizeof(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 *) (buffer + hdr_len); - if ( num_blocks > 0xFFFFFFFFULL ) + if ( num_blocks > 0xFFFFFFFFULL ) { lba_parameter_block_desc->num_blocks = 0xFFFFFFFFUL; // Minus one does not require endianess conversion - else + } 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 ); @@ -2207,8 +2217,9 @@ static int iscsi_connection_pdu_login_response_init(iscsi_pdu *login_response_pd 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 ) + 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); + } login_response_pkt->isid = login_req_pkt->isid; login_response_pkt->tsih = 0; @@ -2683,8 +2694,9 @@ static int iscsi_connection_handle_cmd_sn(iscsi_connection *conn, iscsi_pdu *req return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } - if ( ((scsi_cmd_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0) && (opcode != ISCSI_OPCODE_CLIENT_SCSI_DATA_OUT) ) + if ( ((scsi_cmd_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0) && (opcode != ISCSI_OPCODE_CLIENT_SCSI_DATA_OUT) ) { conn->exp_cmd_sn++; + } return ISCSI_CONNECT_PDU_READ_OK; } @@ -2830,8 +2842,9 @@ static int iscsi_connection_handle_nop(iscsi_connection *conn, const iscsi_pdu * if ( target_xfer_tag != 0xFFFFFFFFUL ) // If the initiator tag is not the special value, the target tag has to be. return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_INVALID_PDU_FIELD ); - if ( ds_len > (uint32_t)conn->opts.MaxRecvDataSegmentLength ) + if ( ds_len > (uint32_t)conn->opts.MaxRecvDataSegmentLength ) { ds_len = conn->opts.MaxRecvDataSegmentLength; + } iscsi_pdu CLEANUP_PDU response_pdu; if ( !iscsi_connection_pdu_init( &response_pdu, ds_len, false ) ) @@ -2856,8 +2869,9 @@ static int iscsi_connection_handle_nop(iscsi_connection *conn, const iscsi_pdu * iscsi_put_be32( (uint8_t *) &nop_in_pkt->stat_sn, conn->stat_sn++ ); // Inc } - if ( nop_out_pkt == NULL || (nop_out_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) + if ( nop_out_pkt == NULL || (nop_out_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) { conn->max_cmd_sn++; + } iscsi_put_be32( (uint8_t *) &nop_in_pkt->exp_cmd_sn, conn->exp_cmd_sn ); iscsi_put_be32( (uint8_t *) &nop_in_pkt->max_cmd_sn, conn->max_cmd_sn ); @@ -3029,9 +3043,11 @@ if ( pairs->key != -1 ) ADD_KV_INTERNAL( true, #key, (const char *)(size_t)(valu # define ADD_KV_PLAIN_STR(key, value) do { \ if ( pairs->key != NULL ) ADD_KV_INTERNAL( false, #key, value ); \ } while (0) + // Reply with these settings with actually negotiated values ADD_KV_OPTION_INT( MaxRecvDataSegmentLength ); ADD_KV_OPTION_INT( MaxBurstLength ); ADD_KV_OPTION_INT( FirstBurstLength ); + // These are always hard-coded to specific value, we don't support anything else ADD_KV_PLAIN_INT( MaxConnections, 1 ); ADD_KV_PLAIN_INT( ErrorRecoveryLevel, 0 ); ADD_KV_PLAIN_STR( HeaderDigest, "None" ); -- cgit v1.2.3-55-g7522 From c7b4578e14155a5aeb6c68f5eee3dc82f367d672 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 9 Dec 2025 12:29:42 +0100 Subject: [SERVER] iscsi: More comments --- src/server/iscsi.c | 72 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 42912bf..a24a727 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -1169,6 +1169,14 @@ static int iscsi_scsi_emu_primary_inquiry(const dnbd3_image_t *image, iscsi_scsi const uint dev_name_len = (uint) (strlen( image->name ) + 1U); const uint port_name_len = (uint) (strlen( port_name ) + 1U); + // Calculate total length required for all design descriptors we are about to add: + // 1. IEEE NAA Extended + // 2. T10 Vendor ID + // 3. SCSI Device Name + // 4. SCSI Target Port Name + // 5. Relative Target Port + // 6. Target Port Group + // 7. Logical Unit Group alloc_len = (sizeof(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(iscsi_scsi_vpd_page_design_desc_ieee_naa_ext_inquiry_data_packet)); // 64-bit IEEE NAA Extended alloc_len += (sizeof(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(iscsi_scsi_vpd_page_design_desc_t10_vendor_id_inquiry_data_packet)); // T10 Vendor ID alloc_len += (uint) (sizeof(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + ISCSI_ALIGN(dev_name_len, ISCSI_ALIGN_SIZE)); // SCSI Device Name @@ -1185,6 +1193,7 @@ static int iscsi_scsi_emu_primary_inquiry(const dnbd3_image_t *image, iscsi_scsi 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; + // 1. Descriptor: IEEE NAA Extended 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(ISCSI_DEFAULT_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; @@ -1195,6 +1204,7 @@ static int iscsi_scsi_emu_primary_inquiry(const dnbd3_image_t *image, iscsi_scsi alloc_len = (sizeof(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(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); + // 2. Descriptor: T10 Vendor ID 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(ISCSI_DEFAULT_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; @@ -1209,6 +1219,7 @@ static int iscsi_scsi_emu_primary_inquiry(const dnbd3_image_t *image, iscsi_scsi alloc_len += (sizeof(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(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(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(iscsi_scsi_vpd_page_design_desc_t10_vendor_id_inquiry_data_packet))); + // 3. Descriptor: SCSI Device Name 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(ISCSI_DEFAULT_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; @@ -1217,6 +1228,7 @@ static int iscsi_scsi_emu_primary_inquiry(const dnbd3_image_t *image, iscsi_scsi alloc_len += (uint) (sizeof(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(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + vpd_page_design_desc_inquiry_data_pkt->len)); + // 4. Descriptor: SCSI Target Port Name 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(ISCSI_DEFAULT_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; @@ -1225,6 +1237,7 @@ static int iscsi_scsi_emu_primary_inquiry(const dnbd3_image_t *image, iscsi_scsi alloc_len += (uint) (sizeof(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(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + vpd_page_design_desc_inquiry_data_pkt->len)); + // 5. Descriptor: Relative Target Port 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(ISCSI_DEFAULT_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; @@ -1238,6 +1251,7 @@ static int iscsi_scsi_emu_primary_inquiry(const dnbd3_image_t *image, iscsi_scsi alloc_len += (sizeof(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(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(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(iscsi_scsi_vpd_page_design_desc_rel_target_port_inquiry_data_packet))); + // 6. Descriptor: Target Port Group 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(ISCSI_DEFAULT_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; @@ -1251,6 +1265,7 @@ static int iscsi_scsi_emu_primary_inquiry(const dnbd3_image_t *image, iscsi_scsi alloc_len += (sizeof(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(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(iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(iscsi_scsi_vpd_page_design_desc_target_port_group_inquiry_data_packet))); + // 7. Descriptor: Logical Unit Group 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(ISCSI_DEFAULT_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; @@ -2505,16 +2520,17 @@ static iscsi_bhs_packet *iscsi_connection_pdu_resize(iscsi_pdu *pdu, const uint return NULL; } if ( !old_alloced ) { - // Old was in internal buffer, copy contents + // Old was in internal buffer, copy contents to the new heap buffer memcpy( bhs_pkt, pdu->internal_buffer, MIN(new_len, old_len) ); } // Update PDU's BHS pointer pdu->big_alloc = bhs_pkt; pdu->bhs_pkt = bhs_pkt; } else { - // New block fits into internal buffer - ignore for now and keep in big buffer - // to avoid needless overhead - PDUs are short-lived anyways. - // Keep using old BHS pointer + // New block fits into internal buffer. + // If we are already using big_alloc, we keep it to avoid realloc/free overhead, + // as PDUs are short-lived. + // Keep using old BHS pointer (which might be big_alloc or internal_buffer) bhs_pkt = pdu->bhs_pkt; } } @@ -2915,13 +2931,14 @@ static int iscsi_connection_handle_scsi_cmd(iscsi_connection *conn, const iscsi_ .scsi_task.connection = conn, }; + // Per iSCSI, READ/WRITE bits in flags_task indicate data direction for this CDB if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_READ) != 0 ) { task.scsi_task.is_read = true; } else { if ( exp_xfer_len != 0UL ) { // Not a read request, but expecting data - not valid iscsi_scsi_task_status_set( &task.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 ); + ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE ); handled = true; } } @@ -2929,6 +2946,7 @@ static int iscsi_connection_handle_scsi_cmd(iscsi_connection *conn, const iscsi_ if ( !handled ) { task.scsi_task.is_write = (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_WRITE) != 0; + // Single-LUN target for now, reject unknown LUNs with ILLEGAL REQUEST if ( task.lun_id != ISCSI_DEFAULT_LUN ) { logadd( LOG_WARNING, "Received SCSI command for unknown LUN %d", task.lun_id ); iscsi_scsi_task_status_set( &task.scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, @@ -2937,13 +2955,13 @@ static int iscsi_connection_handle_scsi_cmd(iscsi_connection *conn, const iscsi_ } else { task.scsi_task.status = ISCSI_SCSI_STATUS_GOOD; - handled = iscsi_scsi_emu_block_process( &task.scsi_task ) - || iscsi_scsi_emu_primary_process( &task.scsi_task ); + // Try block commands first (READ/WRITE family), then primary (INQUIRY, MODE SENSE, etc.) + handled = iscsi_scsi_emu_block_process( &task.scsi_task ) || iscsi_scsi_emu_primary_process( &task.scsi_task ); if ( !handled ) { iscsi_scsi_task_status_set( &task.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 ); + ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_COMMAND_OPERATION_CODE, + ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE ); } } } @@ -3165,6 +3183,9 @@ static int iscsi_connection_handle_login_response(iscsi_connection *conn, iscsi_ */ static int iscsi_connection_handle_login_req(iscsi_connection *conn, iscsi_pdu *request_pdu) { + // Reject malformed login PDUs: + // - DataSegmentLength must fit our initial receive buffer (we don't support multi-PDU login here) + // - Login is only valid in NEW state (before full feature phase) if ( request_pdu->ds_len > ISCSI_DEFAULT_RECV_DS_LEN || conn->state != ISCSI_CONNECT_STATE_NEW ) return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); @@ -3172,6 +3193,7 @@ static int iscsi_connection_handle_login_req(iscsi_connection *conn, iscsi_pdu * request_pdu->cmd_sn = iscsi_get_be32(login_req_pkt->cmd_sn); + // Prepare a response PDU; helper will size DS as needed later iscsi_pdu CLEANUP_PDU login_response_pdu; if ( !iscsi_connection_pdu_init( &login_response_pdu, 0, false ) ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; @@ -3179,12 +3201,13 @@ static int iscsi_connection_handle_login_req(iscsi_connection *conn, iscsi_pdu * int rc = iscsi_connection_pdu_login_response_init( &login_response_pdu, request_pdu ); if ( rc < 0 ) { - // response_init set an error code in the response pdu, send it right away and bail out + // response_init already encoded an error in the response PDU - send and bail return iscsi_send_login_response_pdu( conn, &login_response_pdu ); } iscsi_negotiation_kvp pairs; iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu.bhs_pkt; + // Parse key=value pairs from the login text payload rc = iscsi_parse_login_key_value_pairs( &pairs, (uint8_t *) request_pdu->ds_cmd_data, request_pdu->ds_len ); if ( rc < 0 ) { @@ -3194,14 +3217,17 @@ static int iscsi_connection_handle_login_req(iscsi_connection *conn, iscsi_pdu * return iscsi_send_login_response_pdu( conn, &login_response_pdu ); } + // Handle security/operational negotiation for this stage rc = iscsi_connection_handle_login_phase_none( conn, &login_response_pdu, &pairs ); if ( rc != ISCSI_CONNECT_PDU_READ_OK ) { return iscsi_send_login_response_pdu( conn, &login_response_pdu ); } + // Possibly transition to next stage depending on flags iscsi_connection_handle_login_response( conn, &login_response_pdu, &pairs ); if ( conn->state == ISCSI_CONNECT_STATE_NORMAL_SESSION ) { + // Record ConnectionID from request once we enter full feature phase conn->cid = iscsi_get_be16(login_req_pkt->cid); } return iscsi_send_login_response_pdu( conn, &login_response_pdu ); @@ -3312,48 +3338,55 @@ static int iscsi_connection_pdu_handle(iscsi_connection *conn, iscsi_pdu *reques const uint8_t opcode = ISCSI_GET_OPCODE(request_pdu->bhs_pkt->opcode); if ( conn->state == ISCSI_CONNECT_STATE_NEW ) { - // Fresh connection, not logged in yet - we only support LOGIN in this state + // Fresh connection, not logged in yet - per RFC7143 only LOGIN PDUs are valid here. if ( opcode == ISCSI_OPCODE_CLIENT_LOGIN_REQ ) { rc = iscsi_connection_handle_login_req( conn, request_pdu ); } else { rc = iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); } } else if ( conn->state == ISCSI_CONNECT_STATE_EXITING ) { - // Exiting, nothing to do + // Already transitioning to close: ignore further work but report OK so caller can unwind. rc = ISCSI_CONNECT_PDU_READ_OK; } else if ( conn->state == ISCSI_CONNECT_STATE_NORMAL_SESSION ) { - // Normal operation + // Normal full-feature phase operation. + // First validate/advance CmdSN window semantics (ExpCmdSN/MaxCmdSN handling). rc = iscsi_connection_handle_cmd_sn( conn, request_pdu ); if ( rc != 0 ) return rc; switch ( opcode ) { case ISCSI_OPCODE_CLIENT_NOP_OUT : { + // Keep-alive ping from initiator or response to our NOP-In rc = iscsi_connection_handle_nop( conn, request_pdu ); break; } case ISCSI_OPCODE_CLIENT_SCSI_CMD : { + // SCSI CDB request - may entail data-in or data-out depending on flags rc = iscsi_connection_handle_scsi_cmd( conn, request_pdu ); break; } case ISCSI_OPCODE_CLIENT_TEXT_REQ : { + // Text negotiation/SendTargets style key=value exchange rc = iscsi_connection_handle_text_req( conn, request_pdu ); break; } case ISCSI_OPCODE_CLIENT_LOGOUT_REQ : { + // Session/connection logout (transition to exiting handled in callee) rc = iscsi_connection_handle_logout_req( conn, request_pdu ); break; } case ISCSI_OPCODE_CLIENT_TASK_FUNC_REQ : { + // Task management functions (ABORT TASK, CLEAR TASK SET, etc.) rc = iscsi_connection_handle_task_func_req( conn, request_pdu ); break; } default : { + // Unknown/unsupported opcode - protocol error rc = iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); break; @@ -3395,12 +3428,13 @@ static void iscsi_connection_pdu_read_loop(iscsi_connection *conn, const dnbd3_r memcpy( request_pdu.bhs_pkt, request, len ); if ( (size_t)sock_recv( conn->client->sock, ((uint8_t *)request_pdu.bhs_pkt) + len, sizeof(iscsi_bhs_packet) - len ) != sizeof(iscsi_bhs_packet) - len ) { - logadd( LOG_INFO, "Cannot receive first BHS for client %s", conn->client->hostName ); + logadd( LOG_INFO, "Cannot receive first BHS from client %s", conn->client->hostName ); return; } do { // 2) Evaluate BHS regarding length of AHS and DS + // total_ahs_len is encoded in 4-byte units per RFC; ds_len is 24-bit big-endian. iscsi_bhs_packet *bhs_pkt = request_pdu.bhs_pkt; const uint ahs_len = ((uint) bhs_pkt->total_ahs_len * ISCSI_ALIGN_SIZE); const uint32_t ds_len = iscsi_get_be24(bhs_pkt->ds_len); @@ -3408,13 +3442,14 @@ static void iscsi_connection_pdu_read_loop(iscsi_connection *conn, const dnbd3_r bhs_pkt = iscsi_connection_pdu_resize( &request_pdu, ahs_len, ds_len ); if ( bhs_pkt == NULL ) { + // Allocation/size sanity failed; cannot proceed with this PDU logadd( LOG_WARNING, "Cannot resize PDU for client %s", conn->client->hostName ); break; } // 3) Receive the optional AHS if ( ahs_len != 0 && sock_recv( conn->client->sock, request_pdu.ahs_pkt, ahs_len ) != ahs_len ) { - logadd( LOG_DEBUG1, "Could not receive AHS for client %s", conn->client->hostName ); + logadd( LOG_DEBUG1, "Could not receive AHS from client %s", conn->client->hostName ); break; } @@ -3423,7 +3458,7 @@ static void iscsi_connection_pdu_read_loop(iscsi_connection *conn, const dnbd3_r const uint32_t padded_ds_len = ISCSI_ALIGN( request_pdu.ds_len, ISCSI_ALIGN_SIZE ); if ( sock_recv( conn->client->sock, request_pdu.ds_cmd_data, padded_ds_len ) != padded_ds_len ) { - logadd( LOG_DEBUG1, "Could not receive DS for client %s", conn->client->hostName ); + logadd( LOG_DEBUG1, "Could not receive DS from client %s", conn->client->hostName ); break; } } @@ -3431,6 +3466,7 @@ static void iscsi_connection_pdu_read_loop(iscsi_connection *conn, const dnbd3_r // 5) Handle PDU if ( iscsi_connection_pdu_handle( conn, &request_pdu ) != ISCSI_CONNECT_PDU_READ_OK || conn->state == ISCSI_CONNECT_STATE_EXITING ) { + // Either handler reported a fatal/terminal condition or connection is shutting down break; } @@ -3449,7 +3485,7 @@ static void iscsi_connection_pdu_read_loop(iscsi_connection *conn, const dnbd3_r // 1) Receive entire BHS ret = sock_recv( conn->client->sock, request_pdu.bhs_pkt, sizeof(iscsi_bhs_packet) ); if ( ret == -1 && errno == EAGAIN ) { - // Receive timeout - send a NOP-In and try recv one more time + // Receive timeout - send a NOP-In and try recv one more time; a healthy initiator should reply NOP-Out if ( iscsi_connection_handle_nop( conn, NULL ) != ISCSI_CONNECT_PDU_READ_OK ) { logadd( LOG_DEBUG1, "Cannot send NOP-In to idle client %s - connection dead", conn->client->hostName ); break; @@ -3457,7 +3493,7 @@ static void iscsi_connection_pdu_read_loop(iscsi_connection *conn, const dnbd3_r ret = sock_recv( conn->client->sock, request_pdu.bhs_pkt, sizeof(iscsi_bhs_packet) ); } if ( ret != sizeof(iscsi_bhs_packet) ) { - logadd( LOG_DEBUG1, "Cannot receive BHS for client %s (%d/%d)", conn->client->hostName, (int)ret, (int)errno ); + logadd( LOG_DEBUG1, "Cannot receive BHS from client %s (%d/%d)", conn->client->hostName, (int)ret, (int)errno ); break; } } while ( !_shutdown ); -- cgit v1.2.3-55-g7522