diff options
| author | Sebastian Vater | 2025-08-14 16:27:10 +0200 |
|---|---|---|
| committer | Sebastian Vater | 2025-08-14 16:27:10 +0200 |
| commit | f863ae17d2639e21befd1a19861bb618cf7d339a (patch) | |
| tree | c0b0b95c349d89b50d20b0c42118c6c13fc42fb1 | |
| parent | Added graphviz package for creating call and caller function graphs for doxyg... (diff) | |
| download | dnbd3-f863ae17d2639e21befd1a19861bb618cf7d339a.tar.gz dnbd3-f863ae17d2639e21befd1a19861bb618cf7d339a.tar.xz dnbd3-f863ae17d2639e21befd1a19861bb618cf7d339a.zip | |
Implemented iSCSI global vector init and destruction. Also more complete login phase implementation. Finally, some bugs fixed and data structure rearrangements.
| -rw-r--r-- | src/server/iscsi.c | 836 | ||||
| -rw-r--r-- | src/server/iscsi.h | 59 |
2 files changed, 789 insertions, 106 deletions
diff --git a/src/server/iscsi.c b/src/server/iscsi.c index baee49c..89b7324 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -24,6 +24,7 @@ #include <stdlib.h> #include <string.h> #include <inttypes.h> +#include <strings.h> #include <sys/socket.h> #include <dnbd3/shared/log.h> #include <time.h> @@ -42,6 +43,272 @@ * @see https://www.rfc-editor.org/rfc/rfc7143 */ +/// iSCSI connection key and value pair lookup table. +static const iscsi_key_value_pair_lut_entry iscsi_connection_key_value_pair_lut[] = { + { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_HEADER_DIGEST, (uint8_t *) "None", (uint8_t *) "CRC32C,None", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST, (uint8_t *) "None", (uint8_t *) "CRC32C,None", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_RECV_DS_LEN, (uint8_t *) "8192", (uint8_t *) "512,16777215", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_DECLARATIVE }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_OF_MARKER, (uint8_t *) "No", (uint8_t *) "Yes,No", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_AND }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IF_MARKER, (uint8_t *) "No", (uint8_t *) "Yes,No", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_AND }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_OF_MARK_INT, (uint8_t *) "1", (uint8_t *) "1,65535", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IF_MARK_INT, (uint8_t *) "1", (uint8_t *) "1,65535", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD, (uint8_t *) "None", (uint8_t *) "CHAP,None", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_A, (uint8_t *) "5", (uint8_t *) "5", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_N, (uint8_t *) "", (uint8_t *) "", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_R, (uint8_t *) "", (uint8_t *) "", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_I, (uint8_t *) "", (uint8_t *) "", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_C, (uint8_t *) "", (uint8_t *) "", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE }, + { NULL, NULL, NULL, ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_INVALID } +}; + +/// iSCSI session key and value pair lookup table. +static const iscsi_key_value_pair_lut_entry iscsi_session_key_value_pair_lut[] = { + { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS, (uint8_t *) "1", (uint8_t *) "1,65535", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN }, +#if 0 + /* need special handling */ + { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_SEND_TARGETS, (uint8_t *) "", (uint8_t *) "", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE }, +#endif + { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_NAME, (uint8_t *) "", (uint8_t *) "", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_INITIATOR_NAME, (uint8_t *) "", (uint8_t *) "", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS, (uint8_t *) "", (uint8_t *) "", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_INITIATOR_ALIAS, (uint8_t *) "", (uint8_t *) "", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, (uint8_t *) "", (uint8_t *) "", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG, (uint8_t *) "1", (uint8_t *) "1,65535", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_DECLARATIVE }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_INITIAL_R2T, (uint8_t *) "Yes", (uint8_t *) "Yes,No", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_OR }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IMMEDIATE_DATA, (uint8_t *) "Yes", (uint8_t *) "Yes,No", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_AND }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_BURST_LEN, (uint8_t *) "262144", (uint8_t *) "512,16777215", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_FIRST_BURST_LEN, (uint8_t *) "65536", (uint8_t *) "512,16777215", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_WAIT, (uint8_t *) "2", (uint8_t *) "0,3600", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MAX }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_RETAIN, (uint8_t *) "20", (uint8_t *) "0,3600", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_OUTSTANDING_R2T, (uint8_t *) "1", (uint8_t *) "1,65536", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_PDU_IN_ORDER, (uint8_t *) "Yes", (uint8_t *) "Yes,No", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_OR }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_SEQ_IN_ORDER, (uint8_t *) "Yes", (uint8_t *) "Yes,No", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_OR }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_ERR_RECOVERY_LEVEL, (uint8_t *) "0", (uint8_t *) "0,2", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN }, + { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_SESSION_TYPE, (uint8_t *) "Normal", (uint8_t *) "Normal,Discovery", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE }, + { NULL, NULL, NULL, ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_INVALID } +}; + +/** + * @brief Initializes a global key and value pair with type and list / range informations for fast access. + * + * This function is used to initialize the iSCSI + * global key and value pair list containing + * the key types and allowed values. + * + * @param[in] key_value_pairs Pointer to key and value pair hash map + * which should store the global key and value + * informations, may NOT be NULL, so take caution. + * @param[in] lut Lookup table to use for initialization. + * NULL is not allowed here, so be careful. + * @return 0 on success, a negative error code otherwise. + */ +static int iscsi_global_key_value_pair_init(iscsi_hashmap *key_value_pairs, const iscsi_key_value_pair_lut_entry *lut) +{ + for ( uint i = 0; lut[i].key != NULL; i++ ) { + const uint key_len = (uint) strlen( (char *) lut[i].key ) + 1; + uint8_t *hash_key = iscsi_hashmap_key_create( lut[i].key, key_len ); + + if ( hash_key == NULL ) { + logadd( LOG_ERROR, "iscsi_global_key_value_pair_init: Out of memory allocating key" ); + + return -1L; + } + + iscsi_key_value_pair *key_value_pair = (iscsi_key_value_pair *) malloc( sizeof(struct iscsi_key_value_pair) ); + + if ( key_value_pair == NULL ) { + logadd( LOG_ERROR, "iscsi_global_key_value_pair_init: Out of memory allocating key value pair" ); + + iscsi_hashmap_key_destroy( hash_key ); + + return -1L; + } + + key_value_pair->value = lut[i].value; + key_value_pair->list_range = lut[i].list_range; + key_value_pair->type = lut[i].type; + + const int rc = iscsi_hashmap_put( key_value_pairs, hash_key, key_len, (uint8_t *) key_value_pair ); + + if ( rc < 0 ) { + free( key_value_pair ); + iscsi_hashmap_key_destroy( hash_key ); + + return rc; + } + } + + return 0L; +} + +/** + * @brief Allocates and initializes the iSCSI global vector structure. + * + * This function MUST be called before any iSCSI + * related functions are used.\n + * It is safe to call this function if the iSCSI + * global vector already has been initialized + * in which case this function does nothing. + * + * @return 0 if the iSCSI global vector has been initialized + * successfully and is ready to use, a negative + * error code otherwise (memory exhausted). + */ +int iscsi_create() +{ + if ( iscsi_globvec == NULL ) { + iscsi_globals *globvec = (iscsi_globals *) malloc( sizeof(struct iscsi_globals) ); + + if ( globvec == NULL ) { + logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector" ); + + return -1L; + } + + globvec->devices = iscsi_hashmap_create( 0UL ); + + if ( globvec->devices == NULL ) { + logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector devices hash map" ); + + free( globvec ); + + return -1L; + } + + globvec->portal_groups = iscsi_hashmap_create( 0UL ); + + if ( globvec->portal_groups == NULL ) { + logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector portal groups hash map" ); + + iscsi_hashmap_destroy( globvec->devices ); + free( globvec ); + + return -1L; + } + + globvec->target_nodes = iscsi_hashmap_create( 0UL ); + + if ( globvec->target_nodes == NULL ) { + logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector target nodes hash map" ); + + iscsi_hashmap_destroy( globvec->portal_groups ); + iscsi_hashmap_destroy( globvec->devices ); + free( globvec ); + + return -1L; + } + + globvec->sessions = iscsi_hashmap_create( 0UL ); + + if ( globvec->sessions == NULL ) { + logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector sessions hash map" ); + + iscsi_hashmap_destroy( globvec->target_nodes ); + iscsi_hashmap_destroy( globvec->portal_groups ); + iscsi_hashmap_destroy( globvec->devices ); + free( globvec ); + + return -1L; + } + + globvec->connections = iscsi_hashmap_create( 0UL ); + + if ( globvec->connections == NULL ) { + logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector connections hash map" ); + + iscsi_hashmap_destroy( globvec->sessions ); + iscsi_hashmap_destroy( globvec->target_nodes ); + iscsi_hashmap_destroy( globvec->portal_groups ); + iscsi_hashmap_destroy( globvec->devices ); + free( globvec ); + + return -1L; + } + + globvec->key_value_pairs = iscsi_hashmap_create( 32UL ); + + if ( globvec->key_value_pairs == NULL ) { + logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector key and value pairs hash map" ); + + iscsi_hashmap_destroy( globvec->connections ); + iscsi_hashmap_destroy( globvec->sessions ); + iscsi_hashmap_destroy( globvec->target_nodes ); + iscsi_hashmap_destroy( globvec->portal_groups ); + iscsi_hashmap_destroy( globvec->devices ); + free( globvec ); + + return -1L; + } + + int rc = iscsi_global_key_value_pair_init( globvec->key_value_pairs, &iscsi_connection_key_value_pair_lut[0] ); + rc |= iscsi_global_key_value_pair_init( globvec->key_value_pairs, &iscsi_session_key_value_pair_lut[1] ); + + if ( rc < 0 ) { + logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector key and value pairs hash map" ); + + iscsi_hashmap_iterate( globvec->key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL ); + iscsi_hashmap_destroy( globvec->key_value_pairs ); + iscsi_hashmap_destroy( globvec->connections ); + iscsi_hashmap_destroy( globvec->sessions ); + iscsi_hashmap_destroy( globvec->target_nodes ); + iscsi_hashmap_destroy( globvec->portal_groups ); + iscsi_hashmap_destroy( globvec->devices ); + free( globvec ); + + return -1L; + } + + globvec->flags = 0L; + globvec->max_sessions = 0UL; + globvec->chap_group = 0L; + + iscsi_globvec = globvec; + } + + return 0L; +} + +/** + * @brief Deallocates all resources acquired by iscsi_create. + * + * This function MUST be called before program termination + * for ensuring proper clean up.\n + * After this function returns, calling any iSCSI related + * function except iscsi_create is strictly forbidden.\n + * It is safe to call this function if the iSCSI global + * vector already has been destroyed in which case this + * function does nothing. + */ +void iscsi_destroy() +{ + iscsi_globals *globvec = iscsi_globvec; + + if ( globvec != NULL ) { + iscsi_globvec = NULL; + + iscsi_hashmap_iterate( globvec->key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL ); + iscsi_hashmap_destroy( globvec->key_value_pairs ); + globvec->key_value_pairs = NULL; + + iscsi_hashmap_destroy( globvec->connections ); + globvec->connections = NULL; + + iscsi_hashmap_destroy( globvec->sessions ); + globvec->sessions = NULL; + + iscsi_hashmap_destroy( globvec->target_nodes ); + globvec->target_nodes = NULL; + + iscsi_hashmap_destroy( globvec->portal_groups ); + globvec->portal_groups = NULL; + + iscsi_hashmap_destroy( globvec->devices ); + globvec->devices = NULL; + + free( globvec ); + } +} + /** * @brief Allocates and appends a buffer and sprintf's it. * @@ -1369,7 +1636,7 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint if ( (ISCSI_VERSION_MIN < login_req_pkt->version_min) || (ISCSI_VERSION_MAX > login_req_pkt->version_max) ) return ISCSI_VALIDATE_PACKET_RESULT_ERROR_UNSUPPORTED_VERSION; - if ( (ISCSI_LOGIN_REQ_FLAGS_GET_CURRENT_STAGE(login_req_pkt->flags) == ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_RESERVED) || ((ISCSI_LOGIN_REQ_FLAGS_GET_NEXT_STAGE(login_req_pkt->flags) == ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_RESERVED) && ((login_req_pkt->flags & ISCSI_LOGIN_REQ_FLAGS_TRANSMIT) != 0)) || ((login_req_pkt->flags < 0) && ((login_req_pkt->flags & ISCSI_LOGIN_REQ_FLAGS_CONTINUE) != 0)) || (login_req_pkt->flags & ~(ISCSI_LOGIN_REQ_FLAGS_CONTINUE | ISCSI_LOGIN_REQ_FLAGS_TRANSMIT) != 0) || (login_req_pkt->reserved != 0) || (login_req_pkt->reserved2[0] != 0) || (login_req_pkt->reserved2[1] != 0) ) + if ( (ISCSI_LOGIN_REQ_FLAGS_GET_CURRENT_STAGE(login_req_pkt->flags) == ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_RESERVED) || ((ISCSI_LOGIN_REQ_FLAGS_GET_NEXT_STAGE(login_req_pkt->flags) == ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_RESERVED) && ((login_req_pkt->flags & ISCSI_LOGIN_REQ_FLAGS_TRANSIT) != 0)) || ((login_req_pkt->flags < 0) && ((login_req_pkt->flags & ISCSI_LOGIN_REQ_FLAGS_CONTINUE) != 0)) || (login_req_pkt->flags & ~(ISCSI_LOGIN_REQ_FLAGS_CONTINUE | ISCSI_LOGIN_REQ_FLAGS_TRANSIT) != 0) || (login_req_pkt->reserved != 0) || (login_req_pkt->reserved2[0] != 0) || (login_req_pkt->reserved2[1] != 0) ) return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Current Stage (CSG) is set to reserved and Next Stage (NSG) is reserved and T bit is set, if C bit is set, T MUST be cleared and reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data return iscsi_validate_key_value_pairs( ((const uint8_t *) login_req_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len ); @@ -1469,7 +1736,7 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint if ( (ISCSI_VERSION_MIN < login_response_pkt->version_max) || (ISCSI_VERSION_MAX > login_response_pkt->version_max) || (ISCSI_VERSION_MIN < login_response_pkt->version_active) || (ISCSI_VERSION_MAX > login_response_pkt->version_active) ) return ISCSI_VALIDATE_PACKET_RESULT_ERROR_UNSUPPORTED_VERSION; - if ( (ISCSI_LOGIN_RESPONSES_FLAGS_GET_CURRENT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_RESERVED) || ((ISCSI_LOGIN_RESPONSES_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_RESERVED) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT) != 0)) || ((login_response_pkt->flags < 0) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE) != 0)) || (login_response_pkt->flags & ~(ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE | ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT) != 0) || (login_response_pkt->reserved != 0) || (login_response_pkt->reserved2 != 0) || (login_response_pkt->reserved3 != 0) ) + if ( (ISCSI_LOGIN_RESPONSE_FLAGS_GET_CURRENT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_RESERVED) || ((ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_RESERVED) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0)) || ((login_response_pkt->flags < 0) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE) != 0)) || (login_response_pkt->flags & ~(ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE | ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0) || (login_response_pkt->reserved != 0) || (login_response_pkt->reserved2 != 0) || (login_response_pkt->reserved3 != 0) ) return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Current Stage (CSG) is set to reserved and Next Stage (NSG) is reserved and T bit is set, if C bit is set, T MUST be cleared and reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data return iscsi_validate_key_value_pairs( ((const uint8_t *) login_response_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len ); @@ -1887,8 +2154,8 @@ static int iscsi_get_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_ * and its string value. * * @param[in] key_value_pairs Pointer to the hash map which should - * contain the added string key and value pair. NULL is NOT - * allowed here, so be careful. + * contain the added string key and value pair. + * NULL is NOT allowed here, so be careful. * @param[in] key String containing the key name as string. May * NOT be NULL, so take caution. * @param[in] value String containing the value to be stored. @@ -1901,7 +2168,7 @@ static int iscsi_add_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_ uint8_t *hash_key = iscsi_hashmap_key_create( key, key_len ); if ( hash_key == NULL ) { - logadd( LOG_ERROR, "iscsi_add_key_value_pair: Out of memory allocating key." ); + logadd( LOG_ERROR, "iscsi_add_key_value_pair: Out of memory allocating key" ); return -1L; } @@ -1910,7 +2177,9 @@ static int iscsi_add_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_ uint8_t *hash_val = (uint8_t *) malloc( val_len ); if ( hash_val == NULL ) { - logadd( LOG_ERROR, "iscsi_add_key_value_pair: Out of memory allocating string value." ); + logadd( LOG_ERROR, "iscsi_add_key_value_pair: Out of memory allocating string value" ); + + iscsi_hashmap_key_destroy( hash_key ); return -1L; } @@ -1921,6 +2190,50 @@ static int iscsi_add_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_ } /** + * @brief Allocates and updates a string value of a key / value hash map pair. + * + * This function allocates memory for a string key + * and its string value.\n + * If the key does not exist, it will be added as + * a new one. + * + * @param[in] key_value_pairs Pointer to the hash map which should + * contain the updated string key and value pair. + * NULL is NOT allowed here, so be careful. + * @param[in] key String containing the key name as string. May + * NOT be NULL, so take caution. + * @param[in] value String containing the value to be stored. + * @return 0 on successful operation, or a negative value on + * error (memory exhaustion). + */ +static int iscsi_update_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const uint8_t *value) +{ + const uint key_len = (uint) strlen( (char *) key ) + 1; + uint8_t *hash_key = iscsi_hashmap_key_create( key, key_len ); + + if ( hash_key == NULL ) { + logadd( LOG_ERROR, "iscsi_update_key_value_pair: Out of memory allocating key" ); + + return -1L; + } + + const uint val_len = (uint) strlen( (char *) value ) + 1; + uint8_t *hash_val = (uint8_t *) malloc( val_len ); + + if ( hash_val == NULL ) { + logadd( LOG_ERROR, "iscsi_update_key_value_pair: Out of memory allocating string value" ); + + iscsi_hashmap_key_destroy( hash_key ); + + return -1L; + } + + memcpy( hash_val, value, val_len ); + + return iscsi_hashmap_put_free( key_value_pairs, hash_key, key_len, hash_val, iscsi_hashmap_key_destroy_value_callback, NULL ); +} + +/** * @brief Extracts an integer value from a key and value pair. * * This function converts a string representation of a @@ -1953,8 +2266,8 @@ static int iscsi_get_int_key_value_pair(iscsi_hashmap *key_value_pairs, const ui * and its integer representation as string value. * * @param[in] key_value_pairs Pointer to the hash map which should - * contain the added integer key and value pair. NULL is NOT - * allowed here, so be careful. + * contain the added integer key and value pair. + * NULL is NOT allowed here, so be careful. * @param[in] key String containing the key name as string. May * NOT be NULL, so take caution. * @param[in] value Integer containing the value to be stored. @@ -1975,6 +2288,36 @@ static int iscsi_add_int_key_value_pair(iscsi_hashmap *key_value_pairs, const ui } /** + * @brief Allocates and updates an integer value of a key / value hash map pair. + * + * This function allocates memory for a string key + * and its integer representation as string value.\n + * If the key does not exist, it will be added as + * a new one. + * + * @param[in] key_value_pairs Pointer to the hash map which should + * contain the updated integer key and value pair. + * NULL is NOT allowed here, so be careful. + * @param[in] key String containing the key name as string. May + * NOT be NULL, so take caution. + * @param[in] value Integer containing the value to be stored. + * @return 0 on successful operation, or a negative value on + * error (memory exhaustion). + */ +static int iscsi_update_int_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int value) +{ + const uint8_t *hash_val = iscsi_sprintf_alloc( "%ld", value ); + + if ( hash_val == NULL ) { + logadd( LOG_ERROR, "iscsi_update_int_key_value_pair: Out of memory allocating integer value." ); + + return -1L; + } + + return iscsi_update_key_value_pair( key_value_pairs, key, hash_val ); +} + +/** * @brief Extracts a boolean value from a key and value pair. * * This function converts a string representation of a @@ -2009,8 +2352,8 @@ static int iscsi_get_bool_key_value_pair(iscsi_hashmap *key_value_pairs, const u * The string representation for false is: No * * @param[in] key_value_pairs Pointer to the hash map which should - * contain the added boolean key and value pair. NULL is NOT - * allowed here, so be careful. + * contain the added boolean key and value pair. + * NULL is NOT allowed here, so be careful. * @param[in] key String containing the key name as string. May * NOT be NULL, so take caution. * @param[in] value Boolean containing the value to be stored @@ -2026,6 +2369,33 @@ static int iscsi_add_bool_key_value_pair(iscsi_hashmap *key_value_pairs, const u } /** + * @brief Allocates and updates an boolean value of a key / value hash map pair. + * + * This function allocates memory for a string key + * and its integer value.\n + * The string representation for true is: Yes\n + * The string representation for false is: No\n + * If the key does not exist, it will be added as + * a new one. + * + * @param[in] key_value_pairs Pointer to the hash map which should + * contain the updated boolean key and value pair. + * NULL is NOT allowed here, so be careful. + * @param[in] key String containing the key name as string. May + * NOT be NULL, so take caution. + * @param[in] value Boolean containing the value to be stored + * as string. + * @return 0 on successful operation, or a negative value on + * error (memory exhaustion). + */ +static int iscsi_update_bool_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int value) +{ + const uint8_t *hash_val = (uint8_t *) ((value != 0) ? "Yes" : "No"); + + return iscsi_update_key_value_pair( key_value_pairs, key, hash_val ); +} + +/** * @brief Creates and initializes an iSCSI portal group. * * Specified tag and flags are used for portal group @@ -2586,7 +2956,7 @@ iscsi_session *iscsi_session_create(iscsi_connection *conn, iscsi_target_node *t session->type = type; session->current_text_init_task_tag = 0xFFFFFFFFUL; - session->key_value_pairs = iscsi_hashmap_create( 0UL ); + session->key_value_pairs = iscsi_hashmap_create( 32UL ); if ( session->key_value_pairs == NULL ) { logadd( LOG_ERROR, "iscsi_session_create: Out of memory allocating iSCSI session key and value pairs hash map" ); @@ -2598,17 +2968,18 @@ iscsi_session *iscsi_session_create(iscsi_connection *conn, iscsi_target_node *t return NULL; } - int rc = iscsi_add_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS, session->max_conns ); - rc |= iscsi_add_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_OUTSTANDING_R2T, session->max_outstanding_r2t ); - rc |= iscsi_add_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_WAIT, session->default_time_to_wait ); - rc |= iscsi_add_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_RETAIN, session->default_time_to_retain ); - rc |= iscsi_add_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_FIRST_BURST_LEN, session->first_burst_len ); - rc |= iscsi_add_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_BURST_LEN, session->max_burst_len ); - rc |= iscsi_add_bool_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IMMEDIATE_DATA, session->immediate_data ); - rc |= iscsi_add_bool_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_PDU_IN_ORDER, session->data_pdu_in_order ); - rc |= iscsi_add_bool_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_SEQ_IN_ORDER, session->data_seq_in_order ); - rc |= iscsi_add_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_ERR_RECOVERY_LEVEL, session->err_recovery_level ); - rc |= iscsi_add_int_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_RECV_DS_LEN, conn->max_recv_ds_len ); + int rc = iscsi_session_init_key_value_pairs( session->key_value_pairs ); + rc |= iscsi_update_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS, session->max_conns ); + rc |= iscsi_update_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_OUTSTANDING_R2T, session->max_outstanding_r2t ); + rc |= iscsi_update_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_WAIT, session->default_time_to_wait ); + rc |= iscsi_update_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_RETAIN, session->default_time_to_retain ); + rc |= iscsi_update_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_FIRST_BURST_LEN, session->first_burst_len ); + rc |= iscsi_update_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_BURST_LEN, session->max_burst_len ); + rc |= iscsi_update_bool_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IMMEDIATE_DATA, session->immediate_data ); + rc |= iscsi_update_bool_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_PDU_IN_ORDER, session->data_pdu_in_order ); + rc |= iscsi_update_bool_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_SEQ_IN_ORDER, session->data_seq_in_order ); + rc |= iscsi_update_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_ERR_RECOVERY_LEVEL, session->err_recovery_level ); + rc |= iscsi_update_int_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_RECV_DS_LEN, conn->max_recv_ds_len ); if ( rc != 0 ) { logadd( LOG_ERROR, "iscsi_session_create: Out of memory adding iSCSI session key and integer value pair" ); @@ -2666,6 +3037,50 @@ void iscsi_session_destroy(iscsi_session *session) } /** + * @brief Initializes a key and value pair hash table with default values. + * + * This function is used by iSCSI connections and + * sessions with default values for required keys.\n + * The iSCSI global key and value pair allowed + * values and ranges for fast + * access + * + * @param[in] key_value_pairs Pointer to key and value pair hash map + * which should store all the default values for + * its keys and may NOT be NULL, so take caution. + * @param[in] lut Lookup table to use for initialization. + * NULL is not allowed here, so be careful. + * @return 0 on success, a negative error code otherwise. + */ +static int iscsi_init_key_value_pairs(iscsi_hashmap *key_value_pairs, const iscsi_key_value_pair_lut_entry *lut) +{ + for ( uint i = 0; lut[i].key != NULL; i++ ) { + const int rc = iscsi_add_key_value_pair( key_value_pairs, lut[i].key, lut[i].value ); + + if ( rc < 0 ) + return rc; + } + + return 0L; +} + +/** + * @brief Initializes a key and value pair hash table with default values for an iSCSI session. + * + * This function only initializes the default key + * and value pairs used by iSCSI sessions. + * + * @param[in] key_value_pairs Pointer to key and value pair hash map + * which should store all the default values for + * its keys and may NOT be NULL, so take caution. + * @return 0 on success, a negative error code otherwise. + */ +int iscsi_session_init_key_value_pairs(iscsi_hashmap *key_value_pairs) +{ + return iscsi_init_key_value_pairs( key_value_pairs, &iscsi_session_key_value_pair_lut[0] ); +} + +/** * @brief Creates data structure for an iSCSI connection from iSCSI portal and TCP/IP socket. * * Creates a data structure for incoming iSCSI connection @@ -2698,6 +3113,16 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock) return NULL; } + const int rc = iscsi_connection_init_key_value_pairs( conn->key_value_pairs ); + + if ( rc < 0 ) { + iscsi_hashmap_iterate( conn->key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL ); + iscsi_hashmap_destroy( conn->key_value_pairs ); + free( conn ); + + return NULL; + } + conn->partial_pairs = NULL; conn->device = NULL; conn->init_port = NULL; @@ -2716,6 +3141,7 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock) conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_READY; conn->flags = 0L; conn->state = ISCSI_CONNECT_STATE_INVALID; + conn->login_phase = 0L; conn->max_recv_ds_len = ISCSI_DEFAULT_RECV_DS_LEN; conn->pg_tag = portal->group->tag; conn->isid.a = 0; @@ -2897,6 +3323,45 @@ int iscsi_connection_write(const iscsi_connection *conn, uint8_t *buf, const uin } /** + * @brief Initializes a key and value pair hash table with default values for an iSCSI connection. + * + * This function only initializes the default key + * and value pairs used by iSCSI connectionss. + * + * @param[in] key_value_pairs Pointer to key and value pair hash map + * which should store all the default values for + * its keys and may NOT be NULL, so take caution. + * @return 0 on success, a negative error code otherwise. + */ +int iscsi_connection_init_key_value_pairs(iscsi_hashmap *key_value_pairs) +{ + return iscsi_init_key_value_pairs( key_value_pairs, &iscsi_connection_key_value_pair_lut[0] ); +} + +/** + * @brief Negotiates all key and value pairs required for session authentication. + * + * @param[in] conn Pointer to iSCSI connection to be + * negotiated, may NOT be NULL, so be careful. + * @param[in] key_value_pairs Pointer to key and value pair hash map + * which contains the negotiation pairs. NULL + * is prohibited, so take caution. + * @param[in] buf Pointer to output buffer which may NOT + * be NULL, so be careful. + * @param[in] buf_len Number of bytes already written. + * @param[in] len Length of DataSegement in bytes. + * @return New buffer length in bytes if all keys + * could be negotiated, a negative error + * code otherwise. + */ +int iscsi_negotiate_key_value_pairs(iscsi_connection *conn, iscsi_hashmap *key_value_pairs, uint8_t *buf, const uint buf_len, const uint len) +{ + // TODO: Implement function. + + return 0L; +} + +/** * @brief Copies retrieved key and value pairs into SCSI connection and session structures. * * This function converts string representations of @@ -2984,6 +3449,36 @@ int iscsi_connection_copy_key_value_pairs(iscsi_connection *conn) } /** + * @brief Handles stages of CHAP and other authentication methods. + * + * This function handles the various stages of the + * various authentication methods supported by the + * iSCSI protocol.\n + * Currently, only CHAP is implemented. + * + * @param[in] conn Pointer to iSCSI connection which may NOT + * be NULL, so be careful. + * @param[in] key_value_pairs Pointer to hash map containing the + * authentication key and value pairs. NULL + * is NOT allowed here, so take caution. + * @param[in] auth_method Pointer to string containing the + * authentication method. NULL is forbidden, + * so be careful. + * @param[in] buf Pointer to DataSegment buffer. May NOT be + * NULL, so take caution. + * @param[in] buf_len Remaining number of bytes to read. + * @param[in] len Total number of bytes of DataSegment buffer. + * @return 0 if authentication methods were handled successfully, + * a negative error code otherwise. + */ +static int iscsi_connection_auth_key_value_pairs(iscsi_connection *conn, iscsi_hashmap *key_value_pairs, const uint8_t *auth_method, uint8_t *buf, const uint buf_len, const uint len) +{ + // TODO: Implement CHAP and other authentication methods. + + return 0L; +} + +/** * @brief Checks buffer sizes of an iSCSI connection with it's associated session for consistency. * * This function ensures that, for example, first @@ -3000,9 +3495,9 @@ int iscsi_connection_copy_key_value_pairs(iscsi_connection *conn) static int iscsi_connection_check_key_value_pairs(iscsi_connection *conn) { if ( (conn->session->first_burst_len > conn->session->max_burst_len) || (conn->session->first_burst_len < 512) || (conn->session->max_burst_len < 512) || (conn->session->max_burst_len > ISCSI_SESSION_DEFAULT_MAX_BURST_LEN) || (conn->max_recv_ds_len < 512) || (conn->max_recv_ds_len > ISCSI_SESSION_DEFAULT_MAX_BURST_LEN) ) - return -1; + return -1L; - return 0; + return 0L; } /** @@ -3090,7 +3585,7 @@ static void iscsi_connection_pdu_login_response(iscsi_connection *conn, iscsi_pd } if ( login_response_pkt->status_class != ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SUCCESS ) - login_response_pkt->flags &= (int8_t) ~(ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT | ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_MASK | ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_MASK ); + login_response_pkt->flags &= (int8_t) ~(ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT | ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_MASK | ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_MASK ); iscsi_hashmap_iterate( key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL ); iscsi_hashmap_destroy( key_value_pairs ); @@ -3174,9 +3669,9 @@ static int iscsi_login_response_init(iscsi_pdu *login_response_pdu, const iscsi_ login_response_pdu->ds_cmd_data = (iscsi_ds_cmd_data *) (((uint8_t *) login_response_pkt) + sizeof(struct iscsi_bhs_packet) + pdu->header_digest_size); login_response_pdu->ds_len = ISCSI_DEFAULT_RECV_DS_LEN; - login_response_pkt->flags |= (int8_t) (login_req_pkt->flags & (ISCSI_LOGIN_REQ_FLAGS_TRANSMIT | ISCSI_LOGIN_REQ_FLAGS_CONTINUE | ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_MASK)); + login_response_pkt->flags |= (int8_t) (login_req_pkt->flags & (ISCSI_LOGIN_REQ_FLAGS_TRANSIT | ISCSI_LOGIN_REQ_FLAGS_CONTINUE | ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_MASK)); - if ( (login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT) != 0 ) + if ( (login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0 ) login_response_pkt->flags |= (login_req_pkt->flags & ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_MASK); login_response_pkt->isid.a = login_req_pkt->isid.a; @@ -3190,7 +3685,7 @@ static int iscsi_login_response_init(iscsi_pdu *login_response_pdu, const iscsi_ if ( login_response_pkt->tsih != 0 ) login_response_pkt->stat_sn = login_req_pkt->exp_stat_sn; // Copying over doesn't change endianess.' - if ( ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT) != 0) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE) != 0) ) { + if ( ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE) != 0) ) { login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; @@ -3200,8 +3695,8 @@ static int iscsi_login_response_init(iscsi_pdu *login_response_pdu, const iscsi_ login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_WRONG_VERSION; return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - } else if ( (ISCSI_LOGIN_RESPONSES_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_RESERVED) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT) != 0) ) { - login_response_pkt->flags &= (int8_t) ~(ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_MASK | ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT | ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_MASK); + } else if ( (ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_RESERVED) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0) ) { + login_response_pkt->flags &= (int8_t) ~(ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_MASK | ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT | ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_MASK); login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; @@ -3424,7 +3919,7 @@ static int iscsi_connection_login_check_target(iscsi_connection *conn, iscsi_pdu uint8_t *redirect_adr = iscsi_target_node_get_redirect( conn, *target ); if ( redirect_adr != NULL ) { - if ( iscsi_add_key_value_pair( login_response_pdu->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, redirect_adr ) == 0 ) { + if ( iscsi_update_key_value_pair( login_response_pdu->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, redirect_adr ) == 0 ) { login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_REDIRECT; login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_REDIRECT_TEMP; @@ -4125,50 +4620,6 @@ static int iscsi_connection_pdu_data_handle_scsi_cmd(iscsi_connection *conn, isc } /** - * @brief Allocates and replaces a string value to a key / value hash map pair. - * - * This function allocates memory for a string key - * and its string value and replaces a original - * key and value pair if it exists. - * - * @param[in] conn Pointer to iSCSI connection to update the - * login key and value pair for and may NOT - * be NULL, so take care. - * @param[in] key_value_pairs Pointer to the hash map which should - * contain the replaced string key and value pair. NULL is NOT - * allowed here, so be careful. - * @param[in] key String containing the key name as string. May - * NOT be NULL, so take caution. - * @param[in] value String containing the value to be stored. - * @return 0 on successful operation, or a negative value on - * error (memory exhaustion). - */ -static int iscsi_connection_login_update_key_value_pair(iscsi_connection *conn, const uint8_t *key, const uint8_t *value) -{ - const uint key_len = (uint) strlen( (char *) key ) + 1; - uint8_t *hash_key = iscsi_hashmap_key_create( key, key_len ); - - if ( hash_key == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_login_update_key_value_pair: Out of memory allocating key." ); - - return -1L; - } - - const uint val_len = (uint) strlen( (char *) value ) + 1; - uint8_t *hash_val = (uint8_t *) malloc( val_len ); - - if ( hash_val == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_login_update_key_value_pair: Out of memory allocating string value." ); - - return -1L; - } - - memcpy( hash_val, value, val_len ); - - return iscsi_hashmap_put_free( conn->key_value_pairs, hash_key, key_len, hash_val, iscsi_hashmap_key_destroy_value_callback, NULL ); -} - -/** * @brief Negotiates connection authentication method (none or CHAP). * * This function updates the key and value pairs if, and only if @@ -4180,12 +4631,12 @@ static int iscsi_connection_login_update_key_value_pair(iscsi_connection *conn, */ static int iscsi_connection_chap_negotiate(iscsi_connection *conn) { - int rc = 0; + int rc = 0L; if ( (conn->flags & ISCSI_CONNECT_FLAGS_CHAP_DISABLE) != 0 ) - rc = iscsi_connection_login_update_key_value_pair( conn, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD, (uint8_t *) "None" ); + rc = iscsi_update_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD, (uint8_t *) "None" ); else if ( (conn->flags & ISCSI_CONNECT_FLAGS_CHAP_REQUIRE) != 0 ) - rc = iscsi_connection_login_update_key_value_pair( conn, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD, (uint8_t *) "CHAP" ); + rc = iscsi_update_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD, (uint8_t *) "CHAP" ); return rc; } @@ -4267,19 +4718,15 @@ static int iscsi_connection_login_chap_negotiate(iscsi_connection *conn, const i */ static int iscsi_connection_login_digest_negotiate(iscsi_connection *conn, const iscsi_target_node *target) { - if ( target->header_digest != 0 ) { - const int rc = iscsi_connection_login_update_key_value_pair( conn, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_HEADER_DIGEST, (uint8_t *) "CRC32C" ); - - return rc; - } + int rc = 0L; - if ( target->data_digest != 0 ) { - const int rc = iscsi_connection_login_update_key_value_pair( conn, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST, (uint8_t *) "CRC32C" ); + if ( target->header_digest != 0 ) + rc = iscsi_update_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_HEADER_DIGEST, (uint8_t *) "CRC32C" ); - return rc; - } + if ( target->data_digest != 0 ) + rc = iscsi_update_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST, (uint8_t *) "CRC32C" ); - return 0L; + return rc; } /** @@ -4434,7 +4881,7 @@ static int iscsi_connection_login_set_target_info(iscsi_connection *conn, iscsi_ int rc; if ( target != NULL ) { - rc = iscsi_add_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS, ((target->alias != NULL) ? target->alias : (uint8_t *) "") ); + rc = iscsi_update_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS, ((target->alias != NULL) ? target->alias : (uint8_t *) "") ); if ( rc < 0 ) return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER; @@ -4445,14 +4892,14 @@ static int iscsi_connection_login_set_target_info(iscsi_connection *conn, iscsi_ if ( tmp_buf == NULL ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - rc = iscsi_add_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, tmp_buf ); + rc = iscsi_update_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, tmp_buf ); free( tmp_buf ); if ( rc < 0 ) return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER; - rc = iscsi_add_int_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG, conn->pg_tag ); + rc = iscsi_update_int_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG, conn->pg_tag ); if ( rc < 0 ) return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER; @@ -4461,7 +4908,7 @@ static int iscsi_connection_login_set_target_info(iscsi_connection *conn, iscsi_ rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS, &tmp_buf ); if ( (rc == 0) && (strlen( (char *) tmp_buf ) != 0) ) { - rc = iscsi_add_key_value_pair( login_response_pdu->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS, tmp_buf ); + rc = iscsi_update_key_value_pair( login_response_pdu->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS, tmp_buf ); if ( rc < 0 ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; @@ -4471,7 +4918,7 @@ static int iscsi_connection_login_set_target_info(iscsi_connection *conn, iscsi_ rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, &tmp_buf ); if ( (rc == 0) && (strlen( (char *) tmp_buf ) != 0) ) { - rc = iscsi_add_key_value_pair( login_response_pdu->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, tmp_buf ); + rc = iscsi_update_key_value_pair( login_response_pdu->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, tmp_buf ); if ( rc < 0 ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; @@ -4481,7 +4928,7 @@ static int iscsi_connection_login_set_target_info(iscsi_connection *conn, iscsi_ rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG, &tmp_buf ); if ( rc == 0 ) { - rc = iscsi_add_key_value_pair( login_response_pdu->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG, tmp_buf ); + rc = iscsi_update_key_value_pair( login_response_pdu->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG, tmp_buf ); if ( rc < 0 ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; @@ -4560,6 +5007,185 @@ static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscs } /** + * @brief Handles the Current Stage (CSG) bit of login response. + * + * This function determines the authentication method + * and processes the various authentication stages. + * + * @param[in] conn Pointer to iSCSI connection, may NOT be + * NULL, so take caution. + * @param[in] login_response_pdu Pointer to login response PDU. + * NULL is NOT an allowed value here, so be careful. + * @param[in] key_value_pairs Pointer to key and value pairs to + * retrieve authentication details from. This + * is NOT allowed to be NULL. Be careful! + * @return 0 if authentication has been handled successfully, + * a negative error code otherwise. + */ +static int iscsi_connecction_handle_login_response_csg_bit(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_hashmap *key_value_pairs) +{ + iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; + + switch ( ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) ) { + case ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_SECURITY_NEGOTIATION : { + uint8_t *auth_method; + const int rc = iscsi_get_key_value_pair( key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD, (uint8_t **) &auth_method ); + + if ( rc < 0 ) { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISSING_PARAMETER; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } + + if ( strcasecmp( (char *) auth_method, "None" ) == 0 ) { + conn->flags |= ISCSI_CONNECT_FLAGS_AUTH; + } else { + const int ds_len = iscsi_connection_auth_key_value_pairs( conn, key_value_pairs, auth_method, (uint8_t *) login_response_pdu->ds_cmd_data, login_response_pdu->buf_len, login_response_pdu->ds_len ); + + if ( ds_len < 0 ) { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_ERR; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } + + login_response_pdu->ds_len = ds_len; + + if ( (conn->flags & ISCSI_CONNECT_FLAGS_AUTH) == 0 ) + login_response_pkt->flags &= (int8_t) ~ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT; + } + + break; + } + case ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION : { + if ( conn->state == ISCSI_CONNECT_STATE_INVALID ) { + if ( conn->flags & ISCSI_CONNECT_FLAGS_CHAP_REQUIRE ) { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_ERR; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } else { + conn->flags |= ISCSI_CONNECT_FLAGS_AUTH; + } + } + + if ( (conn->flags & ISCSI_CONNECT_FLAGS_AUTH) == 0 ) { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_ERR; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } + + break; + } + case ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FULL_FEATURE_PHASE : { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + + break; + } + default : { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + + break; + } + } + + return ISCSI_CONNECT_PDU_READ_OK; +} + +/** + * + * @brief Checks whether the session type is valid. + * + * This function also can be used to output + * debugging info about the session, which + * is currently not implemented. + * + * @param[in] conn Pointer to iSCSI connection to be checked for + * validity. May NOT be NULL, so be careful. + * @param[in] login_response_pdu Pointer to login response PDU to + * set status class and detail in case of an error. + * NULL is not an allowed value here, take caution. + * @return 0 if the session type is valid, a negative error code + * otherwise. + */ +static int iscsi_connection_login_session_info_notify(iscsi_connection *conn, iscsi_pdu *login_response_pdu) +{ + if ( (conn->session->type != ISCSI_SESSION_TYPE_NORMAL) && (conn->session->type != ISCSI_SESSION_TYPE_DISCOVERY) ) { + iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; + + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } + + return ISCSI_CONNECT_PDU_READ_OK; +} + +/** + * @brief Handles the Transit (T) bit of login response. + * + * This function handles the transitional stages + * of the authentication process. + * + * @param[in] conn Pointer to iSCSI connection, may NOT be + * NULL, so take caution. + * @param[in] login_response_pdu Pointer to login response PDU. + * NULL is NOT an allowed value here, so be careful. + * @return 0 if transition has been handled successfully, + * a negative error code otherwise. + */ +static int iscsi_connecction_handle_login_response_t_bit(iscsi_connection *conn, iscsi_pdu *login_response_pdu) +{ + iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; + + switch ( ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) ) { + case ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION : { + conn->login_phase = ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION; + + break; + } + case ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION : { + conn->login_phase = ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION; + + break; + } + case ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FULL_FEATURE_PHASE : { + conn->login_phase = ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FULL_FEATURE_PHASE; + + iscsi_put_be16( (uint8_t *) &login_response_pkt->tsih, conn->session->tsih ); + + const int rc = iscsi_connection_login_session_info_notify( conn, login_response_pdu ); + + if ( rc < 0 ) + return rc; + + conn->flags |= ISCSI_CONNECT_FLAGS_FULL_FEATURE; + + break; + } + default : { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + + break; + } + } + + return ISCSI_CONNECT_PDU_READ_OK; +} + +/** * @brief Handles iSCSI connection login response. * * This function negotiates the login parameters @@ -4575,9 +5201,27 @@ static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscs */ static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_hashmap *key_value_pairs) { - // TODO: Implement function. + iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; + const int ds_len = iscsi_negotiate_key_value_pairs( conn, conn->key_value_pairs, (uint8_t *) login_response_pdu->ds_cmd_data, login_response_pdu->buf_len, login_response_pdu->ds_len ); - return 0L; + if ( ds_len < 0 ) { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_ERR; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } + + login_response_pdu->ds_len = (uint) ds_len; + + int rc = iscsi_connecction_handle_login_response_csg_bit( conn, login_response_pdu, key_value_pairs ); + + if ( rc < 0 ) + return rc; + + if ( (login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0) + rc = iscsi_connecction_handle_login_response_t_bit( conn, login_response_pdu ); + + return rc; } /** diff --git a/src/server/iscsi.h b/src/server/iscsi.h index 56e09f9..64d678d 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -4317,7 +4317,7 @@ typedef struct __attribute__((packed)) iscsi_isid { * this also indicates that the initiator is ready for the Login * Final-Response. */ -#define ISCSI_LOGIN_REQ_FLAGS_TRANSMIT (1 << 7) +#define ISCSI_LOGIN_REQ_FLAGS_TRANSIT (1 << 7) /** @@ -4487,7 +4487,7 @@ typedef struct __attribute__((packed)) iscsi_login_req_packet { #define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_MASK ((1 << (ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FIRST_BIT)) | (1 << (ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECOND_BIT))) /// Login response flags: Extracts the Next Stage (NSG) bits. -#define ISCSI_LOGIN_RESPONSES_FLAGS_GET_NEXT_STAGE(x) (((x) & ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_MASK) >> ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FIRST_BIT) +#define ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(x) (((x) & ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_MASK) >> ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FIRST_BIT) @@ -4527,7 +4527,7 @@ typedef struct __attribute__((packed)) iscsi_login_req_packet { #define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_MASK ((1 << (ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FIRST_BIT)) | (1 << (ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_SECOND_BIT))) /// Login request flags: Extracts the Current Stage (CSG) bits. -#define ISCSI_LOGIN_RESPONSES_FLAGS_GET_CURRENT_STAGE(x) (((x) & ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_MASK) >> ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FIRST_BIT) +#define ISCSI_LOGIN_RESPONSE_FLAGS_GET_CURRENT_STAGE(x) (((x) & ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_MASK) >> ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FIRST_BIT) /** @@ -4554,7 +4554,7 @@ typedef struct __attribute__((packed)) iscsi_login_req_packet { * If the Status-Class is 0, the T bit MUST NOT be set to 1 if the T bit * in the request was set to 0. */ -#define ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT (1 << 7) +#define ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT (1 << 7) /** @@ -5688,6 +5688,7 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint /// Maximum length of value for a normal key. #define ISCSI_TEXT_VALUE_MAX_LEN 8192UL + /// iSCSI text key=value pair type: Invalid. #define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_INVALID -1L @@ -5717,6 +5718,27 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint /** + * @brief iSCSI connection and session lookup table entry, used for allowed key values and determining key type. + * + * This structure is shared by the iSCSI session + * and the iSCSI connection lookup table. + */ +typedef struct iscsi_key_value_pair_lut_entry { + /// Name of key. + uint8_t *key; + + /// Default value of the key, always in string representation. + uint8_t *value; + + /// If the key has a list of allowed strings: Comma separated list of allowed string values. If key contains numeric values: Comma separated minimum and maximum integer range. + uint8_t *list_range; + + /// Type of key and value pair. + const int type; +} iscsi_key_value_pair_lut_entry; + + +/** * @brief iSCSI Text / Login extracted key=value pair. * * This structure is used for accessing key and value @@ -5724,14 +5746,17 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint * Text or Login packet data. */ typedef struct iscsi_key_value_pair { + /// Value of the key which is stored in the hash map. + uint8_t *value; + + /// List of allowed values or if numeric, minimum and maximum allowed values. Always comma separated. + uint8_t *list_range; + /// Type of key and value pair. - int type; + int type; /// State index. int state_index; - - /// Value of the key which is stored in the hash map. - uint8_t *value; } iscsi_key_value_pair; /** @@ -5788,6 +5813,9 @@ typedef struct iscsi_globals { /// Hash map containing connections not associated with an iSCSI sessions. iscsi_hashmap *connections; + /// Hash map containing key and value pair types and allowed values or ranges. + iscsi_hashmap *key_value_pairs; + /// Global flags. int flags; @@ -5799,10 +5827,14 @@ typedef struct iscsi_globals { } iscsi_globals; -/// iSCSI vector for global access. +/// iSCSI global vector. MUST be initialized with iscsi_create before any iSCSI functions are used. iscsi_globals *iscsi_globvec = NULL; +int iscsi_create(); // Allocates and initializes the iSCSI global vector structure +int iscsi_global_key_value_pair_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // iSCSI global key and value pair destructor callback for hash map +void iscsi_destroy(); // Deallocates all resources acquired by iscsi_create + /** * @brief iSCSI portal group: Private portal group if set, public otherwise. * @@ -6314,6 +6346,9 @@ typedef struct iscsi_connection { /// iSCSI connection state. int state; + /// iSCSI connection login phase. + int login_phase; + /// Maximum receive DataSegment length in bytes. uint max_recv_ds_len; @@ -6406,7 +6441,7 @@ typedef struct iscsi_pdu { /// Remaining bytes of DataSegment buffer to read. uint buf_len; - /// Associated connection. + /// Associated iSCSI connection. iscsi_connection *conn; /// CmdSN. @@ -6423,6 +6458,8 @@ int iscsi_target_node_access(iscsi_connection *conn, iscsi_target_node *target, iscsi_session *iscsi_session_create(iscsi_connection *conn, iscsi_target_node *target, const int type); // Creates and initializes an iSCSI session void iscsi_session_destroy(iscsi_session *session); // Deallocates all resources acquired by iscsi_session_create +int iscsi_session_init_key_value_pairs(iscsi_hashmap *key_value_pairs); // Initializes a key and value pair hash table with default values + iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock); // Creates data structure for an iSCSI connection from iSCSI portal and TCP/IP socket int iscsi_connection_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // iSCSI connection destructor callback for hash map void iscsi_connection_destroy(iscsi_connection *conn); // Deallocates all resources acquired by iscsi_connection_create @@ -6433,6 +6470,8 @@ void iscsi_connection_schedule(iscsi_connection *conn); // Schedules an iSCSI co int iscsi_connection_read(const iscsi_connection *conn, uint8_t *buf, const uint len); // Reads data for the specified iSCSI connection from its TCP socket int iscsi_connection_write(const iscsi_connection *conn, uint8_t *buf, const uint len); // Writes data for the specified iSCSI connection to its TCP socket +int iscsi_connection_init_key_value_pairs(iscsi_hashmap *key_value_pairs); // Initializes a key and value pair hash table with default values for an iSCSI connection +int iscsi_negotiate_key_value_pairs(iscsi_connection *conn, iscsi_hashmap *key_value_pairs, uint8_t *buf, const uint buf_len, const uint len); // Negotiates all key and value pairs required for session authentication int iscsi_connection_copy_key_value_pairs(iscsi_connection *conn); // Copies retrieved key and value pairs into SCSI connection and session structures int iscsi_connection_save_incoming_key_value_pairs(iscsi_connection *conn, iscsi_hashmap *key_value_pairs, iscsi_pdu *login_response_pdu, const iscsi_pdu *pdu); // Saves incoming key / value pairs from the client of a login request PDU |
