summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Vater2025-09-29 09:56:10 +0200
committerSebastian Vater2025-09-29 09:56:10 +0200
commit267454de87a92db1dbe14b39fea65c48cb3da0d0 (patch)
tree85cad220a825ce01691c913753566568bf197352
parentImplemented iSCSI DNBD3 image name and WWN extraction from IQN. Also fixed so... (diff)
downloaddnbd3-267454de87a92db1dbe14b39fea65c48cb3da0d0.tar.gz
dnbd3-267454de87a92db1dbe14b39fea65c48cb3da0d0.tar.xz
dnbd3-267454de87a92db1dbe14b39fea65c48cb3da0d0.zip
Fixed various memory leaks reported by valgrind. Also fixed some bugs in DNBD3 image WWN retrieval handling. Finally, did huge code refactoring.
-rw-r--r--Doxyfile1
-rw-r--r--src/server/iscsi.c2595
-rw-r--r--src/server/iscsi.h565
3 files changed, 1589 insertions, 1572 deletions
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/src/server/iscsi.c b/src/server/iscsi.c
index a65009b..05c8876 100644
--- a/src/server/iscsi.c
+++ b/src/server/iscsi.c
@@ -30,6 +30,7 @@
#include <strings.h>
#include <sys/socket.h>
#include <sys/types.h>
+#include <dnbd3/config.h>
#include <dnbd3/shared/log.h>
#include <dnbd3/shared/sockhelper.h>
#include <dnbd3/types.h>
@@ -500,230 +501,6 @@ void iscsi_strcpy_pad(char *dst, const char *src, const size_t size, const int p
}
/**
- * @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.
- * */
-void iscsi_list_create(iscsi_list *list)
-{
- list->head = (iscsi_node *) &list->tail;
- list->tail = NULL;
- 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.
- */
-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.
- */
-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.
- */
-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.
- */
-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.
- */
-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.
- */
-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.
- */
-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.
- */
-iscsi_node *iscsi_list_peek(const iscsi_list *list)
-{
- iscsi_node *head = list->head;
-
- return (head->succ != NULL) ? head : NULL;
-}
-
-/**
* @brief Creates an empty hash map with either specified or default capacity.
*
* Creates a ultra hardcore speed optimized empty
@@ -781,12 +558,11 @@ iscsi_hashmap *iscsi_hashmap_create(const uint capacity)
return NULL;
}
+ iscsi_list_create( &map->list );
+
+ map->last_insert_id = 0ULL;
map->cap_load = (uint) ((map->capacity * 3U) >> 2U); // 75% of capacity
map->count = 0U;
- map->removed_count = 0U;
- map->first = NULL;
- map->last = (iscsi_hashmap_bucket *) &map->first;
- map->last_insert_id = 0ULL;
return map;
}
@@ -806,149 +582,13 @@ 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 );
- free( 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
- * 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 - 1U));
-
- for ( ;; ) {
- iscsi_hashmap_bucket *entry = &map->buckets[index];
-
- if ( entry->key == NULL ) {
- *entry = *old_entry;
-
- return entry;
- }
-
- index = ((index + 1) & (map->capacity - 1U));
- }
-}
-
-/**
- * @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 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 = (uint) ((map->capacity * 3U) >> 2U); // 75% of capacity
- map->last = (iscsi_hashmap_bucket *) &map->first;
- map->count -= map->removed_count;
- map->removed_count = 0U;
-
- do {
- iscsi_hashmap_bucket *current = map->last->next;
-
- if ( current->key == NULL ) {
- map->last->next = current->next;
-
- continue;
+ map->buckets = NULL;
}
- 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;
-}
-
-/**
- * @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_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);
-}
-
-/**
- * @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.
- */
-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 - 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)) )
- return entry;
-
- index = ((index + 1UL) & (map->capacity - 1U));
+ free( map );
}
}
@@ -970,8 +610,8 @@ static iscsi_hashmap_bucket *iscsi_hashmap_find_entry(iscsi_hashmap *map, const
*/
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 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" );
@@ -1100,7 +740,176 @@ int iscsi_hashmap_key_destroy_value_callback(uint8_t *key, const size_t key_size
}
/**
- * @brief Assigns key / value pair to hash map at the tail of linked list without making copies.
+ * @brief Compares two hash keys with equal length match.
+ *
+ * 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] 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 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.
+ */
+static iscsi_hashmap_bucket *iscsi_hashmap_find_entry(iscsi_hashmap *map, const uint8_t *key, size_t key_size, uint32_t hash)
+{
+ 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) && iscsi_hashmap_key_eq( (uint64_t *) entry->key, (uint64_t *) key, num_blocks )) )
+ return entry;
+
+ index = ((index + 1UL) & (map->capacity - 1U));
+ }
+}
+
+/**
+ * @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;
+
+ do {
+ hash ^= *hash_data++;
+ hash *= ISCSI_HASHMAP_HASH_MUL;
+ } while ( --num_blocks > 0UL );
+
+ return (uint32_t) (hash ^ hash >> 32ULL);
+}
+
+/**
+ * @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
+ * 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 - 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 Resizes a hash map by doubling its bucket capacity.
+ *
+ * Resizes a hash map by doubling its bucket capacity 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;
+ iscsi_list old_list = {map->list.head, map->list.tail, map->list.pred};
+
+ map->capacity <<= ISCSI_HASHMAP_RESIZE_SHIFT;
+
+ map->buckets = (iscsi_hashmap_bucket *) calloc( map->capacity, sizeof(struct iscsi_hashmap_bucket) );
+
+ if ( map->buckets == NULL ) {
+ map->capacity = old_capacity;
+ map->buckets = old_buckets;
+
+ return -1;
+ }
+
+ map->cap_load = (uint) ((map->capacity * 3U) >> 2U); // 75% of capacity
+
+ iscsi_list_clear( &map->list );
+
+ iscsi_hashmap_bucket *current;
+ iscsi_hashmap_bucket *tmp;
+
+ iscsi_list_foreach_safe_node ( &old_list, current, tmp ) {
+ if ( current->key == NULL )
+ continue;
+
+ current = iscsi_hashmap_resize_entry( map, current );
+
+ iscsi_list_enqueue( &map->list, &current->node );
+ }
+
+ free( old_buckets );
+
+ return 0;
+}
+
+/**
+ * @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
@@ -1131,13 +940,11 @@ int iscsi_hashmap_put(iscsi_hashmap *map, uint8_t *key, const size_t key_size, u
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++;
@@ -1152,7 +959,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
@@ -1186,20 +993,18 @@ int iscsi_hashmap_get_put(iscsi_hashmap *map, uint8_t *key, const size_t key_siz
if ( ((map->count + 1U) > map->cap_load) && (iscsi_hashmap_resize( map ) < 0) )
return -1;
- const uint32_t hash = iscsi_hashmap_hash_data( key, key_size );
+ const uint32_t hash = iscsi_hashmap_key_hash_data( key, key_size );
iscsi_hashmap_bucket *entry = iscsi_hashmap_find_entry( map, key, key_size, hash );
if ( entry->key == NULL ) {
- map->last->next = entry;
- map->last = entry;
- entry->next = NULL;
-
- map->count++;
+ iscsi_list_enqueue( &map->list, &entry->node );
- entry->value = *out_in_value;
entry->key = key;
entry->key_size = key_size;
entry->hash = hash;
+ entry->value = *out_in_value;
+
+ map->count++;
return 0;
}
@@ -1252,21 +1057,19 @@ int iscsi_hashmap_put_free(iscsi_hashmap *map, uint8_t *key, const size_t key_si
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;
}
@@ -1294,7 +1097,7 @@ int iscsi_hashmap_put_free(iscsi_hashmap *map, uint8_t *key, const size_t key_si
*/
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);
@@ -1326,7 +1129,7 @@ bool iscsi_hashmap_contains(iscsi_hashmap *map, const uint8_t *key, const size_t
*/
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;
@@ -1335,12 +1138,11 @@ 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.
+ * @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.
+ * hash map. Removing sets the buckets key and
+ * value to NULL.
* If the specified key already has been removed,
* this function will do nothing.
*
@@ -1352,30 +1154,30 @@ int iscsi_hashmap_get(iscsi_hashmap *map, const uint8_t *key, const size_t key_s
*/
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.
+ * 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.
@@ -1394,46 +1196,45 @@ void iscsi_hashmap_remove(iscsi_hashmap *map, const uint8_t *key, const size_t k
*/
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.
+ * 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. 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.\n
+ * 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.
@@ -1441,8 +1242,7 @@ uint iscsi_hashmap_size(const iscsi_hashmap *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
+ * 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
@@ -1454,508 +1254,24 @@ uint iscsi_hashmap_size(const iscsi_hashmap *map)
*/
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;
- }
-
- bhs_pkt->opcode = 0U; // Initialize everything to zero
- bhs_pkt->opcode_fields[0] = 0U;
- bhs_pkt->opcode_fields[1] = 0U;
- bhs_pkt->opcode_fields[2] = 0U;
- bhs_pkt->total_ahs_len = 0U;
- bhs_pkt->ds_len[0] = 0U;
- bhs_pkt->ds_len[1] = 0U;
- bhs_pkt->ds_len[2] = 0U;
- 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;
-}
-
-/**
- * @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 );
-}
-
-/**
- * @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!\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 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 = (uint32_t) (old_pkt_size + iscsi_align(ahs_len, ISCSI_ALIGN_SIZE));
-
- 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;
- }
-
- 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((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;
-}
-
-/**
- * @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
- * 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 == 0U )
- return 0;
-
- iscsi_ahs_packet *ahs_pkt = (iscsi_ahs_packet *) ((iscsi_bhs_packet *) packet_data + 1); // First AHS packet
- int count = 0;
- 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 = (iscsi_ahs_packet *) (((uint8_t *) ahs_pkt) + (len - offsetof(struct iscsi_ahs_packet, data))); // Advance pointer to next AHS packet
- count++;
- }
-
- return count;
-}
-
-/**
- * @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 == 0U) )
- 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-- < 0 )
- 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 = (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" );
-
- 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.
- *
- * 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;
-
- 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) + header_digest_size;
- const uint32_t new_pkt_size = (uint32_t) (old_pkt_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( (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;
-}
-
-/// 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[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, ISCSI_DIGEST_SIZE), ISCSI_CRC32C_INITIAL ) ^ ISCSI_CRC32C_XOR;
-
- iscsi_put_be32( hdr_digest, 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] 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;
-}
-
-/**
- * @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, ISCSI_DIGEST_SIZE);
- 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 );
-}
-
-/**
- * @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, ISCSI_DIGEST_SIZE);
- 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;
-}
-
-/**
- * @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 ( 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 = (uint) (key_end - packet_data);
-
- if ( key_len == 0U )
- 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 = (uint) strnlen( (char *) (key_end + 1UL), (key_val_len - key_len - 1U) );
- const uint max_len = (memcmp( packet_data, "CHAP_C=", (key_len + 1U) ) == 0) || (memcmp( packet_data, "CHAP_R=", (key_len + 1U) ) == 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 (int) (key_len + 1U + val_len + 1U); // Number of bytes for processed key / value pair (+1 for '=' and NUL terminator)
-}
-
-/**
- * @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 == 0U )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Zero length is not allowed -> invalid iSCSI packet data
-
- int 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 )
- return rc;
-
- offset += rc;
- }
-
- 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;
-}
-
-/**
* @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
@@ -2023,7 +1339,7 @@ static int iscsi_parse_text_key_value_pair(iscsi_hashmap *key_value_pairs, const
return -1;
}
- uint8_t *hash_val = (uint8_t *) malloc( iscsi_align(val_len, ISCSI_TEXT_VALUE_ALIGN) );
+ uint8_t *hash_val = (uint8_t *) malloc( ISCSI_ALIGN(val_len, ISCSI_TEXT_VALUE_ALIGN) );
if ( hash_val == NULL ) {
logadd( LOG_ERROR, "iscsi_parse_text_key_value_pair: Out of memory allocating memory for value string" );
@@ -2185,7 +1501,7 @@ static int iscsi_add_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_
}
const uint val_len = (uint) (strlen( (char *) value ) + 1U);
- uint8_t *hash_val = (uint8_t *) malloc( iscsi_align(val_len, ISCSI_TEXT_VALUE_ALIGN) );
+ uint8_t *hash_val = (uint8_t *) malloc( ISCSI_ALIGN(val_len, ISCSI_TEXT_VALUE_ALIGN) );
if ( hash_val == NULL ) {
logadd( LOG_ERROR, "iscsi_add_key_value_pair: Out of memory allocating string value" );
@@ -2229,7 +1545,7 @@ static int iscsi_update_key_value_pair(iscsi_hashmap *key_value_pairs, const uin
}
const uint val_len = (uint) (strlen( (char *) value ) + 1U);
- uint8_t *hash_val = (uint8_t *) malloc( iscsi_align(val_len, ISCSI_TEXT_VALUE_ALIGN) );
+ uint8_t *hash_val = (uint8_t *) malloc( ISCSI_ALIGN(val_len, ISCSI_TEXT_VALUE_ALIGN) );
if ( hash_val == NULL ) {
logadd( LOG_ERROR, "iscsi_update_key_value_pair: Out of memory allocating string value" );
@@ -2287,7 +1603,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 int32_t value)
{
- const uint8_t *hash_val = iscsi_sprintf_alloc( "%d", value );
+ const uint8_t *hash_val = iscsi_sprintf_alloc( "%" PRId32, value );
if ( hash_val == NULL ) {
logadd( LOG_ERROR, "iscsi_add_int_key_value_pair: Out of memory allocating integer value." );
@@ -2317,7 +1633,7 @@ static int iscsi_add_int_key_value_pair(iscsi_hashmap *key_value_pairs, const ui
*/
static int iscsi_update_int_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int32_t value)
{
- const uint8_t *hash_val = iscsi_sprintf_alloc( "%d", value );
+ const 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." );
@@ -2506,11 +1822,6 @@ void iscsi_task_destroy_callback(iscsi_scsi_task *scsi_task)
iscsi_task *sub_task;
iscsi_task *tmp;
- iscsi_list_foreach_safe_node ( &task->sub_tasks, sub_task, tmp ) {
- iscsi_list_remove( &sub_task->node );
- iscsi_task_destroy( sub_task );
- }
-
if ( task->parent != NULL ) {
if ( (task->scsi_task.flags & ISCSI_SCSI_TASK_FLAGS_XFER_READ) != 0 )
task->conn->scsi_data_in_cnt--;
@@ -2616,13 +1927,9 @@ static void iscsi_task_xfer_complete_process_read_sub_tasks(iscsi_connection *co
iscsi_task *tmp;
iscsi_list_foreach_safe_node ( &primary_task->sub_tasks, sub_task, tmp ) {
- logadd( LOG_ERROR, "DEBUG iscsi_task_xfer_complete_process_read_sub_tasks: xfer_pos subtask->xfer_pos = %d, subtask->xfer_pos = %d, subtask->pos = %d, subtask->len = %d, scsi->xfer_pos = %d, scsi->xfer_len = %d, scsi->pos = %d, scsi->len = %d", sub_task->des_data_xfer_pos, sub_task->des_data_xfer_len, sub_task->pos, sub_task->len, sub_task->scsi_task.xfer_pos, sub_task->scsi_task.xfer_len, sub_task->scsi_task.pos, sub_task->scsi_task.len );
-
if ( primary_task->des_data_xfer_pos != sub_task->scsi_task.pos )
break;
- logadd( LOG_ERROR, "DEBUG iscsi_task_xfer_complete_process_read_sub_tasks: RESPONDING: xfer_pos subtask->xfer_pos = %d, subtask->xfer_pos = %d, subtask->pos = %d, subtask->len = %d, scsi->xfer_pos = %d, scsi->xfer_len = %d, scsi->pos = %d, scsi->len = %d", sub_task->des_data_xfer_pos, sub_task->des_data_xfer_len, sub_task->pos, sub_task->len, sub_task->scsi_task.xfer_pos, sub_task->scsi_task.xfer_len, sub_task->scsi_task.pos, sub_task->scsi_task.len );
-
iscsi_list_remove( &sub_task->node );
primary_task->des_data_xfer_pos += sub_task->scsi_task.len;
@@ -2680,33 +1987,22 @@ void iscsi_task_xfer_complete_process_read(iscsi_connection *conn, iscsi_task *t
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;
- }
- }
+ } else if ( task->scsi_task.pos != primary_task->des_data_xfer_pos ) {
+ iscsi_task *sub_task;
- iscsi_list_enqueue( &primary_task->sub_tasks, &task->node );
+ 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 );
- iscsi_list_foreach_node ( &primary_task->sub_tasks, sub_task ) {
- logadd( LOG_ERROR, "DEBUG iscsi_task_xfer_complete_process_read: pos NE xfer_pos subtask->xfer_pos = %d, subtask->xfer_pos = %d, subtask->pos = %d, subtask->len = %d, scsi->xfer_pos = %d, scsi->xfer_len = %d, scsi->pos = %d, scsi->len = %d", sub_task->des_data_xfer_pos, sub_task->des_data_xfer_len, sub_task->pos, sub_task->len, sub_task->scsi_task.xfer_pos, sub_task->scsi_task.xfer_len, sub_task->scsi_task.pos, sub_task->scsi_task.len );
+ return;
}
- } else {
- iscsi_list_push( &primary_task->sub_tasks, &task->node );
+ }
- iscsi_task *sub_task;
- iscsi_list_foreach_node ( &primary_task->sub_tasks, sub_task ) {
- logadd( LOG_ERROR, "DEBUG iscsi_task_xfer_complete_process_read: pos NE xfer_pos subtask->xfer_pos = %d, subtask->xfer_pos = %d, subtask->pos = %d, subtask->len = %d, scsi->xfer_pos = %d, scsi->xfer_len = %d, scsi->pos = %d, scsi->len = %d", sub_task->des_data_xfer_pos, sub_task->des_data_xfer_len, sub_task->pos, sub_task->len, sub_task->scsi_task.xfer_pos, sub_task->scsi_task.xfer_len, sub_task->scsi_task.pos, sub_task->scsi_task.len );
- }
+ 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 );
- }
+ iscsi_task_xfer_complete_process_read_sub_tasks( conn, primary_task );
}
}
@@ -2935,7 +2231,7 @@ static void iscsi_connection_pdu_scsi_data_in_complete(uint8_t *user_data)
*/
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 );
+ 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" );
@@ -2943,38 +2239,14 @@ static uint32_t iscsi_scsi_data_in_send(iscsi_connection *conn, iscsi_task *task
return data_sn;
}
- iscsi_scsi_data_in_response_packet *scsi_data_in_pkt = (iscsi_scsi_data_in_response_packet *) iscsi_append_ds_packet( response_pdu->bhs_pkt, conn->header_digest, len, conn->data_digest );
-
- if ( scsi_data_in_pkt == NULL ) {
- logadd( LOG_ERROR, "iscsi_scsi_data_in_send: Out of memory while allocating iSCSI SCSI Data In packet data" );
-
- iscsi_connection_pdu_destroy( response_pdu );
-
- return data_sn;
- }
-
- response_pdu->bhs_pkt = (iscsi_bhs_packet *) scsi_data_in_pkt;
-
- if ( conn->header_digest != 0 ) {
- response_pdu->header_digest = (iscsi_header_digest *) (((iscsi_bhs_packet *) scsi_data_in_pkt) + 1);
- response_pdu->header_digest_size = conn->header_digest;
- }
-
- response_pdu->ds_cmd_data = (iscsi_scsi_ds_cmd_data *) (((uint8_t *) scsi_data_in_pkt) + sizeof(struct iscsi_bhs_packet) + conn->header_digest);
- response_pdu->ds_len = len;
-
- if ( conn->data_digest != 0 ) {
- response_pdu->data_digest = (iscsi_data_digest *) (((uint8_t *) response_pdu->ds_cmd_data) + iscsi_align(len, ISCSI_ALIGN_SIZE));
- response_pdu->data_digest_size = conn->data_digest;
- }
-
- memcpy( response_pdu->ds_cmd_data, (task->scsi_task.buf + pos), len );
-
response_pdu->task = task;
task->scsi_task.ref++;
- 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));
+ 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;
@@ -2986,13 +2258,20 @@ static uint32_t iscsi_scsi_data_in_send(iscsi_connection *conn, iscsi_task *task
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_be24( (uint8_t *) &scsi_data_in_pkt->ds_len, len );
+ 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 );
@@ -3002,9 +2281,11 @@ static uint32_t iscsi_scsi_data_in_send(iscsi_connection *conn, iscsi_task *task
if ( conn->session->err_recovery_level > 0UL )
primary_task->data_sn = data_sn;
- const uint32_t offset = (pos + task->scsi_task.pos);
+ 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);
@@ -3117,7 +2398,8 @@ void iscsi_task_response(iscsi_connection *conn, iscsi_task *task)
return;
}
- iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn );
+ 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" );
@@ -3125,69 +2407,25 @@ void iscsi_task_response(iscsi_connection *conn, iscsi_task *task)
return;
}
- iscsi_scsi_response_packet *scsi_response_pkt;
- uint32_t ds_len;
+ iscsi_scsi_response_packet *scsi_response_pkt = (iscsi_scsi_response_packet *) response_pdu->bhs_pkt;
if ( task->scsi_task.sense_data_len != 0U ) {
- ds_len = (task->scsi_task.sense_data_len + offsetof(struct iscsi_scsi_ds_cmd_data, sense_data));
- scsi_response_pkt = (iscsi_scsi_response_packet *) iscsi_append_ds_packet( response_pdu->bhs_pkt, conn->header_digest, ds_len, conn->data_digest );
-
- if ( scsi_response_pkt == NULL ) {
- logadd( LOG_ERROR, "iscsi_task_response: Out of memory while allocating iSCSI SCSI response packet data" );
-
- iscsi_connection_pdu_destroy( response_pdu );
-
- return;
- }
-
- response_pdu->bhs_pkt = (iscsi_bhs_packet *) scsi_response_pkt;
-
- if ( conn->header_digest != 0 ) {
- response_pdu->header_digest = (iscsi_header_digest *) (((iscsi_bhs_packet *) scsi_response_pkt) + 1);
- response_pdu->header_digest_size = conn->header_digest;
- }
-
- response_pdu->ds_cmd_data = (iscsi_scsi_ds_cmd_data *) (((uint8_t *) scsi_response_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_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_be24( (uint8_t *) &scsi_response_pkt->ds_len, ds_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 {
- ds_len = 0uL;
-
- if ( conn->header_digest != 0 ) {
- scsi_response_pkt = (iscsi_scsi_response_packet *) iscsi_append_header_digest_packet( response_pdu->bhs_pkt, conn->header_digest );
-
- if ( scsi_response_pkt == NULL ) {
- logadd( LOG_ERROR, "iscsi_task_response: Out of memory while allocating iSCSI SCSI response packet data" );
-
- iscsi_connection_pdu_destroy( response_pdu );
-
- return;
- }
-
- response_pdu->bhs_pkt = (iscsi_bhs_packet *) scsi_response_pkt;
- response_pdu->header_digest = (iscsi_header_digest *) (((iscsi_bhs_packet *) scsi_response_pkt) + 1);
- response_pdu->header_digest_size = conn->header_digest;
- } else {
- scsi_response_pkt = (iscsi_scsi_response_packet *) response_pdu->bhs_pkt;
- }
+ *(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->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;
@@ -3202,11 +2440,17 @@ void iscsi_task_response(iscsi_connection *conn, iscsi_task *task)
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->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 )
@@ -3214,6 +2458,8 @@ void iscsi_task_response(iscsi_connection *conn, iscsi_task *task)
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 );
}
@@ -3308,9 +2554,9 @@ void iscsi_portal_group_destroy(iscsi_portal_group *portal_group)
* This function allocates host:port of iSCSI portal for use
* as key and sets the portal group in the portal.
*
- * @param[in] iSCSI portal group to add portal to. May NOT be NULL,
+ * @param[in] portal_group iSCSI portal group to add portal to. May NOT be NULL,
* so take caution.
- * @param[in] iSCSI portal to add to portal group. NULL is NOT
+ * @param[in] portal iSCSI portal to add to portal group. NULL is NOT
* allowed here, so be careful.
* @retval -1 An error occured during adding the portal,
* usually caused by memory exhaustion
@@ -3321,8 +2567,11 @@ int iscsi_portal_group_add_portal(iscsi_portal_group *portal_group, iscsi_portal
{
uint8_t *tmp_buf = iscsi_sprintf_alloc( "%s:%s", portal->host, portal->port );
- if ( tmp_buf == NULL )
+ 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 );
@@ -3351,6 +2600,50 @@ int iscsi_portal_group_add_portal(iscsi_portal_group *portal_group, iscsi_portal
}
/**
+ * @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;
+ }
+
+ int rc = iscsi_hashmap_get( portal_group->portals, key, key_len, (uint8_t **) &portal );
+
+ 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
@@ -3950,8 +3243,9 @@ void iscsi_scsi_lun_task_run(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task)
* @brief Handles iSCSI SCSI task completition.
*
* This function removes the completed task from
- * the iSCSI SCSI LUN task hash map and calls
- * the transfer finished callback function.
+ * 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.
@@ -3968,7 +3262,7 @@ void iscsi_scsi_lun_task_complete(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_tas
}
/**
- * @brief Appends iSCSI SCSI task to pending tasks hash map and / or runs it directly.
+ * @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
@@ -5545,7 +4839,7 @@ static int iscsi_scsi_emu_check_len(iscsi_scsi_task *scsi_task, const uint len,
* NAA for. NULL is NOT allowed here, so
* take caution.
*/
-static void iscsi_scsi_emu_naa_ieee_ext_set(uint64_t *buf, const uint8_t *name)
+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 );
@@ -5614,7 +4908,7 @@ int iscsi_scsi_emu_primary_inquiry_callback(uint8_t *key, const size_t key_size,
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));
+ 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;
@@ -5745,8 +5039,8 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task
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 += (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
@@ -6037,7 +5331,7 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task
char image_rev[sizeof(std_inquiry_data_pkt->product_rev_level) + 1];
- sprintf( image_rev, "%04X", image->rid );
+ 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));
@@ -7196,7 +6490,7 @@ int iscsi_port_transport_id_set(iscsi_port *port, const uint8_t *name, const uin
}
const uint name_len = (uint) (strlen( (char *) tmp_buf ) + 1U);
- const uint len = iscsi_align(name_len, ISCSI_ALIGN_SIZE);
+ 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" );
@@ -7593,7 +6887,7 @@ int iscsi_target_node_create_callback(uint8_t *key, const size_t key_size, uint8
{
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.4x", target->device->name, portal_group->tag );
+ 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;
@@ -7763,7 +7057,7 @@ void iscsi_target_node_destroy(iscsi_target_node *target)
* @return The new position of the written data or a
* negative error code otherwise.
*/
-int 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)
+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.
@@ -7882,12 +7176,23 @@ dnbd3_image_t *iscsi_target_node_image_get(uint8_t *iqn)
}
const uint16_t rev = (uint16_t) ((len > 0U) ? atoi( (char *) image_rev ) : 0);
- dnbd3_image_t *image = image_getOrLoad( (char *) tmp, rev );
+ dnbd3_image_t *image = image_getOrLoad( (char *) image_name, rev );
if ( image == NULL ) {
- const uint64_t wwn = iscsi_target_node_wwn_get( tmp );
+ image = image_getOrLoad( (char *) tmp, rev );
+
+ if ( image == NULL ) {
+ if ( strncasecmp( (char *) image_name, ISCSI_TARGET_NODE_WWN_NAME_PREFIX, ISCSI_STRLEN(ISCSI_TARGET_NODE_WWN_NAME_PREFIX) ) == 0 ) {
+ uint64_t wwn = strtoull( (char *) (image_name + ISCSI_STRLEN(ISCSI_TARGET_NODE_WWN_NAME_PREFIX)), NULL, 16 );
+
+ image = image_getByWwn( wwn, rev, true );
- image = image_getByWwn( wwn, rev, true );
+ if ( image == NULL ) {
+ wwn = strtoull( (char *) (tmp + ISCSI_STRLEN(ISCSI_TARGET_NODE_WWN_NAME_PREFIX)), NULL, 16 );
+ image = image_getByWwn( wwn, rev, true );
+ }
+ }
+ }
}
if ( len > 0U )
@@ -7923,7 +7228,7 @@ iscsi_target_node *iscsi_target_node_find(uint8_t *target_name)
if ( image == NULL )
return NULL;
- target_find.target = iscsi_target_node_create( target_name, NULL, 0, 8U, 1U, 0, 0L, 0, 0 );
+ target_find.target = iscsi_target_node_create( target_name, NULL, 0, 8U, 16U, 0, 0L, 0, 0 );
if ( target_find.target == NULL ) {
logadd( LOG_ERROR, "iscsi_target_node_find: Out of memory while allocating iSCSI target node" );
@@ -8265,21 +7570,8 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock)
conn->login_response_pdu = NULL;
- conn->pdu_snack = iscsi_hashmap_create( 0U );
-
- if ( conn->pdu_snack == NULL ) {
- logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI SNACK PDU hash map" );
-
- iscsi_hashmap_iterate( conn->text_key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
- 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;
- }
-
+ 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 );
@@ -8313,6 +7605,31 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock)
conn->stat_sn = 0UL;
conn->exp_stat_sn = 0UL;
+ 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_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_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;
}
@@ -8341,6 +7658,45 @@ int iscsi_connection_destroy_callback(uint8_t *key, const size_t key_size, uint8
}
/**
+ * @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 Deallocates all resources acquired by iscsi_connection_create.
*
* Deallocates a data structure of an iSCSI connection
@@ -8356,6 +7712,14 @@ 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_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_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( conn->stat_iscsi_opcodes );
+ conn->stat_iscsi_opcodes = NULL;
+
iscsi_task *task;
iscsi_task *tmp;
@@ -8369,11 +7733,17 @@ void iscsi_connection_destroy(iscsi_connection *conn)
iscsi_task_destroy( task );
}
- if ( conn->pdu_snack != NULL ) {
- iscsi_hashmap_iterate( conn->pdu_snack, iscsi_hashmap_key_destroy_callback, NULL );
- iscsi_hashmap_destroy( conn->pdu_snack );
+ iscsi_pdu *pdu;
+ iscsi_pdu *tmp_pdu;
- conn->pdu_snack = NULL;
+ 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 ) {
@@ -8616,14 +7986,14 @@ static int32_t iscsi_append_special_key_value_pair_packet(iscsi_connection *conn
return pos;
if ( (key_value_pair->flags & ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_OVERRIDE_DEFAULT) != 0 ) {
- if ( (int32_t) (len - pos) < 1L )
+ if ( pos >= len )
return -1L;
- pos += (uint32_t) (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 ( (int32_t) (len - pos) < 1L )
+ if ( pos >= len )
return -1L;
uint8_t *first_burst_len_val = NULL;
@@ -8638,15 +8008,14 @@ static int32_t iscsi_append_special_key_value_pair_packet(iscsi_connection *conn
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 += (uint32_t) (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;
-
}
/**
@@ -8675,7 +8044,7 @@ static int32_t iscsi_append_special_key_value_pair_packet(iscsi_connection *conn
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 ( (int32_t) (len - pos) < 1L )
+ if ( pos >= len )
return -1L;
pos += (uint32_t) (snprintf( (char *) (buf + pos), (len - pos), "%s=%s", key, value ) + 1);
@@ -8778,7 +8147,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;
}
@@ -8984,7 +8353,7 @@ int iscsi_negotiate_key_value_pair_callback(uint8_t *key, const size_t key_size,
max_burst_len = (rc < 0) ? ISCSI_SESSION_DEFAULT_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 )
@@ -9163,7 +8532,7 @@ 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.
@@ -9250,22 +8619,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.
* @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 ) {
@@ -9279,10 +8656,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;
}
/**
@@ -9341,27 +8722,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_OPCODE_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_scsi_ds_cmd_data *) (((uint8_t *) login_response_pkt) + sizeof(struct iscsi_bhs_packet) + pdu->header_digest_size);
- login_response_pdu->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);
@@ -9372,10 +8741,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;
@@ -9393,11 +8768,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;
}
@@ -9630,7 +9005,7 @@ static int iscsi_connection_login_check_target(iscsi_connection *conn, iscsi_pdu
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
}
- login_response_pdu->ds_len = ds_len;
+ 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;
@@ -9755,16 +9130,29 @@ static int iscsi_connection_login_check_session(iscsi_connection *conn, iscsi_pd
* @param[in] pdu Pointer to iSCSI login request PDU, may NOT
* be NULL, so be careful.
*/
-void iscsi_login_response_reject_init(iscsi_pdu *login_response_pdu, const iscsi_pdu *pdu)
+void iscsi_connection_login_response_reject(iscsi_pdu *login_response_pdu, const iscsi_pdu *pdu)
{
iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt;
- login_response_pkt->opcode = ISCSI_OPCODE_SERVER_LOGIN_RES;
- login_response_pkt->version_max = ISCSI_VERSION_MAX;
- login_response_pkt->version_active = ISCSI_VERSION_MAX;
- login_response_pkt->init_task_tag = ((iscsi_login_req_packet *) pdu->bhs_pkt)->init_task_tag;
- login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR;
- login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_INVALID_LOGIN_REQ_TYPE;
+ login_response_pkt->opcode = ISCSI_OPCODE_SERVER_LOGIN_RES;
+ login_response_pkt->flags = 0;
+ login_response_pkt->version_max = ISCSI_VERSION_MAX;
+ login_response_pkt->version_active = ISCSI_VERSION_MAX;
+ *(uint32_t *) &login_response_pkt->total_ahs_len = 0UL; // TotalAHSLength and DataSegmentLength are always 0, so write in one step.
+ login_response_pkt->isid.a = 0U;
+ login_response_pkt->isid.b = 0U;
+ login_response_pkt->isid.c = 0U;
+ login_response_pkt->isid.d = 0U;
+ login_response_pkt->tsih = 0U;
+ login_response_pkt->init_task_tag = ((iscsi_login_req_packet *) pdu->bhs_pkt)->init_task_tag;
+ login_response_pkt->reserved = 0UL;
+ login_response_pkt->stat_sn = 0UL;
+ login_response_pkt->exp_cmd_sn = 0UL;
+ login_response_pkt->max_cmd_sn = 0UL;
+ login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR;
+ login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_INVALID_LOGIN_REQ_TYPE;
+ login_response_pkt->reserved2 = 0U;
+ login_response_pkt->reserved3 = 0ULL;
}
/**
@@ -9775,12 +9163,24 @@ void iscsi_login_response_reject_init(iscsi_pdu *login_response_pdu, const iscsi
* filling the data until everything has been read.
*
* @param[in] conn Pointer to connection to link the PDU with.
- * If this is NULL the connection has to be linked later.
+ * If this is NULL the connection has to be
+ * linked later.
+ * @param[in] ahs_len Length of AHS packet data to be appended.
+ * @param[in] header_digest_size Length of header digest. Currently,
+ * only 0, in which case the header digest will
+ * be removed, or 4 for CRC32C are allowed.
+ * @param[in] ds_len Length of DataSegment packet data to be appended.
+ * May not exceed 16MiB - 1 (16777215 bytes).
+ * @param[in] data_digest_size Length of optional data digest (0 or
+ * 4 for now) to add.
* @return Pointer to allocated and zero filled PDU or NULL
* in case of an error (usually memory exhaustion).
*/
-iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn)
+iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint ahs_len, const int header_digest_size, const uint32_t ds_len, const int data_digest_size)
{
+ if ( (ahs_len > ISCSI_MAX_AHS_SIZE) || ((header_digest_size != 0) && (header_digest_size != ISCSI_DIGEST_SIZE)) || ((data_digest_size != 0) && data_digest_size != ISCSI_DIGEST_SIZE) || (ds_len > ISCSI_MAX_DS_SIZE) )
+ return NULL;
+
iscsi_pdu *pdu = (iscsi_pdu *) malloc( sizeof(struct iscsi_pdu) );
if ( pdu == NULL ) {
@@ -9789,18 +9189,25 @@ iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn)
return NULL;
}
- pdu->bhs_pkt = iscsi_create_packet();
+ 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" );
- 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->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;
@@ -9809,17 +9216,20 @@ iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn)
pdu->ref = 1UL;
pdu->bhs_pos = 0U;
pdu->ahs_pos = 0U;
- pdu->ahs_len = 0U;
+ pdu->ahs_len = ahs_len;
pdu->header_digest_pos = 0U;
- pdu->header_digest_size = 0;
- pdu->ds_len = 0UL;
+ pdu->header_digest_size = header_digest_size;
+ pdu->ds_len = pkt_ds_len;
pdu->pos = 0UL;
- pdu->len = 0UL;
+ pdu->len = pkt_ds_len;
pdu->data_digest_pos = 0U;
- pdu->data_digest_size = 0;
+ 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;
}
@@ -9839,7 +9249,11 @@ void iscsi_connection_pdu_destroy(iscsi_pdu *pdu)
if ( pdu->bhs_pkt != NULL ) {
free( pdu->bhs_pkt );
- pdu->bhs_pkt = NULL;
+ pdu->bhs_pkt = NULL;
+ pdu->ahs_pkt = NULL;
+ pdu->header_digest = NULL;
+ pdu->ds_cmd_data = NULL;
+ pdu->data_digest = NULL;
}
free( pdu );
@@ -9847,6 +9261,69 @@ void iscsi_connection_pdu_destroy(iscsi_pdu *pdu)
}
/**
+ * @brief Appends packet data to an iSCSI PDU structure used by connections.
+ *
+ * This function adjusts the pointers if
+ * the packet data size needs to be
+ * extended.
+ *
+ * @param[in] pdu Pointer to iSCSI PDU where to append
+ * the packet data to. May NOT be NULL, so
+ * be careful.
+ * @param[in] ahs_len Length of AHS packet data to be appended.
+ * @param[in] header_digest_size Length of header digest. Currently,
+ * only 0, in which case the header digest will
+ * be removed, or 4 for CRC32C are allowed.
+ * @param[in] ds_len Length of DataSegment packet data to be appended.
+ * May not exceed 16MiB - 1 (16777215 bytes).
+ * @param[in] data_digest_size Length of optional data digest (0 or
+ * 4 for now) to add.
+ * @return Pointer to allocated and zero filled PDU or NULL
+ * in case of an error (usually memory exhaustion).
+ */
+iscsi_bhs_packet *iscsi_connection_pdu_append(iscsi_pdu *pdu, const uint ahs_len, const int header_digest_size, const uint32_t ds_len, const int data_digest_size)
+{
+ if ( (ahs_len > ISCSI_MAX_AHS_SIZE) || ((header_digest_size != 0) && (header_digest_size != ISCSI_DIGEST_SIZE)) || ((data_digest_size != 0) && data_digest_size != ISCSI_DIGEST_SIZE) || (ds_len > ISCSI_MAX_DS_SIZE) )
+ return NULL;
+
+ if ( (ahs_len != pdu->ahs_len) || (header_digest_size != pdu->header_digest_size) || (ds_len != pdu->ds_len) || (data_digest_size != pdu->data_digest_size) ) {
+ iscsi_bhs_packet *bhs_pkt;
+ const uint32_t pkt_ds_len = ISCSI_ALIGN(ds_len, ISCSI_ALIGN_SIZE);
+ const uint32_t old_len = (uint32_t) (sizeof(struct iscsi_bhs_packet) + (uint32_t) pdu->ahs_len + pdu->header_digest_size + pdu->ds_len + ((pdu->ds_len != 0UL) ? (uint32_t) pdu->data_digest_size : 0UL));
+ const uint32_t new_len = (uint32_t) (sizeof(struct iscsi_bhs_packet) + (uint32_t) ahs_len + header_digest_size + pkt_ds_len + ((pkt_ds_len != 0UL) ? (uint32_t) data_digest_size : 0UL));
+
+ if ( new_len > old_len ) {
+ bhs_pkt = realloc( pdu->bhs_pkt, new_len );
+
+ if ( bhs_pkt == NULL ) {
+ logadd( LOG_ERROR, "iscsi_connection_pdu_append: Out of memory while reallocating iSCSI PDU packet data" );
+
+ return NULL;
+ }
+
+ pdu->bhs_pkt = bhs_pkt;
+ } else {
+ bhs_pkt = pdu->bhs_pkt;
+ }
+
+ pdu->ahs_pkt = (ahs_len != 0U) ? (iscsi_ahs_packet *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet) ) : NULL;
+ pdu->header_digest = (header_digest_size != 0) ? (iscsi_header_digest *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet) + ahs_len) : NULL;
+ pdu->ds_cmd_data = (pkt_ds_len != 0UL) ? (iscsi_scsi_ds_cmd_data *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet) + ahs_len + header_digest_size) : NULL;
+ pdu->data_digest = ((pkt_ds_len != 0UL) && (data_digest_size != 0)) ? (iscsi_data_digest *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet) + ahs_len + header_digest_size + pkt_ds_len) : NULL;
+ pdu->ahs_len = ahs_len;
+ pdu->header_digest_size = header_digest_size;
+ pdu->ds_len = pkt_ds_len;
+ pdu->len = pkt_ds_len;
+ pdu->data_digest_size = data_digest_size;
+
+ if ( pkt_ds_len != 0UL )
+ memset( (((uint8_t *) pdu->ds_cmd_data) + ds_len), 0, (pkt_ds_len - ds_len) );
+ }
+
+ return pdu->bhs_pkt;
+}
+
+/**
* @brief Frees an iSCSI PDU structure used by using connection callback function.
*
* This function frees an iSCSI PDU structure.
@@ -9869,7 +9346,289 @@ void iscsi_connection_pdu_free(iscsi_connection *conn, iscsi_pdu *pdu)
iscsi_connection_pdu_destroy( pdu );
- callback( user_data );
+ 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_get_ahs_packet: Specified index for AHS packet does not exist" );
+
+ return NULL;
+}
+
+/**
+ * @brief Counts number of AHS packets of an iSCSI PDU.
+ *
+ * Gets the total number of AHS packets.
+ *
+ * @param[in] pdu Pointer to iscsi PDU of which the
+ * number of AHS packets should be counted.
+ * May NOT be NULL, so be careful.
+ * @return The number of AHS packets or 0 if no AHS
+ * packet data is available.
+ */
+int iscsi_connection_pdu_ahs_packet_count(const iscsi_pdu *pdu)
+{
+ const iscsi_ahs_packet *ahs_pkt = pdu->ahs_pkt; // First AHS packet
+
+ if ( ahs_pkt == NULL )
+ return 0;
+
+ int count = 0;
+ uint ahs_len = pdu->ahs_len;
+
+ while ( (int) ahs_len > 0 ) {
+ uint len = iscsi_get_be16(ahs_pkt->len) + offsetof(struct iscsi_ahs_packet, data); // Total length of current AHS packet
+
+ len = ISCSI_ALIGN(len, ISCSI_ALIGN_SIZE);
+ ahs_len -= len;
+ ahs_pkt = (iscsi_ahs_packet *) (((uint8_t *) ahs_pkt) + (len - offsetof(struct iscsi_ahs_packet, data))); // Advance pointer to next AHS packet
+ count++;
+ }
+
+ return count;
+}
+
+/// CRC32C lookup table. Created with a polynomial reflect value of 0x82F63B78.
+static const uint32_t crc32c_lut[] = {
+ 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
+ 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
+ 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
+ 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
+ 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
+ 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
+ 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
+ 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
+ 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
+ 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
+ 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
+ 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
+ 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
+ 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
+ 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
+ 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
+ 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
+ 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
+ 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
+ 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
+ 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
+ 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
+ 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
+ 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
+ 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
+ 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
+ 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
+ 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
+ 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
+ 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
+ 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
+ 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351};
+
+/**
+ * @brief Calculates digest (CRC32C).
+ *
+ * Calculates CRC32C with 0x82F63B78 polynomial
+ * reflect according to iSCSI specs.\n
+ * TODO: Implement optimized SSE4.2 and ARM versions
+ *
+ * @param[in] data Pointer to data to calculate CRC32C for.
+ * @param[in] len Length of data to be calculated. Must be
+ * divisable by 4 which is guaranteed by iSCSI standard.
+ * @param[in] crc32c Previous CRC32C in case of multiple passes.
+ * @return CRC32C value. THis function cannot fail.
+ */
+static inline uint32_t iscsi_crc32c_update(const uint8_t *data, const uint len, uint32_t crc32c)
+{
+ for ( uint i = 0; i < len; i += 4 ) {
+ crc32c = (crc32c >> 8UL) ^ crc32c_lut[(crc32c ^ data[i]) & 0xFF];
+ crc32c = (crc32c >> 8UL) ^ crc32c_lut[(crc32c ^ data[i + 1]) & 0xFF];
+ crc32c = (crc32c >> 8UL) ^ crc32c_lut[(crc32c ^ data[i + 2]) & 0xFF];
+ crc32c = (crc32c >> 8UL) ^ crc32c_lut[(crc32c ^ data[i + 3]) & 0xFF];
+ }
+
+ return crc32c;
+}
+
+/**
+ * @brief Calculate and store iSCSI header digest (CRC32C).
+ *
+ * Calculates header digest (CRC32C) with
+ * 0x82F63B78 polynomial reflect according
+ * to iSCSI specs and stores the result in
+ * the iSCSI packet data. This function
+ * cannot fail.
+ *
+ * @param[out] header_digest Pointer to iSCSI header digest
+ * packet data to put CRC32C into.
+ * May NOT be NULL, so be careful.
+ * @param[in] packet_data Pointer to ISCSI BHS packet to
+ * calculate CRC32C for. NULL is NOT
+ * allowed here, take caution.
+ * @param[in] ahs_len AHS segment length in bytes.
+ */
+void iscsi_connection_pdu_digest_header_update(iscsi_header_digest *header_digest, const iscsi_bhs_packet *packet_data, const uint ahs_len)
+{
+ const uint32_t crc32c = iscsi_crc32c_update( (uint8_t *) packet_data, (sizeof(struct iscsi_bhs_packet) + ahs_len), ISCSI_CRC32C_INITIAL ) ^ ISCSI_CRC32C_XOR;
+
+ iscsi_put_le32( (uint8_t *) &header_digest->crc32c, crc32c );
+}
+
+/**
+ * @brief Validates a stored iSCSI header digest (CRC32C) with actual header data.
+ *
+ * Verifies header digest (CRC32C) with
+ * 0x82F63B78 polynomial reflect according
+ * to iSCSI specs. This function cannot
+ * fail.
+ *
+ * @param[in] header_digest Pointer to iSCSI header digest
+ * packet data to compare CRC32C with.
+ * May NOT be NULL, so be careful.
+ * @param[in] packet_data Pointer to ISCSI BHS packet to
+ * validate CRC32C for. May NOT be NULL,
+ * so be careful.
+ * @param[in] ahs_len AHS segment length in bytes.
+ * @retval true CRC32C matches the stored value.
+ * @retval false CRC32C does NOT match the stored value.
+ */
+bool iscsi_connection_pdu_digest_header_verify(const iscsi_header_digest *header_digest, const iscsi_bhs_packet *packet_data, const uint ahs_len)
+{
+ const uint32_t crc32c = iscsi_crc32c_update( (uint8_t *) packet_data, (sizeof(struct iscsi_bhs_packet) + ahs_len), ISCSI_CRC32C_INITIAL ) ^ ISCSI_CRC32C_XOR;
+
+ return (iscsi_get_le32(crc32c) == header_digest->crc32c);
+}
+
+/**
+ * @brief Calculate iSCSI data digest (CRC32C).
+ *
+ * Calculates data digest (CRC32) with
+ * 0x82F63B78 polynomial reflect of a
+ * whole DataSegment (CRC32C) according
+ * to the iSCSI specs.\n
+ * The resulting CRC32C will be stored
+ * in the iSCSI packet.
+ *
+ * @param[out] data_digest Pointer to iSCSI data digest
+ * packet data to put CRC32C into.
+ * May NOT be NULL, so be careful.
+ * @param[in] ds_cmd_data Pointer to iSCSI DataSegment packet to
+ * calculate CRC32C for. NULL is NOT
+ * allowed here, take caution.
+ * @param[in] ds_len Data segment length in bytes.
+ */
+void iscsi_connection_pdu_digest_data_update(iscsi_data_digest *data_digest, const iscsi_scsi_ds_cmd_data *ds_cmd_data, const uint32_t ds_len)
+{
+ const uint32_t crc32c = iscsi_crc32c_update( (uint8_t *) ds_cmd_data, ISCSI_ALIGN(ds_len, ISCSI_DIGEST_SIZE), ISCSI_CRC32C_INITIAL ) ^ ISCSI_CRC32C_XOR;
+
+ iscsi_put_le32( (uint8_t *) &data_digest->crc32c, crc32c );
+}
+
+/**
+ * @brief Validates a stored iSCSI data digest (CRC32C) with actual DataSegment.
+ *
+ * Verifies data digest (CRC32C) with
+ * 0x82F63B78 polynomial reflect according
+ * to iSCSI specs. This function cannot
+ * fail.
+ *
+ * @param[out] data_digest Pointer to iSCSI data digest
+ * packet data to compare CRC32C with.
+ * May NOT be NULL, so be careful.
+ * @param[in] ds_cmd_data Pointer to iSCSI DataSegment
+ * packet to calculate CRC32C for. May NOT
+ * be NULL, so be careful.
+ * @param[in] ds_len Data segment length in bytes.
+ * @retval true CRC32C matches the stored value.
+ * @retval false CRC32C does NOT match the stored value.
+ */
+bool iscsi_connection_pdu_digest_data_verify(const iscsi_data_digest *data_digest, const iscsi_scsi_ds_cmd_data *ds_cmd_data, const uint32_t ds_len)
+{
+ const uint32_t crc32c = iscsi_crc32c_update( (uint8_t *) ds_cmd_data, ISCSI_ALIGN(ds_len, ISCSI_DIGEST_SIZE), ISCSI_CRC32C_INITIAL ) ^ ISCSI_CRC32C_XOR;
+
+ return (iscsi_get_le32(crc32c) == data_digest->crc32c);
+}
+
+/**
+ * @brief Checks whether iSCSI PDU cleanup procedure has to be deferred.
+ *
+ * This function checks whether the cleanup
+ * process of a written PDU has to be
+ * deferred to a later stage.
+ *
+ * @param[in] pdu Pointer to iSCSI PDU to be checked for
+ * deferrred cleanup processs.
+ * @retval true The PDUs cleanup stage has to be
+ * deferred to a later stage.
+ * @retval false The PDU can be cleaned up immediately.
+ */
+static inline bool iscsi_connection_pdu_free_is_deferred(const iscsi_pdu *pdu)
+{
+ return ((pdu != NULL) && ((pdu->bhs_pkt->opcode == ISCSI_OPCODE_SERVER_READY_XFER) || (pdu->bhs_pkt->opcode == ISCSI_OPCODE_SERVER_SCSI_DATA_IN)));
+}
+
+/**
+ * @brief Handles iSCSI PDU cleanup after the PDU has been sent via TCP/IP to the client.
+ *
+ * This function checks whether there are PDU
+ * cleanup actions required and either frees
+ * the PDU or adds it to the PDU Sequence
+ * Number Acknowledgement (SNACK) list.
+ *
+ * @param[in] user_data Pointer to iSCSI PDU which completed
+ * the TCP/IP write. May NOT be NULL, so be
+ * careful.
+ */
+static void iscsi_connection_pdu_write_complete(uint8_t *user_data, int err)
+{
+ iscsi_pdu *pdu = (iscsi_pdu *) user_data;
+ iscsi_connection *conn = pdu->conn;
+
+ if ( conn->state >= ISCSI_CONNECT_STATE_EXITING )
+ return;
+
+ iscsi_list_remove( &pdu->node );
+
+ if ( err != 0 )
+ conn->state = ISCSI_CONNECT_STATE_EXITING;
+
+ if ( ((conn->flags & ISCSI_CONNECT_FLAGS_FULL_FEATURE) != 0) && (conn->session->err_recovery_level > 0UL) && iscsi_connection_pdu_free_is_deferred( pdu ) )
+ iscsi_list_enqueue( &conn->pdus_snack, &pdu->node );
+ else
+ iscsi_connection_pdu_free( conn, pdu );
}
/**
@@ -9893,27 +9652,40 @@ void iscsi_connection_pdu_free(iscsi_connection *conn, 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 ( conn->state >= ISCSI_CONNECT_STATE_EXITING )
- return;
-
if ( ISCSI_GET_OPCODE(pdu->bhs_pkt->opcode) != ISCSI_OPCODE_CLIENT_LOGIN_REQ ) {
- if ( conn->header_digest != 0 )
- iscsi_calc_header_digest( pdu->bhs_pkt );
+ if ( pdu->header_digest != NULL )
+ iscsi_connection_pdu_digest_header_update( pdu->header_digest, pdu->bhs_pkt, pdu->ahs_len );
- if ( (conn->data_digest != 0) && (pdu->ds_len != 0U) )
- iscsi_calc_data_digest( pdu->bhs_pkt, conn->header_digest );
+ if ( pdu->data_digest != NULL )
+ iscsi_connection_pdu_digest_data_update( pdu->data_digest, pdu->ds_cmd_data, pdu->ds_len );
}
- 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);
+ 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;
- // TODO: Do the writing in a queue.
- iscsi_connection_write( conn, (uint8_t *) pdu->bhs_pkt, len );
+ 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: Begin remove after I/O async implementation
+ int32_t rc = iscsi_connection_write( conn, (uint8_t *) pdu->bhs_pkt, len );
+
+ uint64_t *exec_data = malloc( 64 );
+
+ exec_data[2] = (uint64_t *) iscsi_connection_pdu_write_complete;
+ exec_data[3] = 2ULL;
+ exec_data[4] = pdu;
+ exec_data[5] = (rc == (int32_t) len) ? 0 : -1;
+
+ iscsi_list_enqueue( &iscsi_globvec->exec_queue, (iscsi_node *) exec_data );
+
if ( callback == NULL )
return;
- uint64_t *exec_data = malloc( 64 );
+ exec_data = malloc( 64 );
exec_data[2] = (uint64_t *) callback;
exec_data[3] = 1ULL;
@@ -9922,7 +9694,9 @@ void iscsi_connection_pdu_write(iscsi_connection *conn, iscsi_pdu *pdu, iscsi_co
iscsi_list_enqueue( &iscsi_globvec->exec_queue, (iscsi_node *) exec_data );
// TODO: End remove after I/O async implementation
-// if ( callback != NULL )
+// iscsi_connection_pdu_write_complete( (uint8_t *) pdu, (rc == (int32_t) len) ? 0 : -1 );
+//
+// if ( user_data != NULL )
// callback( user_data );
}
@@ -9965,37 +9739,7 @@ static inline int iscsi_seq_num_cmp_gt(const uint32_t seq_num, const uint32_t se
}
/**
- * @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_connection_pdu_ack_remove_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
-{
- iscsi_connection *conn = (iscsi_connection *) user_data;
- iscsi_pdu *pdu = (iscsi_pdu *) value;
- 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_hashmap_remove_free( conn->pdu_snack, key, key_size, iscsi_hashmap_key_destroy_callback, NULL );
- iscsi_connection_pdu_free( conn, pdu );
- }
-
- return 0;
-}
-
-/**
- * @brief Removes an acknowledged PDU from SNACK PDU hash map by ExpStatSN.
+ * @brief Removes an acknowledged PDU from SNACK PDU doubly linked list by ExpStatSN.
*
* This function is invoked when ExpStatSN becomes
* invalid.
@@ -10008,7 +9752,18 @@ void iscsi_connection_pdu_ack_remove(iscsi_connection *conn, const uint32_t exp_
{
conn->exp_stat_sn = (exp_stat_sn < conn->stat_sn) ? exp_stat_sn : conn->stat_sn;
- iscsi_hashmap_iterate( conn->pdu_snack, iscsi_connection_pdu_ack_remove_callback, (uint8_t *) conn );
+ 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 );
+ }
+ }
}
/**
@@ -10030,7 +9785,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" );
@@ -10038,48 +9794,29 @@ static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
}
- const uint32_t ds_len = (uint32_t) sizeof(struct iscsi_bhs_packet) + ((uint32_t) pdu->bhs_pkt->total_ahs_len << 2UL) + conn->header_digest;
- iscsi_reject_packet *reject_pkt = (iscsi_reject_packet *) iscsi_append_ds_packet( response_pdu->bhs_pkt, conn->header_digest, ds_len, conn->data_digest );
-
- if ( reject_pkt == NULL ) {
- logadd( LOG_ERROR, "iscsi_connection_handle_reject: Out of memory while allocating iSCSI reject packet data" );
+ iscsi_reject_packet *reject_pkt = (iscsi_reject_packet *) response_pdu->bhs_pkt;
- iscsi_connection_pdu_destroy( response_pdu );
-
- return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
- }
-
- response_pdu->bhs_pkt = (iscsi_bhs_packet *) reject_pkt;
-
- if ( conn->header_digest != 0 ) {
- response_pdu->header_digest = (iscsi_header_digest *) (((iscsi_bhs_packet *) reject_pkt) + 1);
- response_pdu->header_digest_size = conn->header_digest;
- }
-
- response_pdu->ds_cmd_data = (iscsi_scsi_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_OPCODE_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; // Minus one does not require endianess conversion
+ 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 );
@@ -10128,7 +9865,7 @@ static int iscsi_connection_update_cmd_sn(iscsi_connection *conn, iscsi_pdu *pdu
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_NOP_OUT) )
+ 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;
@@ -10161,12 +9898,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 );
@@ -10232,6 +9969,37 @@ static int iscsi_connection_pdu_header_handle_nop_out(iscsi_connection *conn, is
*/
static int iscsi_connection_pdu_header_handle_scsi_cmd(iscsi_connection *conn, iscsi_pdu *pdu)
{
+ 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 ( stat_rc < 0 ) {
+ free( 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;
@@ -10443,7 +10211,7 @@ iscsi_pdu *iscsi_r2t_find_pdu_bhs(iscsi_connection *conn, iscsi_pdu *pdu)
*/
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 );
+ 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" );
@@ -10453,24 +10221,10 @@ int iscsi_r2t_send(iscsi_connection *conn, iscsi_task *task, uint32_t *r2t_sn, c
iscsi_r2t_packet *r2t_pkt = (iscsi_r2t_packet *) response_pdu->bhs_pkt;
- if ( conn->header_digest != 0 ) {
- r2t_pkt = (iscsi_r2t_packet *) iscsi_append_header_digest_packet( response_pdu->bhs_pkt, conn->header_digest );
-
- if ( r2t_pkt == NULL ) {
- logadd( LOG_ERROR, "iscsi_r2t_send: Out of memory while allocating iSCSI Ready To Transfer packet data" );
-
- iscsi_connection_pdu_destroy( response_pdu );
-
- return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
- }
-
- response_pdu->bhs_pkt = (iscsi_bhs_packet *) r2t_pkt;
- response_pdu->header_digest = (iscsi_header_digest *) (((iscsi_bhs_packet *) r2t_pkt) + 1);
- response_pdu->header_digest_size = conn->header_digest;
- }
-
- r2t_pkt->opcode = ISCSI_OPCODE_SERVER_READY_XFER;
- r2t_pkt->flags = -0x80;
+ 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 );
@@ -10480,6 +10234,7 @@ int iscsi_r2t_send(iscsi_connection *conn, iscsi_task *task, uint32_t *r2t_sn, c
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;
@@ -10496,53 +10251,15 @@ int iscsi_r2t_send(iscsi_connection *conn, iscsi_task *task, uint32_t *r2t_sn, c
}
/**
- * @brief Finds and removes an iSCSI PDU by Ready To Transfer Sequence Number (R2TSN).
- *
- * Callback function for each element while iterating
- * through the iSCSI SNACK PDU's.
- *
- * @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 PDU, the iSCSI task,
- * the iSCSI SNACK PDU's hash map and the
- * Ready To Transfer Sequence Number (R2TSN)
- * to be searched for and may NOT be NULL, so
- * be careful.
- * @retval -1 The PDU has been found and stored
- * in the result structure, as well as been
- * removed from the SNACK PDU's hash map.
- * Therefore, no further searching is needed.
- * @retval 0 The PDU has not been found yet.
- */
-int iscsi_r2t_remove_pdu_from_snack_list_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
-{
- iscsi_r2t_remove_pdu *pdu_remove = (iscsi_r2t_remove_pdu *) user_data;
- iscsi_pdu *pdu = (iscsi_pdu *) value;
- iscsi_r2t_packet *r2t_pkt = (iscsi_r2t_packet *) pdu->bhs_pkt;
-
- if ( (pdu->task != pdu_remove->task) || (iscsi_get_be32(r2t_pkt->r2t_sn) != pdu_remove->r2t_sn) )
- return 0;
-
- pdu_remove->pdu = pdu;
- iscsi_hashmap_remove_free( pdu_remove->pdu_snack, key, key_size, iscsi_hashmap_key_destroy_callback, NULL );
-
- return -1;
-}
-
-/**
- * @brief Searches an iSCSI PDU task by Ready To Transfer Sequence Number (R2TSN) and removes it from PDU SNACK hash map.
+ * @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 S(NACK)
+ * 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 list.
+ * PDU SNACK doubly linked list.
*
* @param[in] conn Pointer to iSCSI connection to
* search in the Sequence Number
@@ -10560,11 +10277,21 @@ int iscsi_r2t_remove_pdu_from_snack_list_callback(uint8_t *key, const size_t key
*/
static iscsi_pdu *iscsi_r2t_remove_pdu_from_snack_list(iscsi_connection *conn, iscsi_task *task, const uint32_t r2t_sn)
{
- iscsi_r2t_remove_pdu pdu_remove = {NULL, task, conn->pdu_snack, 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;
- iscsi_hashmap_iterate( conn->pdu_snack, iscsi_r2t_remove_pdu_from_snack_list_callback, (uint8_t *) &pdu_remove );
+ if ( (pdu->task == task) && (iscsi_get_be32(r2t_pkt->r2t_sn) == r2t_sn) ) {
+ iscsi_list_remove( &pdu->node );
- return pdu_remove.pdu;
+ return pdu;
+ }
+ }
+ }
+
+ return NULL;
}
/**
@@ -10743,7 +10470,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 );
+ 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" );
@@ -10753,22 +10480,6 @@ static int iscsi_connection_pdu_header_handle_logout_req(iscsi_connection *conn,
iscsi_logout_response_packet *logout_response_pkt = (iscsi_logout_response_packet *) response_pdu->bhs_pkt;
- if ( conn->header_digest != 0 ) {
- logout_response_pkt = (iscsi_logout_response_packet *) iscsi_append_header_digest_packet( response_pdu->bhs_pkt, conn->header_digest );
-
- if ( logout_response_pkt == NULL ) {
- logadd( LOG_ERROR, "iscsi_connection_pdu_header_handle_logout_req: Out of memory while allocating iSCSI logout packet data" );
-
- iscsi_connection_pdu_destroy( response_pdu );
-
- return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
- }
-
- response_pdu->bhs_pkt = (iscsi_bhs_packet *) logout_response_pkt;
- response_pdu->header_digest = (iscsi_header_digest *) (((iscsi_bhs_packet *) logout_response_pkt) + 1);
- response_pdu->header_digest_size = conn->header_digest;
- }
-
logout_response_pkt->opcode = ISCSI_OPCODE_SERVER_LOGOUT_RES;
logout_response_pkt->flags = -0x80;
@@ -10782,7 +10493,11 @@ static int iscsi_connection_pdu_header_handle_logout_req(iscsi_connection *conn,
logout_response_pkt->response = ISCSI_LOGOUT_RESPONSE_CID_NOT_FOUND;
}
- logout_response_pkt->init_task_tag = logout_req_pkt->init_task_tag; // Copying over doesn't change endianess.
+ 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 ) {
@@ -10796,8 +10511,10 @@ static int iscsi_connection_pdu_header_handle_logout_req(iscsi_connection *conn,
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 );
@@ -10852,12 +10569,12 @@ 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 );
+ 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;
@@ -10934,7 +10651,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;
@@ -10947,7 +10664,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" );
@@ -10955,34 +10672,12 @@ 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_scsi_ds_cmd_data *) (((uint8_t *) nop_in_pkt) + sizeof(struct iscsi_bhs_packet) + conn->header_digest);
- response_pdu->ds_len = ds_len;
-
- if ( conn->data_digest != 0 ) {
- response_pdu->data_digest = (iscsi_data_digest *) (((uint8_t *) response_pdu->ds_cmd_data) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE));
- response_pdu->data_digest_size = conn->data_digest;
- }
+ iscsi_nop_in_packet *nop_in_pkt = (iscsi_nop_in_packet *) response_pdu->bhs_pkt;
nop_in_pkt->opcode = ISCSI_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; // Minus one does not require endianess conversion
iscsi_put_be32( (uint8_t *) &nop_in_pkt->init_task_tag, init_task_tag );
@@ -10993,6 +10688,11 @@ static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscs
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 );
@@ -11385,7 +11085,7 @@ static int iscsi_connection_login_set_target_info(iscsi_connection *conn, iscsi_
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;
@@ -11417,7 +11117,7 @@ static int iscsi_connection_login_set_target_info(iscsi_connection *conn, iscsi_
if ( ds_len < 0L )
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
- login_response_pdu->ds_len = ds_len;
+ login_response_pdu->len = ds_len;
}
if ( type == ISCSI_SESSION_TYPE_DISCOVERY ) {
@@ -11563,9 +11263,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->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;
@@ -11724,7 +11424,7 @@ 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 int32_t 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->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 < 0L ) {
login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR;
@@ -11863,7 +11563,7 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc
conn->text_key_value_pairs = tmp_hashmap;
}
- iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn );
+ 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" );
@@ -11874,12 +11574,11 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
}
- const uint32_t ds_len = conn->max_recv_ds_len;
- iscsi_text_response_packet *text_response_pkt = (iscsi_text_response_packet *) iscsi_append_ds_packet( response_pdu->bhs_pkt, conn->header_digest, ds_len, conn->data_digest );
+ response_pdu->ds_len = 0UL;
- if ( text_response_pkt == NULL ) {
- logadd( LOG_ERROR, "iscsi_connection_pdu_data_handle_text_req: Out of memory while allocating iSCSI text packet data" );
+ 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 );
@@ -11887,32 +11586,10 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
}
- response_pdu->bhs_pkt = (iscsi_bhs_packet *) text_response_pkt;
-
- if ( conn->header_digest != 0 ) {
- response_pdu->header_digest = (iscsi_header_digest *) (((iscsi_bhs_packet *) text_response_pkt) + 1);
- response_pdu->header_digest_size = conn->header_digest;
- }
-
- response_pdu->ds_cmd_data = (iscsi_scsi_ds_cmd_data *) (((uint8_t *) text_response_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;
- }
-
- response_pdu->pos = iscsi_negotiate_key_value_pairs( conn, key_value_pairs, (uint8_t *) response_pdu->ds_cmd_data, response_pdu->pos, response_pdu->ds_len );
-
- if ( (int32_t) response_pdu->pos < 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;
@@ -11920,6 +11597,8 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc
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 );
@@ -11942,7 +11621,7 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc
if ( send_targets_val[0] == '\0' )
send_targets_val = (uint8_t *) "ALL";
- response_pdu->pos = iscsi_target_node_send( conn, send_targets_val, conn->init_name, (uint8_t *) response_pdu->ds_cmd_data, response_pdu->pos, response_pdu->ds_len );
+ 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;
@@ -11959,9 +11638,9 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
}
- response_pdu->pos = 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, response_pdu->pos, response_pdu->ds_len );
+ 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 {
- response_pdu->pos = iscsi_target_node_send( conn, send_targets_val, conn->init_name, (uint8_t *) response_pdu->ds_cmd_data, response_pdu->pos, response_pdu->ds_len );
+ 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 );
}
}
@@ -11971,7 +11650,7 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc
}
}
- if ( (int32_t) response_pdu->pos < 0L ) {
+ 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 );
@@ -11986,7 +11665,9 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc
conn->text_key_value_pairs = key_value_pairs;
}
- iscsi_put_be24( (uint8_t *) &text_response_pkt->ds_len, (uint32_t) response_pdu->pos );
+ 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.
@@ -12005,6 +11686,8 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc
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 );
@@ -12115,27 +11798,9 @@ static int iscsi_connection_pdu_data_handle(iscsi_connection *conn, iscsi_pdu *p
int iscsi_connection_pdu_data_read(iscsi_connection *conn, iscsi_pdu *pdu)
{
const uint32_t ds_len = pdu->ds_len;
- uint8_t *buf = (uint8_t *) pdu->ds_cmd_data;
-
- if ( buf == NULL ) {
- buf = (uint8_t *) iscsi_append_ds_packet( (iscsi_bhs_packet *) pdu->bhs_pkt, conn->header_digest, ds_len, conn->data_digest );
-
- if ( buf == NULL )
- return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
-
- pdu->bhs_pkt = (iscsi_bhs_packet *) buf;
- pdu->ahs_pkt = (iscsi_ahs_packet *) (((iscsi_bhs_packet *) pdu->bhs_pkt) + 1);
- pdu->header_digest = (iscsi_header_digest *) (((uint8_t *) pdu->bhs_pkt) + sizeof(struct iscsi_bhs_packet) + pdu->ahs_len);
- pdu->ds_cmd_data = (iscsi_scsi_ds_cmd_data *) (((uint8_t *) pdu->bhs_pkt) + sizeof(struct iscsi_bhs_packet) + pdu->ahs_len + conn->header_digest);
-
- if ( conn->data_digest != 0 )
- pdu->data_digest = (iscsi_data_digest *) (((uint8_t *) pdu->bhs_pkt) + sizeof(struct iscsi_bhs_packet) + pdu->ahs_len + conn->header_digest + ds_len);
-
- pdu->data_digest_size = conn->data_digest;
- }
if ( pdu->pos < ds_len ) {
- const int32_t len = iscsi_connection_read( conn, (((uint8_t *) pdu->ds_cmd_data) + pdu->pos), (pdu->ds_len - pdu->pos) );
+ 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;
@@ -12146,20 +11811,20 @@ int iscsi_connection_pdu_data_read(iscsi_connection *conn, iscsi_pdu *pdu)
if ( pdu->pos < ds_len )
return ISCSI_CONNECT_PDU_READ_PROCESSED;
- if ( conn->data_digest != 0 ) {
- if ( pdu->data_digest_pos < (uint) conn->data_digest ) {
- const int32_t len = iscsi_connection_read( conn, (((uint8_t *) pdu->data_digest) + pdu->data_digest_pos), (conn->data_digest - pdu->data_digest_pos) );
+ 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 ( pdu->data_digest_pos < (uint) conn->data_digest )
+ if ( (int) pdu->data_digest_pos < pdu->data_digest_size )
return ISCSI_CONNECT_PDU_READ_OK;
}
- if ( iscsi_validate_data_digest( pdu->bhs_pkt, conn->data_digest ) == 0 )
+ if ( !iscsi_connection_pdu_digest_data_verify( pdu->data_digest, pdu->ds_cmd_data, ds_len ) )
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
}
@@ -12193,7 +11858,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;
@@ -12224,24 +11889,46 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn)
break;
}
- pdu->ds_len = iscsi_get_be24(pdu->bhs_pkt->ds_len);
- pdu->ds_len = iscsi_align(pdu->ds_len, ISCSI_ALIGN_SIZE);
- pdu->pos = 0UL;
- pdu->len = pdu->ds_len;
+ iscsi_bhs_packet *bhs_pkt = pdu->bhs_pkt;
+ const uint ahs_len = ((uint) bhs_pkt->total_ahs_len << 2U);
+ const uint32_t ds_len = iscsi_get_be24(bhs_pkt->ds_len);
- const uint ahs_len = ((uint) pdu->bhs_pkt->total_ahs_len << 2U);
+ bhs_pkt = iscsi_connection_pdu_append( pdu, ahs_len, conn->header_digest, ds_len, conn->data_digest );
- if ( pdu->ahs_pos < ahs_len ) {
- if ( pdu->ahs_pkt == NULL ) {
- pdu->ahs_pkt = (iscsi_ahs_packet *) iscsi_append_ahs_packet( pdu->bhs_pkt, (uint32_t) ahs_len );
+ if ( bhs_pkt == NULL )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ uint64_t stat_opcode = (uint64_t) ISCSI_GET_OPCODE(bhs_pkt->opcode);
+ uint64_t *stat_value = NULL;
+ int stat_rc = iscsi_hashmap_get( conn->stat_iscsi_opcodes, (uint8_t *) &stat_opcode, sizeof(stat_opcode), (uint8_t *) &stat_value );
+
+ if ( stat_value == NULL ) {
+ stat_value = malloc( sizeof(uint64_t) );
+
+ if ( stat_value != NULL ) {
+ uint8_t *stat_key = iscsi_hashmap_key_create( (uint8_t *) &stat_opcode, sizeof(stat_opcode) );
- if ( pdu->ahs_pkt == NULL )
- return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ if ( stat_key != NULL ) {
+ *stat_value = 0ULL;
- pdu->bhs_pkt = (iscsi_bhs_packet *) pdu->ahs_pkt;
- pdu->ahs_pkt = (iscsi_ahs_packet *) (((iscsi_bhs_packet *) pdu->bhs_pkt) + 1);
+ stat_rc = iscsi_hashmap_put( conn->stat_iscsi_opcodes, stat_key, sizeof(stat_opcode), (uint8_t *) stat_value );
+
+ if ( stat_rc < 0 ) {
+ free( 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) );
if ( len < 0L ) {
@@ -12256,20 +11943,9 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn)
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->bhs_pkt = (iscsi_bhs_packet *) pdu->header_digest;
- pdu->ahs_pkt = (iscsi_ahs_packet *) (((iscsi_bhs_packet *) pdu->bhs_pkt) + 1);
- pdu->header_digest = (iscsi_header_digest *) (((uint8_t *) pdu->bhs_pkt) + sizeof(struct iscsi_bhs_packet) + ahs_len);
- }
-
- if ( pdu->header_digest_pos < (uint) conn->header_digest ) {
- const int32_t len = iscsi_connection_read( conn, (((uint8_t *) pdu->header_digest) + pdu->header_digest_pos), (conn->header_digest - pdu->header_digest_pos) );
+ 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;
@@ -12279,11 +11955,11 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn)
pdu->header_digest_pos += len;
- if ( pdu->header_digest_pos < (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;
@@ -12365,8 +12041,11 @@ int iscsi_connection_pdu_handle(iscsi_connection *conn)
for ( i = 0; i < ISCSI_PDU_HANDLE_COUNT; i++ ) {
pthread_rwlock_wrlock( &iscsi_globvec_rwlock );
- if ( iscsi_globvec == NULL )
+ if ( iscsi_globvec == NULL ) {
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
// TODO: Remove after I/O sync implementation
iscsi_list_create( &iscsi_globvec->exec_queue );
@@ -12383,6 +12062,9 @@ int iscsi_connection_pdu_handle(iscsi_connection *conn)
if ( exec_data[3] == 1ULL ) {
iscsi_scsi_task_xfer_complete_callback callback = (iscsi_scsi_task_xfer_complete_callback) exec_data[2];
callback( (uint8_t *) exec_data[4] );
+ } else if ( exec_data[3] == 2ULL ) {
+ iscsi_connection_write_complete_callback callback = (iscsi_connection_write_complete_callback) exec_data[2];
+ callback( (uint8_t *) exec_data[4], (int) exec_data[5] );
} else if ( exec_data[3] == 3ULL ) {
iscsi_scsi_emu_io_complete_callback callback = (iscsi_scsi_emu_io_complete_callback) exec_data[2];
callback( (dnbd3_image_t *) exec_data[4], (uint8_t *) exec_data[5], (bool) exec_data[6] );
@@ -12395,9 +12077,9 @@ int iscsi_connection_pdu_handle(iscsi_connection *conn)
pthread_rwlock_unlock( &iscsi_globvec_rwlock );
- if ( rc == 0 )
+ 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 )
@@ -12426,12 +12108,6 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ
_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
- host_to_string( &client->host, client->hostName, HOSTNAMELEN );
- const uint8_t *port = memchr( client->hostName, ':', HOSTNAMELEN );
-
- if ( port != NULL )
- port++;
-
pthread_rwlock_wrlock( &iscsi_globvec_rwlock );
if ( iscsi_globvec == NULL )
@@ -12477,11 +12153,34 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ
}
}
- uint8_t *tmp_buf = iscsi_sprintf_alloc( "%s:%s", client->hostName, port );
+ 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;
@@ -12496,6 +12195,7 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ
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;
@@ -12505,11 +12205,17 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ
rc = iscsi_hashmap_get( portal_group->portals, (uint8_t *) hash_key, key_len, (uint8_t **) &portal );
if ( portal == NULL ) {
- portal = iscsi_portal_create( (uint8_t *) client->hostName, port );
+ 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" );
+ free( host );
pthread_rwlock_unlock( &iscsi_globvec_rwlock );
return;
@@ -12519,18 +12225,21 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ
if ( rc < 0 ) {
iscsi_portal_destroy( portal );
-
+ free( host );
pthread_rwlock_unlock( &iscsi_globvec_rwlock );
return;
}
}
+ 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 );
@@ -12543,17 +12252,19 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ
logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI connection" );
iscsi_connection_destroy( conn );
+ iscsi_portal_group_del_portal( portal_group, portal );
iscsi_portal_destroy( portal );
pthread_rwlock_unlock( &iscsi_globvec_rwlock );
return;
}
- conn->pdu_processing = iscsi_connection_pdu_create( conn );
+ conn->pdu_processing = iscsi_connection_pdu_create( conn, 0U, 0, 0UL, 0 );
if ( conn->pdu_processing == NULL ) {
iscsi_hashmap_key_destroy( (uint8_t *) hash_key );
iscsi_connection_destroy( conn );
+ iscsi_portal_group_del_portal( portal_group, portal );
iscsi_portal_destroy( portal );
pthread_rwlock_unlock( &iscsi_globvec_rwlock );
@@ -12575,6 +12286,7 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ
iscsi_connection_pdu_destroy( conn->pdu_processing );
iscsi_hashmap_key_destroy( (uint8_t *) hash_key );
iscsi_connection_destroy( conn );
+ iscsi_portal_group_del_portal( portal_group, portal );
iscsi_portal_destroy( portal );
pthread_rwlock_unlock( &iscsi_globvec_rwlock );
@@ -12585,4 +12297,31 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ
while ( iscsi_connection_pdu_handle( conn ) >= ISCSI_CONNECT_PDU_READ_OK ) {
}
+
+ pthread_rwlock_wrlock( &iscsi_globvec_rwlock );
+
+ if ( iscsi_globvec == NULL )
+ return;
+
+ iscsi_hashmap_bucket *stat_bucket;
+
+ 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 ? conn->device->name : "(null)"), conn->init_name, ((conn->init_port != NULL) ? conn->init_port->name : "(null)"), portal->host, 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 ? conn->device->name : "(null)"), conn->init_name, ((conn->init_port != NULL) ? conn->init_port->name : "(null)"), portal->host, portal->port, *(uint64_t *) stat_bucket->key, *stat_opcode );
+ }
+
+ iscsi_connection_destroy( conn );
+
+ iscsi_portal_group_del_portal( portal_group, portal );
+ iscsi_portal_destroy( portal );
+
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
}
diff --git a/src/server/iscsi.h b/src/server/iscsi.h
index dac8e8f..6f48e80 100644
--- a/src/server/iscsi.h
+++ b/src/server/iscsi.h
@@ -36,9 +36,9 @@
extern "C" {
#endif
-#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
+#include <stdint.h>
#include <stdio.h>
#include <sys/types.h>
#include <dnbd3/types.h>
@@ -47,6 +47,26 @@ extern "C" {
#include "globals.h"
#include "image.h"
+#if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
+ // GCC-compatible compiler, targeting x86/x86-64
+ #include <x86intrin.h>
+#elif defined(__GNUC__) && defined(__ARM_NEON__)
+ // GCC-compatible compiler, targeting ARM with NEON
+ #include <arm_neon.h>
+#elif defined(__GNUC__) && defined(__IWMMXT__)
+ // GCC-compatible compiler, targeting ARM with WMMX
+ #include <mmintrin.h>
+#elif (defined(__GNUC__) || defined(__xlC__)) && (defined(__VEC__) || defined(__ALTIVEC__))
+ // XLC or GCC-compatible compiler, targeting PowerPC with VMX/VSX
+ #include <altivec.h>
+#elif defined(__GNUC__) && defined(__SPE__)
+ // GCC-compatible compiler, targeting PowerPC with SPE
+ #include <spe.h>
+#elif defined(_MSC_VER)
+ // Microsoft C/C++-compatible compiler
+ #include <intrin.h>
+#endif
+
#if defined(__BIG_ENDIAN__) || (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN) || (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#define iscsi_get_be16(x) (x)
#define iscsi_get_be24(x) (iscsi_get_be32((*(uint32_t *) ((uint8_t *) x - 1))) & 0xFFFFFFUL)
@@ -74,27 +94,55 @@ static inline void iscsi_put_be64(uint8_t *data, const uint64_t value)
{
(*(uint64_t *) data) = 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(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
- // GCC-compatible compiler, targeting x86/x86-64
- #include <x86intrin.h>
-#elif defined(__GNUC__) && defined(__ARM_NEON__)
- // GCC-compatible compiler, targeting ARM with NEON
- #include <arm_neon.h>
-#elif defined(__GNUC__) && defined(__IWMMXT__)
- // GCC-compatible compiler, targeting ARM with WMMX
- #include <mmintrin.h>
-#elif (defined(__GNUC__) || defined(__xlC__)) && (defined(__VEC__) || defined(__ALTIVEC__))
- // XLC or GCC-compatible compiler, targeting PowerPC with VMX/VSX
- #include <altivec.h>
-#elif defined(__GNUC__) && defined(__SPE__)
- // GCC-compatible compiler, targeting PowerPC with SPE
- #include <spe.h>
+
+#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)
- // Microsoft C/C++-compatible compiler
- #include <intrin.h>
+// 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
#define iscsi_get_be16(x) (__builtin_bswap16(x))
@@ -142,6 +190,33 @@ 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
@@ -257,7 +332,12 @@ static inline uint32_t iscsi_get_log2_of_pow2(const uint32_t value)
/// 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)) - 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
@@ -343,15 +423,246 @@ typedef struct iscsi_list {
#define iscsi_list_foreach_safe_node(list, entry, tmp) iscsi_list_foreach_safe_field(list, entry, node, tmp)
-void iscsi_list_create(iscsi_list *list); // Initializes a doubly linked list for usage
-void iscsi_list_push(iscsi_list *list, iscsi_node *node); // Adds a node at the head of a doubly linked list
-void iscsi_list_enqueue(iscsi_list *list, iscsi_node *node); // Adds a node at the tail of a doubly linked list
-void iscsi_list_insert(iscsi_list *list, iscsi_node *node, iscsi_node *pred); // Inserts a node into a doubly linked list before an already existing node
-void iscsi_list_pop(iscsi_list *list); // Removes the node from the head of a doubly linked list
-void iscsi_list_dequeue(iscsi_list *list); // Removes the node from the tail of a doubly linked list
-void iscsi_list_remove(iscsi_node *node); // Removes a specified node from a doubly linked list
-bool iscsi_list_empty(const iscsi_list *list); // Checks whether a doubly linked list is empty
-iscsi_node *iscsi_list_peek(const iscsi_list *list); // Gets the node from the head of a doubly linked list
+/**
+ * @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;
+}
/**
@@ -362,7 +673,7 @@ iscsi_node *iscsi_list_peek(const iscsi_list *list); // Gets the node from the h
*/
typedef struct iscsi_hashmap_bucket {
/// Next bucket, must be first element.
- struct iscsi_hashmap_bucket *next;
+ iscsi_node node;
/// Data used as key, must be aligned to 8 bytes and zero padded.
uint8_t *key;
@@ -382,33 +693,26 @@ 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;
-
- /// Last inserted unique identifier (primary key).
- uint64_t last_insert_id;
} iscsi_hashmap;
/**
@@ -447,19 +751,18 @@ int iscsi_hashmap_key_destroy_callback(uint8_t *key, const size_t key_size, uint
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 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 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_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 which has not been removed
+int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, uint8_t *user_data); // Iterator with callback function invoked on each element
/* iSCSI protocol stuff (all WORD/DWORD/QWORD values are big endian by default
unless specified otherwise). */
@@ -4878,6 +5181,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cmd_packet {
*/
#define ISCSI_SCSI_RESPONSE_STATUS_TASK_ABORTED 0x40
+
/// SCSI response code: Command Completed at Target.
#define ISCSI_SCSI_RESPONSE_CODE_OK 0x00
@@ -9178,7 +9482,7 @@ typedef struct __attribute__((packed)) iscsi_reject_packet {
uint32_t data_r2t_sn;
/// Reserved for future usage, always MUST be 0.
- uint32_t reserved4[2];
+ uint64_t reserved4;
/// Optional header digest.
iscsi_header_digest hdr_digest;
@@ -9369,7 +9673,10 @@ typedef struct __attribute__((packed)) iscsi_nop_in_packet {
uint32_t max_cmd_sn;
/// Reserved for future usage, always MUST be 0.
- uint32_t reserved2[3];
+ uint32_t reserved2;
+
+ /// Reserved for future usage, always MUST be 0.
+ uint64_t reserved3;
/// Optional header digest.
iscsi_header_digest hdr_digest;
@@ -9440,54 +9747,8 @@ typedef struct __attribute__((packed)) iscsi_transport_id {
} iscsi_transport_id;
-/// iSCSI packet validation return code from iscsi_validate_packet function: Validation successful -> iSCSI packet recognized and compliance to protocol specification.
-#define ISCSI_VALIDATE_PACKET_RESULT_OK 0
-
-/// iSCSI packet validation return code from iscsi_validate_packet function: Validation failed -> No packet data specified.
-#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_NO_DATA -1
-
-/// 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 -2
-
-/// 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 -3
-
-/// 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 -4
-
-/// 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 -5
-
-/// 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 -6
-
-/// 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 -7
-
-/// 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 -8
-
-
-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
-
-
/// Maximum length of a key according to iSCSI specifications.
-#define ISCSI_TEXT_KEY_MAX_LEN 63U
+#define ISCSI_TEXT_KEY_MAX_LEN 63U
/// Maximum length of value for a simple key type.
#define ISCSI_TEXT_VALUE_MAX_SIMPLE_LEN 255U
@@ -9496,10 +9757,10 @@ int iscsi_validate_data_digest(const iscsi_bhs_packet *packet_data, const int he
#define ISCSI_TEXT_VALUE_MAX_LEN 8192U
/// Value data shift value for key value alignment enforcement.
-#define ISCSI_TEXT_VALUE_ALIGN_SHIFT 4UL
+#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))
+#define ISCSI_TEXT_VALUE_ALIGN (1UL << (ISCSI_TEXT_VALUE_ALIGN_SHIFT))
/// iSCSI text key=value pair type: Invalid.
@@ -9694,7 +9955,6 @@ extern pthread_rwlock_t iscsi_globvec_rwlock;
int iscsi_create(); // Allocates and initializes the iSCSI global vector structure
-int iscsi_global_key_value_pair_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // iSCSI global key and value pair destructor callback for hash map
void iscsi_destroy(); // Deallocates all resources acquired by iscsi_create
/**
@@ -9768,6 +10028,7 @@ typedef struct iscsi_portal {
iscsi_portal_group *iscsi_portal_group_create(const uint64_t 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_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
void iscsi_portal_destroy(iscsi_portal *portal);
@@ -10261,11 +10522,11 @@ void iscsi_scsi_lun_destroy(iscsi_scsi_lun *lun); // Deallocates all resources a
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 hash map
+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 hash map and / or runs it directly
+void iscsi_scsi_lun_task_exec(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task); // Appends iSCSI SCSI task to pending tasks doubly linked list and / or runs it directly
int iscsi_scsi_pr_check_scsi2(iscsi_scsi_task *scsi_task); // Checks the iSCSI SCSI Persistent Reservation (PR) SCSI-2 reserve of an iSCSI SCSI task
int iscsi_scsi_pr_registrant_get_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Finds an iSCSI SCSI Persistent Reservation (PR) registrant by target and initiator port
@@ -10383,32 +10644,6 @@ typedef struct iscsi_scsi_lun {
} iscsi_scsi_lun;
-typedef struct iscsi_pdu iscsi_pdu;
-typedef struct iscsi_task iscsi_task;
-
-
-/**
- * @brief iSCSI PDU search and removal by Ready To Transfer Sequence Number (R2TSN).
- *
- * This structure is used by iterating through
- * all SNACK PDUs finding by Ready To Transfer
- * Sequence Number (R2TSN).
- */
-typedef struct iscsi_r2t_remove_pdu {
- /// Found iSCSI PDU is stored here, should be initialized to NULL.
- iscsi_pdu *pdu;
-
- /// iSCSI task containing the Ready To Transfer Sequence Number (R2TSN).
- iscsi_task *task;
-
- /// Hash map containing SNACK PDU's associated with this removal task.
- iscsi_hashmap *pdu_snack;
-
- /// The Ready To Transfer Sequence Number (R2TSN) to be removed.
- uint32_t r2t_sn;
-} iscsi_r2t_remove_pdu;
-
-
/// iSCSI device flags: Allocated.
#define ISCSI_DEVICE_FLAGS_ALLOCATED (1 << 0)
@@ -10459,6 +10694,9 @@ typedef struct iscsi_device_find_lun_id {
} iscsi_device_find_lun_id;
+/// iSCSI target node WWN identifier prefix string.
+#define ISCSI_TARGET_NODE_WWN_NAME_PREFIX "wwn-0x"
+
/// iSCSI target node maximum length
#define ISCSI_TARGET_NODE_MAX_NAME_LEN 223U
@@ -10671,6 +10909,9 @@ 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 0
@@ -10808,8 +11049,11 @@ typedef struct iscsi_connection {
/// Doubly linked list containing enqueued SCSI Data In tasks.
iscsi_list scsi_data_in_queued_tasks;
- /// Hash map containing SNACK PDU's associated with this connection.
- iscsi_hashmap *pdu_snack;
+ /// 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;
@@ -10894,12 +11138,21 @@ typedef struct iscsi_connection {
/// ExpStatSN.
uint32_t exp_stat_sn;
+
+ // TODO: Remove after test finish
+ iscsi_hashmap *stat_iscsi_opcodes;
+
+ // TODO: Remove after test finish
+ iscsi_hashmap *stat_scsi_opcodes;
} iscsi_connection;
typedef void (*iscsi_connection_xfer_complete_callback)(uint8_t *user_data); // iSCSI transfer completed callback function.
+typedef struct iscsi_task iscsi_task;
+
+
/// iSCSI PDU flags: Rejected.
#define ISCSI_PDU_FLAGS_REJECTED (1 << 0)
@@ -10912,6 +11165,9 @@ typedef void (*iscsi_connection_xfer_complete_callback)(uint8_t *user_data); //
* and filling the BHS, AHS and DS properly.
*/
typedef struct iscsi_pdu {
+ /// Doubly linked list node.
+ iscsi_node node;
+
/// iSCSI Basic Header Segment (BHS) packet data.
iscsi_bhs_packet *bhs_pkt;
@@ -10983,6 +11239,19 @@ typedef struct iscsi_pdu {
} iscsi_pdu;
+/**
+ * @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 task flags: Ready To Transfer is active.
#define ISCSI_TASK_FLAGS_R2T_ACTIVE (1 << 0)
@@ -11075,7 +11344,7 @@ void iscsi_task_destroy(iscsi_task *task); // Deallocates resources acquired by
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) hash map by Target Transfer Tag (TTT)
+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.
@@ -11095,7 +11364,7 @@ int iscsi_target_node_create_callback(uint8_t *key, const size_t key_size, uint8
iscsi_target_node *iscsi_target_node_create(uint8_t *name, const uint8_t *alias, const int index, const uint luns, 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
void iscsi_target_node_destroy(iscsi_target_node *target); // Deallocates all resources acquired by iscsi_target_node_create
-int 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
+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
@@ -11124,16 +11393,24 @@ int iscsi_connection_init_key_value_pairs(iscsi_hashmap *key_value_pairs); // In
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
-
-iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn); // Creates an iSCSI PDU structure used by connections
+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
-void iscsi_connection_pdu_ack_remove(iscsi_connection *conn, const uint32_t exp_stat_sn); // Removes an acknowledged PDU from SNACK PDU hash map by ExpStatSN
+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 hash map
+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_r2t_remove_pdu_from_snack_list_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Finds and removes an iSCSI PDU by Ready To Transfer Sequence Number (R2TSN)
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);