From 92050d3e96b0e18bfc3658893e2043258a35e1bd Mon Sep 17 00:00:00 2001 From: Sebastian Vater Date: Fri, 25 Jul 2025 15:30:05 +0200 Subject: Added initial capacity parameter for iSCSI hash map creation. Also mostly finished iSCSI packet validation function and fixed some iSCSI data packet structures. --- src/server/iscsi.c | 197 +++++++++++++++++++++++++++++++++++++++++------------ src/server/iscsi.h | 86 ++++++++++++++--------- 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_ */ -- cgit v1.2.3-55-g7522