summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Vater2025-07-25 15:30:05 +0200
committerSebastian Vater2025-07-25 15:30:05 +0200
commit92050d3e96b0e18bfc3658893e2043258a35e1bd (patch)
tree4f41c64a1dde78ada2230e91553506722e6defb6
parentResize hash map after half capacity load instead full in order to avoid bucke... (diff)
downloaddnbd3-92050d3e96b0e18bfc3658893e2043258a35e1bd.tar.gz
dnbd3-92050d3e96b0e18bfc3658893e2043258a35e1bd.tar.xz
dnbd3-92050d3e96b0e18bfc3658893e2043258a35e1bd.zip
Added initial capacity parameter for iSCSI hash map creation. Also mostly finished iSCSI packet validation function and fixed some iSCSI data packet structures.
-rw-r--r--src/server/iscsi.c197
-rw-r--r--src/server/iscsi.h86
2 files changed, 206 insertions, 77 deletions
diff --git a/src/server/iscsi.c b/src/server/iscsi.c
index 19eb8d4..9bdfe83 100644
--- a/src/server/iscsi.c
+++ b/src/server/iscsi.c
@@ -39,16 +39,20 @@
*/
/**
- * Creates a ultra hardcore speed optimized empty hash map and allocates
- * enough buckets to hold default capacity elements.
+ * Creates a ultra hardcore speed optimized empty hash map and
+ * allocates enough buckets to hold default capacity elements.
* The speed optimizations require all keys having a size of
- * a multiple of 8 bytes with zero padding.
+ * a multiple of 8 bytes with zero padding. Also the capacity
+ * always nas to be a power of two.
* TODO: Move all hash map related functions to different source file
* later and implement in a lock-free way for better concurrency.
*
+ * @param[in] capacity Desired initial capacity, will be rounded up
+ * to the nearest power of two. If set to 0, a default capacity of
+ * 32 buckets will be used instead.
* @return A pointer to the hash map structure or NULL in case of an error.
*/
-iscsi_hashmap *iscsi_hashmap_create()
+iscsi_hashmap *iscsi_hashmap_create(const uint capacity)
{
iscsi_hashmap *map = (iscsi_hashmap *) malloc( sizeof(iscsi_hashmap) );
@@ -58,7 +62,19 @@ iscsi_hashmap *iscsi_hashmap_create()
return map;
}
- map->capacity = (1UL << ISCSI_HASHMAP_DEFAULT_CAPACITY);
+ if ( capacity > 0UL ) {
+ uint new_capacity = (capacity + 1); // 1UL << (lg(capacity - 1) + 1)
+
+ new_capacity |= (new_capacity >> 1UL);
+ new_capacity |= (new_capacity >> 2UL);
+ new_capacity |= (new_capacity >> 4UL);
+ new_capacity |= (new_capacity >> 8UL);
+ new_capacity |= (new_capacity >> 16UL);
+
+ map->capacity = ++new_capacity; // Round up actual new capacity to nearest power of two
+ } else {
+ map->capacity = ISCSI_HASHMAP_DEFAULT_CAPACITY;
+ }
map->buckets = (iscsi_hashmap_bucket *) calloc( map->capacity, sizeof(struct iscsi_hashmap_bucket) );
@@ -940,10 +956,16 @@ int iscsi_validate_data_digest(const iscsi_bhs_packet *packet_data, const int he
}
/**
- * Checks whether packet data is an iSCSI packet. Since iSCSI doesn't
- * have a magic identifier for its packets, a partial heuristic approach
- * is needed for it. There is not always a guarantee that a packet
- * clearly belongs to iSCSI.
+ * Checks whether packet data is an iSCSI packet or not.
+ * Since iSCSI doesn't have a magic identifier for its packets, a
+ * partial heuristic approach is needed for it. There is not always
+ * a guarantee that a packet clearly belongs to iSCSI. If header
+ * and/or data digests are present, their respective CRC32C's are
+ * validated, too. Note that this function does NOT check if the
+ * iSCSI command fits in, e.g. a SCSI data in/out command without
+ * prior authentication, which is NOT allowed by the iSCSI standard,
+ * will NOT be considered an error by this function as it has no
+ * knowledge of prior commands sent.
*
* @param[in] packet_data Pointer to packet data to check for iSCSI, may
* not be NULL.
@@ -960,67 +982,110 @@ int iscsi_validate_data_digest(const iscsi_bhs_packet *packet_data, const int he
* were necessary. A negative value is returned in case of an error or
* if it's clearly not an iSCSI packet.
*/
-int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint32_t len, const uint32_t header_digest_size, const uint32_t data_digest_size)
+int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint32_t len, const int header_digest_size, const int data_digest_size)
{
if ( packet_data == NULL )
- return -1; // Packet data not specified (prevent null pointer exception)
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_NO_DATA;
else if ( len < sizeof (struct iscsi_bhs_packet) )
- return -2; // Packet data length smaller than minimum required iSCSI packet size -> clearly no iSCSI packet
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_SIZE_TOO_SMALL;
const uint32_t ahs_len = (uint32_t) packet_data->total_ahs_len << 2UL; // AHS length is in DWORD's
const uint32_t ds_len = (uint32_t) iscsi_get_be24(packet_data->ds_len);
if ( len != (sizeof (struct iscsi_bhs_packet) + ahs_len + header_digest_size + iscsi_align(ds_len, ISCSI_ALIGN_SIZE) + data_digest_size) )
- return -3; // Calculated packet length doesn't match with actual size of iSCSI packet data
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_SIZE_MISMATCH;
const uint8_t opcode = ISCSI_GET_OPCODE(packet_data->opcode);
switch ( opcode ) {
case ISCSI_CLIENT_NOP_OUT : {
if ( (int8_t) packet_data->opcode < 0 )
- return -4; // MSB always MUST be cleared for this opcode -> no iSCSI packet data
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // MSB always MUST be cleared for this opcode -> invalid iSCSI packet data
+
+ const iscsi_nop_out_packet *nop_out_pkt = (const iscsi_nop_out_packet *) packet_data;
+
+ if ( (*(uint16_t *) nop_out_pkt->reserved != 0) || (nop_out_pkt->reserved[2] != 0) || (nop_out_pkt->reserved2[0] != 0) || (nop_out_pkt->reserved2[1] != 0) )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
break;
}
case ISCSI_CLIENT_SCSI_CMD : {
if ( (int8_t) packet_data->opcode < 0 )
- return -4; // MSB always MUST be cleared for this opcode -> no iSCSI packet data
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // MSB always MUST be cleared for this opcode -> invalid iSCSI packet data
+
+ const iscsi_scsi_cmd_packet *scsi_cmd_pkt = (const iscsi_scsi_cmd_packet *) packet_data;
+
+ if ( scsi_cmd_pkt->reserved != 0 )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved field needs to be zero, but is NOT -> invalid iSCSI packet data
break;
}
case ISCSI_CLIENT_TASK_FUNC_REQ : {
if ( ((int8_t) packet_data->opcode < 0) || (ahs_len != 0) || (ds_len != 0) )
- return -4; // MSB MUST always be cleared, AHS and DataSegment MUST be zero according to specs -> no iSCSI packet data
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // MSB MUST always be cleared, AHS and DataSegment MUST be zero according to specs -> invalid iSCSI packet data
+
+ const iscsi_task_mgmt_func_req_packet *task_mgmt_func_req_pkt = (const iscsi_task_mgmt_func_req_packet *) packet_data;
+
+ if ( (task_mgmt_func_req_pkt->reserved != 0) || (task_mgmt_func_req_pkt->reserved2 != 0) )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
break;
}
case ISCSI_CLIENT_LOGIN_REQ : {
if ( (packet_data->opcode != (opcode | 0x40)) || (ahs_len != 0) || (ds_len != 0) )
- return -4; // Bit 6 always MUST be set, AHS and DataSegment MUST be zero according to specs -> no iSCSI packet data
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bit 6 always MUST be set, AHS and DataSegment MUST be zero according to specs -> invalid iSCSI packet data
+
+ const iscsi_login_req_packet *login_req_pkt = (const iscsi_login_req_packet *) packet_data;
+
+ if ( (ISCSI_VERSION_MIN < login_req_pkt->version_min) || (ISCSI_VERSION_MAX > login_req_pkt->version_max) )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_UNSUPPORTED_VERSION;
+
+ if ( (login_req_pkt->reserved != 0) || (login_req_pkt->reserved2[0] != 0) || (login_req_pkt->reserved2[1] != 0) )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
break;
}
case ISCSI_CLIENT_TEXT_REQ : {
- if ( (int8_t) packet_data->opcode < 0 )
- return -4; // MSB always MUST be cleared for this opcode -> no iSCSI packet data
+ if ( ((int8_t) packet_data->opcode < 0) || (ds_len == 0) )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // MSB always MUST be cleared for this opcode and DataSegment is mandatory -> invalid iSCSI packet data
+
+ const iscsi_text_req_packet *text_req_pkt = (const iscsi_text_req_packet *) packet_data;
+
+ if ( (text_req_pkt->reserved != 0) || (text_req_pkt->reserved2[0] != 0) || (text_req_pkt->reserved2[1] != 0) )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
break;
}
case ISCSI_CLIENT_SCSI_DATA_OUT : {
- if ( packet_data->opcode != opcode )
- return -4; // Bits 6 and 7 always MUST be cleared for this opcode -> no iSCSI packet data
+ if ( (packet_data->opcode != opcode) || (ds_len == 0) )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bits 6 and 7 always MUST be cleared for this opcode and DataSegment is mandatory -> invalid iSCSI packet data
+
+ const iscsi_scsi_data_out_req_packet *scsi_data_out_req_pkt = (const iscsi_scsi_data_out_req_packet *) packet_data;
+
+ if ( (scsi_data_out_req_pkt->reserved != 0) || (scsi_data_out_req_pkt->reserved2 != 0) || (scsi_data_out_req_pkt->reserved3 != 0) || (scsi_data_out_req_pkt->reserved4 != 0) )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
break;
}
case ISCSI_CLIENT_LOGOUT_REQ : {
if ( (int8_t) packet_data->opcode < 0 )
- return -4; // MSB always MUST be cleared for this opcode -> no iSCSI packet data
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // MSB always MUST be cleared for this opcode -> invalid iSCSI packet data
+
+ const iscsi_logout_req_packet *logout_req_pkt = (const iscsi_logout_req_packet *) packet_data;
+
+ if ( (logout_req_pkt->reserved != 0) || (logout_req_pkt->reserved2 != 0) || (logout_req_pkt->reserved3 != 0) || (logout_req_pkt->reserved4[0] != 0) || (logout_req_pkt->reserved4[1] != 0) )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
break;
}
case ISCSI_CLIENT_SNACK_REQ : {
if ( packet_data->opcode != opcode )
- return -4; // Bits 6 and 7 always MUST be cleared for this opcode -> no iSCSI packet data
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bits 6 and 7 always MUST be cleared for this opcode -> invalid iSCSI packet data
+
+ const iscsi_snack_req_packet *snack_req_pkt = (const iscsi_snack_req_packet *) packet_data;
+
+ if ( (snack_req_pkt->reserved != 0) || (snack_req_pkt->reserved2 != 0) || (snack_req_pkt->reserved3 != 0) )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
break;
}
@@ -1031,55 +1096,98 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint
}
case ISCSI_SERVER_NOP_IN : {
if ( packet_data->opcode != opcode )
- return -4; // Bits 6 and 7 always MUST be cleared for this opcode -> no iSCSI packet data
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bits 6 and 7 always MUST be cleared for this opcode -> invalid iSCSI packet data
+
+ const iscsi_nop_in_packet *nop_in_pkt = (const iscsi_nop_in_packet *) packet_data;
+
+ if ( (*(uint16_t *) nop_in_pkt->reserved != 0) || (nop_in_pkt->reserved[2] != 0) || (nop_in_pkt->reserved2[0] != 0) || (nop_in_pkt->reserved2[1] != 0) )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
break;
}
case ISCSI_SERVER_SCSI_RESPONSE : {
if ( packet_data->opcode != opcode )
- return -4; // Bits 6 and 7 always MUST be cleared for this opcode -> no iSCSI packet data
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bits 6 and 7 always MUST be cleared for this opcode -> invalid iSCSI packet data
break;
}
case ISCSI_SERVER_TASK_FUNC_RES : {
if ( (packet_data->opcode != opcode) || (ahs_len != 0) || (ds_len != 0) )
- return -4; // Bits 6 and 7 always MUST be cleared, AHS and DataSegment MUST be zero according to specs -> no iSCSI packet data
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bits 6 and 7 always MUST be cleared, AHS and DataSegment MUST be zero according to specs -> invalid iSCSI packet data
+
+ const iscsi_task_mgmt_func_response_packet *task_mgmt_func_response_pkt = (const iscsi_task_mgmt_func_response_packet *) packet_data;
+
+ if ( (task_mgmt_func_response_pkt->reserved != 0) || (task_mgmt_func_response_pkt->reserved2 != 0) || (task_mgmt_func_response_pkt->reserved3 != 0) || (task_mgmt_func_response_pkt->reserved4 != 0) || (task_mgmt_func_response_pkt->reserved5 != 0) )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
break;
}
case ISCSI_SERVER_LOGIN_RES : {
- if ( packet_data->opcode != opcode )
- return -4; // Bits 6 and 7 always MUST be cleared for this opcode -> no iSCSI packet data
+ if ( (packet_data->opcode != opcode) || (ds_len == 0) )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bits 6 and 7 always MUST be cleared for this opcode -> invalid iSCSI packet data and DataSegment is mandatory
+
+ const iscsi_login_response_packet *login_response_pkt = (const iscsi_login_response_packet *) packet_data;
+
+ if ( (ISCSI_VERSION_MIN < login_response_pkt->version_max) || (ISCSI_VERSION_MAX > login_response_pkt->version_max) || (ISCSI_VERSION_MIN < login_response_pkt->version_active) || (ISCSI_VERSION_MAX > login_response_pkt->version_active) )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_UNSUPPORTED_VERSION;
+
+ if ( (login_response_pkt->reserved != 0) || (login_response_pkt->reserved2 != 0) || (login_response_pkt->reserved3 != 0) )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
break;
}
case ISCSI_SERVER_TEXT_RES : {
- if ( packet_data->opcode != opcode )
- return -4; // Bits 6 and 7 always MUST be cleared for this opcode -> no iSCSI packet data
+ if ( (packet_data->opcode != opcode) || (ds_len == 0) )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bits 6 and 7 always MUST be cleared for this opcode and DataSegment is mandatory -> invalid iSCSI packet data
+
+ const iscsi_text_response_packet *text_response_pkt = (const iscsi_text_response_packet *) packet_data;
+
+ if ( (text_response_pkt->reserved != 0) || (text_response_pkt->reserved2[0] != 0) || (text_response_pkt->reserved2[1] != 0) )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
break;
}
case ISCSI_SERVER_SCSI_DATA_IN : {
- if ( packet_data->opcode != opcode )
- return -4; // Bits 6 and 7 always MUST be cleared for this opcode -> no iSCSI packet data
+ if ( (packet_data->opcode != opcode) || (ds_len == 0) )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bits 6 and 7 always MUST be cleared for this opcode and DataSegment is mandatory -> invalid iSCSI packet data
+
+ const iscsi_scsi_data_in_response_packet *scsi_data_in_pkt = (const iscsi_scsi_data_in_response_packet *) packet_data;
+
+ if ( scsi_data_in_pkt->reserved != 0 )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved field needs to be zero, but is NOT -> invalid iSCSI packet data
break;
}
case ISCSI_SERVER_LOGOUT_RES : {
if ( (packet_data->opcode != opcode) || (ahs_len != 0) || (ds_len != 0) )
- return -4; // Bits 6 and 7 always MUST be cleared, AHS and DataSegment MUST be zero according to specs -> no iSCSI packet data
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bits 6 and 7 always MUST be cleared, AHS and DataSegment MUST be zero according to specs -> invalid iSCSI packet data
+
+ const iscsi_logout_response_packet *logout_response_pkt = (const iscsi_logout_response_packet *) packet_data;
+
+ if ( (logout_response_pkt->reserved != 0) || (logout_response_pkt->reserved2 != 0) || (logout_response_pkt->reserved3 != 0) || (logout_response_pkt->reserved4 != 0) || (logout_response_pkt->reserved5 != 0) )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
break;
}
case ISCSI_SERVER_READY_XFER : {
if ( (packet_data->opcode != opcode) || (ahs_len != 0) || (ds_len != 0) )
- return -4; // AHS and DataSegment MUST be zero according to specs -> no iSCSI packet data
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // AHS and DataSegment MUST be zero according to specs -> invalid iSCSI packet data
+
+ const iscsi_r2t_packet *r2t_pkt = (const iscsi_r2t_packet *) packet_data;
+
+ if ( r2t_pkt->reserved != 0 )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved field needs to be zero, but is NOT -> invalid iSCSI packet data
break;
}
case ISCSI_SERVER_ASYNC_MSG : {
- if ( packet_data->opcode != opcode )
- return -4; // Bits 6 and 7 always MUST be cleared for this opcode -> no iSCSI packet data
+ if ( (packet_data->opcode != opcode) || (ds_len == 0) )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bits 6 and 7 always MUST be cleared for this opcode and DataSegment is mandatory -> invalid iSCSI packet data
+
+ const iscsi_async_msg_packet *async_msg_pkt = (const iscsi_async_msg_packet *) packet_data;
+
+ if ( (async_msg_pkt->tag != 0xFFFFFFFFUL) || (async_msg_pkt->reserved != 0) || (async_msg_pkt->reserved2 != 0) || (async_msg_pkt->reserved3 != 0) )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
break;
}
@@ -1089,25 +1197,28 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint
break;
}
case ISCSI_SERVER_REJECT : {
- const iscsi_reject_packet *reject_packet = (iscsi_reject_packet *) reject_packet;
+ if ( packet_data->opcode != opcode )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bits 6 and 7 always MUST be cleared according to specs -> invalid iSCSI packet data
+
+ const iscsi_reject_packet *reject_pkt = (const iscsi_reject_packet *) packet_data;
- if ( (packet_data->opcode != opcode) || (reject_packet->tag != 0xFFFFFFFFUL) )
- return -4; // Bits 6 and 7 always MUST be cleared, tag always MUST be 0xFFFFFFFFUL according to specs -> no iSCSI packet data
+ if ( (reject_pkt->tag != 0xFFFFFFFFUL) || (reject_pkt->reserved != 0) || (reject_pkt->reserved2 != 0) || (reject_pkt->reserved3 != 0) || (reject_pkt->reserved4[0] != 0) || (reject_pkt->reserved4[1] != 0) )
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved fields need all to be zero, but are NOT, tag always MUST be 0xFFFFFFFFUL -> invalid iSCSI packet data
break;
}
default : {
- return -6; // Not valid iSCSI opcode, so can't be iSCSI for sure
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_INVALID_OPCODE;
break;
}
}
if ( (header_digest_size != 0) && (iscsi_validate_header_digest( packet_data ) == 0) )
- return -7; // CRC32C of AHS and BHS header doesn't match with header digest
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_CRC32C_HDR_DIGEST;
if ( (data_digest_size != 0) && (iscsi_validate_data_digest( packet_data, header_digest_size ) == 0) )
- return -8; // CRC32C of data segment doesn't match with data digest
+ return ISCSI_VALIDATE_PACKET_RESULT_ERROR_CRC32C_DATA_DIGEST;
- return 0; // All tests for iSCSI passed, return 0
+ return ISCSI_VALIDATE_PACKET_RESULT_OK; // All tests for iSCSI passed, return 0
}
diff --git a/src/server/iscsi.h b/src/server/iscsi.h
index cf8fd34..bd5f320 100644
--- a/src/server/iscsi.h
+++ b/src/server/iscsi.h
@@ -107,7 +107,8 @@ static inline void iscsi_put_be64(uint8_t *data, const uint64_t val)
// Align a value so that it's evenly divisable by n
#define iscsi_align(x, n) (((x) + (n) - 1) & ~((n) - 1))
-#define ISCSI_HASHMAP_DEFAULT_CAPACITY 5UL // Shift factor with base 1
+#define ISCSI_HASHMAP_DEFAULT_CAPACITY_SHIFT 5UL // Shift factor for default capacity
+#define ISCSI_HASHMAP_DEFAULT_CAPACITY (1UL << (ISCSI_HASHMAP_DEFAULT_CAPACITY_SHIFT)) // Default capacity is 32 buckets
#define ISCSI_HASHMAP_RESIZE_SHIFT 1UL // Number of bits to shift left when resizing
#define ISCSI_HASHMAP_KEY_ALIGN_SHIFT 3UL // Key data shift value for alignment enforcement
#define ISCSI_HASHMAP_KEY_ALIGN (1UL << (ISCSI_HASHMAP_KEY_ALIGN_SHIFT)) // Key data size must be multiple of 8 bytes by now
@@ -151,7 +152,7 @@ typedef struct iscsi_hashmap {
typedef int (*iscsi_hashmap_callback)(const uint8_t *key, size_t key_size, uint8_t *value, uint8_t *user_data); // A Callback for iterating over map, freeing and removing entries.
// user_data is free for personal use
-iscsi_hashmap *hashmap_create(); // Creates an empty hash map with default capacity specified as above
+iscsi_hashmap *hashmap_create(const uint capacity); // Creates an empty hash map with default capacity specified as above
void iscsi_hashmap_destroy(iscsi_hashmap *map); // Deallocates the hash map objects and buckets, not elements.
// Use iscsi_hashmap_iterate to deallocate the elements themselves
uint8_t *iscsi_hashmap_create_key(const uint8_t *data, const size_t len); // Creates a key suitable for hashmap usage (ensures 8-byte boundary and zero padding)
@@ -170,9 +171,12 @@ int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, u
/* iSCSI protocol stuff (all WORD/DWORD/QWORD values are big endian by default
unless specified otherwise). */
-#define ISCSI_BHS_SIZE 48 // iSCSI Basic Header Segment size
-#define ISCSI_DIGEST_SIZE 4 // iSCSI header and data digest size (CRC32C)
-#define ISCSI_ALIGN_SIZE 4 // iSCSI packet data alignment (BHS, AHS and DS)
+#define ISCSI_BHS_SIZE 48UL // iSCSI Basic Header Segment size
+#define ISCSI_DIGEST_SIZE 4UL // iSCSI header and data digest size (CRC32C)
+#define ISCSI_ALIGN_SIZE 4UL // iSCSI packet data alignment (BHS, AHS and DS)
+
+#define ISCSI_VERSION_MIN 0 // Current minimum iSCSI protocol version supported by this implementation
+#define ISCSI_VERSION_MAX 0 // Current maximum iSCSI protocol version supported by this implementation
// CRC32C constants for header and data digest
#define ISCSI_CRC32C_INITIAL 0xFFFFFFFFUL
@@ -663,7 +667,7 @@ typedef struct __attribute__((packed)) iscsi_task_mgmt_func_req_packet {
*/
typedef struct __attribute__((packed)) iscsi_task_mgmt_func_response_packet {
uint8_t opcode; // Always 0x22 according to specification (see above)
- uint8_t reserved; // Reserved for future usage
+ uint8_t flags; // Reserved for future usage
uint8_t response; // Function response (see above)
// For the TARGET COLD RESET and TARGET WARM RESET functions, the target
// cancels all pending operations across all LUs known to the issuing
@@ -700,22 +704,23 @@ typedef struct __attribute__((packed)) iscsi_task_mgmt_func_response_packet {
// and the CmdSN indicated by the RefCmdSN field in the Task
// Management Function Request is outside the valid CmdSN window,
// then targets must return the "Task does not exist" response
- uint8_t reserved2; // Reserved for future usage
+ uint8_t reserved; // Reserved for future usage
uint8_t total_ahs_len; // TotalAHSLength (MUST be 0 for this PDU)
uint8_t ds_len[3]; // DataSegmentLength (MUST be 0 for this PDU)
- uint32_t reserved3; // Reserved for future usage
+ uint64_t reserved2; // Reserved for future usage
uint32_t init_task_tag; // Initiator task tag
- uint32_t reserved4; // Reserved for future usage
+ uint32_t reserved3; // Reserved for future usage
uint32_t stat_sn; // StatSN
uint32_t exp_cmd_sn; // ExpCmdSN
uint32_t max_cmd_sn; // MaxCmdSN
- uint32_t reserved5[3]; // Reserved for future usage
+ uint32_t reserved4; // Reserved for future usage
+ uint64_t reserved5; // Reserved for future usage
struct iscsi_header_digest hdr_digest; // Optional header digest
} iscsi_task_mgmt_func_response_packet;
#define ISCSI_SCSI_DATA_OUT_DATA_IN_FLAGS_IMMEDIATE (1 << 7) // Immediately process transfer
-typedef struct __attribute__((packed)) iscsi_scsi_data_out_data_in_packet {
+typedef struct __attribute__((packed)) iscsi_scsi_data_out_req_packet {
uint8_t opcode; // Always 0x02 according to specification (see above)
int8_t flags; // Flags (see above)
uint16_t reserved; // Reserved for future usage
@@ -732,7 +737,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_data_out_data_in_packet {
uint32_t reserved4; // Reserved for future usage
struct iscsi_header_digest hdr_digest; // Optional header digest
struct iscsi_data_digest data_digest; // Optional data digest
-} iscsi_scsi_data_out_data_in_packet;
+} iscsi_scsi_data_out_req_packet;
#define ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS (1 << 0) // (S) set to indicate that the Command Status field
// contains status. If this bit is set to 1, the
@@ -925,7 +930,8 @@ typedef struct __attribute__((packed)) iscsi_scsi_data_in_response_packet {
*/
typedef struct __attribute__((packed)) iscsi_r2t_packet {
uint8_t opcode; // Always 0x31 according to specification (see above)
- uint8_t reserved[3]; // Reserved for future usage
+ uint8_t flags; // Reserved for future usage
+ uint16_t reserved; // Reserved for future usage
uint8_t total_ahs_len; // TotalAHSLength, MUST be 0 for this PDU
uint8_t ds_len[3]; // DataSegmentLength, MUST be 0 0 for this PDU
uint64_t lun; // Logical Unit Number (LUN) or Reserved
@@ -1044,8 +1050,9 @@ typedef struct __attribute__((packed)) iscsi_r2t_packet {
others are related to SCSI
*/
typedef struct __attribute__((packed)) iscsi_async_msg_packet {
- uint8_t opcode; // Always 0x32 according to specification (see above)
- uint8_t reserved[3]; // Reserved for future usage
+ uint8_t opcode; // Always 0x32 according to specification (see above
+ uint8_t flags; // Reserved for future usage
+ uint16_t reserved; // Reserved for future usage
uint8_t total_ahs_len; // TotalAHSLength, MUST be 0 for this PDU
uint8_t ds_len[3]; // DataSegmentLength, MUST be 0 0 for this PDU
uint64_t lun; // The LUN field MUST be valid if AsyncEvent is 0. Otherwise, this
@@ -1111,7 +1118,7 @@ typedef struct __attribute__((packed)) iscsi_sense_event_data_packet {
typedef struct __attribute__((packed)) iscsi_text_req_packet {
uint8_t opcode; // Always 0x04 according to specification (see above)
int8_t flags; // Text request flags (see above)
- uint8_t reserved[2]; // Reserved for future usage
+ uint16_t reserved; // Reserved for future usage
uint8_t total_ahs_len; // TotalAHSLength
uint8_t ds_len[3]; // DataSegmentLength
uint64_t lun; // Logical Unit Number (LUN) or Reserved
@@ -1197,7 +1204,7 @@ typedef struct __attribute__((packed)) iscsi_text_req_packet {
typedef struct __attribute__((packed)) iscsi_text_response_packet {
uint8_t opcode; // Always 0x24 according to specification (see above)
int8_t flags; // Text response flags (see above)
- uint8_t reserved[2]; // Reserved for future usage
+ uint16_t reserved; // Reserved for future usage
uint8_t total_ahs_len; // TotalAHSLength
uint8_t ds_len[3]; // DataSegmentLength
uint64_t lun; // Logical Unit Number (LUN) or Reserved
@@ -2177,6 +2184,7 @@ typedef struct __attribute__((packed)) iscsi_login_req_packet {
// A Login Request with a non-zero TSIH and a CID equal to that of an
// existing connection implies a logout of the connection followed by a
// login
+ uint16_t reserved; // Reserved for future usage
uint32_t cmd_sn; // The CmdSN is either the initial command sequence number of a session
// (for the first Login Request of a session - the "leading" login) or
// the command sequence number in the command stream if the login is for
@@ -2200,7 +2208,7 @@ typedef struct __attribute__((packed)) iscsi_login_req_packet {
// Request restarts a connection
// For subsequent Login Requests, it is used to acknowledge the Login
// Responses with their increasing StatSN values
- uint64_t reserved[2]; // Reserved for future usage
+ uint64_t reserved2[2]; // Reserved for future usage
struct iscsi_ds_cmd_data ds_cmd_data; // Data segment - Login Parameters in Text Request Format
// The initiator MUST provide some basic parameters in order
// to enable the target to determine if the initiator may use
@@ -2495,18 +2503,18 @@ typedef struct __attribute__((packed)) iscsi_logout_req_packet {
*/
typedef struct __attribute__((packed)) iscsi_logout_response_packet {
uint8_t opcode; // Always 0x26 according to specification (see above)
- uint8_t reserved; // Reserved for future usage
+ uint8_t flags; // Reserved for future usage
uint8_t response; // Response
- uint8_t reserved2; // Reserved for future usage
+ uint8_t reserved; // Reserved for future usage
uint8_t total_ahs_len; // TotalAHSLength (MUST be 0 for this PDU)
uint8_t ds_len[3]; // DataSegmentLength (MUST be 0 for this PDU)
- uint64_t reserved3; // Reserved for future usage
+ uint64_t reserved2; // Reserved for future usage
uint32_t init_task_tag; // Initiator task tag
- uint32_t reserved4; // Reserved for future usage
+ uint32_t reserved3; // Reserved for future usage
uint32_t stat_sn; // StatSN
uint32_t exp_cmd_sn; // ExpCmdSN
uint32_t max_cmd_sn; // MaxCmdSN
- uint32_t reserved5; // Reserved for future usage
+ uint32_t reserved4; // Reserved for future usage
uint16_t time_wait; // Time2Wait
// If the Logout response code is 0 and the operational
// ErrorRecoveryLevel is 2, this is the minimum amount of time, in
@@ -2537,7 +2545,7 @@ typedef struct __attribute__((packed)) iscsi_logout_response_packet {
// If Time2Retain is 0, the target has already discarded the connection
// (and possibly the session) state along with the task states. No
// reassignment or Logout is required in this case
- uint32_t reserved6; // Reserved for future usage
+ uint32_t reserved5; // Reserved for future usage
struct iscsi_header_digest hdr_digest; // Optional header digest
} iscsi_logout_response_packet;
@@ -2692,7 +2700,7 @@ typedef struct __attribute__((packed)) iscsi_snack_req_packet {
typedef struct __attribute__((packed)) iscsi_reject_packet {
uint8_t opcode; // Always 0x3F according to specification (see above)
- uint8_t reserved; // Reserved for future usage
+ uint8_t flags; // Reserved for future usage
uint8_t reason; // Reject reason (see above for definitions).
// In all the cases in which a pre-instantiated SCSI task is terminated
// because of the reject, the target MUST issue a proper SCSI command
@@ -2705,12 +2713,12 @@ typedef struct __attribute__((packed)) iscsi_reject_packet {
// outstanding R2Ts, if any), the target MUST wait until it receives
// the last expected Data-Out PDUs with the F bit set to 1 before
// sending the Response PDU
- uint8_t reserved2; // Reserved for future usage
+ uint8_t reserved; // Reserved for future usage
uint8_t total_ahs_len; // TotalAHSLength
uint8_t ds_len[3]; // DataSegmentLength
- uint64_t reserved3; // Reserved for future usage
+ uint64_t reserved2; // Reserved for future usage
uint32_t tag; // Always 0xFFFFFFFF for now
- uint32_t reserved4; // Reserved for future usage
+ uint32_t reserved3; // Reserved for future usage
uint32_t stat_sn; // StatSN. This field carries its usual value and is not related to the
// rejected command. The StatSN is advanced after a Reject
uint32_t exp_cmd_sn; // ExpCmdSN. This field carries its usual value and is not related to the
@@ -2722,7 +2730,7 @@ typedef struct __attribute__((packed)) iscsi_reject_packet {
// the Reject reason code is "Protocol Error". The DataSN/R2TSN is the
// next Data/R2T sequence number that the target would send for the
// task, if any
- uint32_t reserved5[2]; // Reserved for future usage
+ uint32_t reserved4[2]; // Reserved for future usage
struct iscsi_header_digest hdr_digest; // Optional header digest
struct iscsi_bhs_packet bad_pdu_hdr; // Complete Header of Bad PDU. The target returns the
// header (not including the digest) of the PDU in error
@@ -2749,7 +2757,7 @@ typedef struct __attribute__((packed)) iscsi_reject_packet {
in order to avoid lengthy sequences of NOP-In and NOP-Out PDUs sent
in response to each other.
*/
-typedef struct __attribute__((packed)) iscsi_nop_out {
+typedef struct __attribute__((packed)) iscsi_nop_out_packet {
uint8_t opcode; // Always 0x00 according to specification (see above)
uint8_t reserved[3]; // Reserved for future usage
uint8_t total_ahs_len; // TotalAHSLength
@@ -2781,7 +2789,7 @@ typedef struct __attribute__((packed)) iscsi_nop_out {
// ping data is indicated by the DataSegmentLength. 0 is a valid value
// for the DataSegmentLength and indicates the absence of ping data
struct iscsi_data_digest data_digest; // Optional data digest
-} iscsi_nop_out;
+} iscsi_nop_out_packet;
/* NOP-In is sent by a target as either a response to a NOP-Out, a
"ping" to an initiator, or a means to carry a changed ExpCmdSN and/or
@@ -2805,7 +2813,7 @@ typedef struct __attribute__((packed)) iscsi_nop_out {
(DataSegmentLength MUST be 0).
*/
-typedef struct __attribute__((packed)) iscsi_nop_in {
+typedef struct __attribute__((packed)) iscsi_nop_in_packet {
uint8_t opcode; // Always 0x20 according to specification (see above)
uint8_t reserved[3]; // Reserved for future usage
uint8_t total_ahs_len; // TotalAHSLength
@@ -2830,7 +2838,17 @@ typedef struct __attribute__((packed)) iscsi_nop_in {
struct iscsi_header_digest hdr_digest; // Optional header digest
struct iscsi_ds_cmd_data ds_ping_data; // DataSegment - Return Ping Data
struct iscsi_data_digest data_digest; // Optional data digest
-} iscsi_nop_in;
+} iscsi_nop_in_packet;
+
+#define ISCSI_VALIDATE_PACKET_RESULT_OK 0L // Validation successful -> iSCSI packet recognized and compliance to protocol specification
+#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_NO_DATA -1L // Validation failed -> No packet data specified
+#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_SIZE_TOO_SMALL -2L // Validation failed -> Packet size smaller than smallest possible iSCSI packet
+#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_SIZE_MISMATCH -3L // Validation failed -> Packet size doesn't match calculated lengths from BHS
+#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_UNSUPPORTED_VERSION -4L // Validation failed -> iSCSI protocol version not supported yet
+#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS -5L // Validation failed -> Valid opcode but violates iSCSI protocol specification
+#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_INVALID_OPCODE -6L // Validation failed -> Invalid opcode according to iSCSI protocol specification
+#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_CRC32C_HDR_DIGEST -7L // Validation failed -> CRC32C check failed for header (BHS and/or AHS)
+#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_CRC32C_DATA_DIGEST -8L // Validation failed -> CRC32C check failed for data segment
iscsi_bhs_packet *iscsi_create_packet(); // Allocate and initialize an iSCSI BHS packet
void iscsi_destroy_packet(iscsi_bhs_packet *packet_data); // Free resources allocated by iscsi_create_packet
@@ -2842,6 +2860,6 @@ void iscsi_calc_header_digest(const iscsi_bhs_packet *packet_data); // Calculate
int iscsi_validate_header_digest(const iscsi_bhs_packet *packet_data); // Validates a stored iSCSI header digest (CRC32C) with actual header data
void iscsi_calc_data_digest(const iscsi_bhs_packet *packet_data, const int header_digest_size); // Calculate iSCSI data digest (CRC32C)
int iscsi_validate_data_digest(const iscsi_bhs_packet *packet_data, const int header_digest_size); // Validates a stored iSCSI data digest (CRC32C) with actual DataSegment
-int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint32_t len, const uint32_t header_digest_size, const uint32_t data_digest_size); // Check if valid iSCSI packet and validate if necessarily
+int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint32_t len, const int header_digest_size, const int data_digest_size); // Check if valid iSCSI packet and validate if necessarily
#endif /* DNBD3_ISCSI_H_ */