diff options
Diffstat (limited to 'src/server/iscsi.c')
| -rw-r--r-- | src/server/iscsi.c | 1677 |
1 files changed, 381 insertions, 1296 deletions
diff --git a/src/server/iscsi.c b/src/server/iscsi.c index cc08e29..2b39955 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -72,11 +72,6 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task); static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task); - - - - - static void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task); // Allocates and initializes a SCSI task static void iscsi_scsi_task_xfer_complete(iscsi_scsi_task *scsi_task, iscsi_pdu *pdu); // Callback function when an iSCSI SCSI task completed the data transfer @@ -89,15 +84,9 @@ static int iscsi_scsi_lun_get_from_iscsi(const uint64_t lun); // Converts an iSC static void iscsi_scsi_lun_task_run( iscsi_scsi_task *scsi_task, iscsi_pdu *pdu); // Runs an iSCSI SCSI task for a specified iSCSI SCSI LUN -static int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, uint8_t *buf, dnbd3_image_t *image, const uint64_t offset_blocks, const uint64_t num_blocks, const uint32_t block_size); // Reads a number of blocks from a block offset of a DNBD3 image to a specified buffer - +static int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, dnbd3_image_t *image, const uint64_t offset_blocks, const uint64_t num_blocks); // Reads a number of blocks from a block offset of a DNBD3 image to a specified buffer - -static uint8_t *iscsi_vsprintf_append_realloc(char *buf, const char *format, va_list args); // Allocates and appends a buffer and sprintf's it -static uint8_t *iscsi_sprintf_append_realloc(char *buf, const char *format, ...); // Allocates and appends a buffer and sprintf's it -static uint8_t *iscsi_vsprintf_alloc(const char *format, va_list args); // Allocates a buffer and sprintf's it -static uint8_t *iscsi_sprintf_alloc(const char *format, ... ); // Allocates a buffer and sprintf's it static void iscsi_strcpy_pad(char *dst, const char *src, const size_t size, const int pad); // Copies a string with additional padding character to fill in a specified size @@ -107,129 +96,25 @@ static void iscsi_task_destroy(iscsi_task *task); // Deallocates resources acqui static void iscsi_task_response(iscsi_connection *conn, iscsi_task *task, iscsi_pdu *pdu); // Creates, initializes and sends an iSCSI task reponse PDU. static uint64_t iscsi_target_node_wwn_get(const uint8_t *name); // Calculates the WWN using 64-bit IEEE Extended NAA for a name -static dnbd3_image_t *iscsi_target_node_image_get(uint8_t *iqn); // Extracts the DNBD3 image out of an iSCSI IQN string and opens the DNBD3 image static iscsi_session *iscsi_session_create(iscsi_connection *conn, const int type); // Creates and initializes an iSCSI session static void iscsi_session_destroy(iscsi_session *session); // Deallocates all resources acquired by iscsi_session_create -static iscsi_connection *iscsi_connection_create(dnbd3_client_t *sock); // Creates data structure for an iSCSI connection from iSCSI portal and TCP/IP socket +static iscsi_connection *iscsi_connection_create(dnbd3_client_t *client); // Creates data structure for an iSCSI connection from iSCSI portal and TCP/IP socket static void iscsi_connection_destroy(iscsi_connection *conn); // Deallocates all resources acquired by iscsi_connection_create static int32_t iscsi_connection_read(const iscsi_connection *conn, uint8_t *buf, const uint32_t len); // Reads data for the specified iSCSI connection from its TCP socket -static int32_t iscsi_connection_write(const iscsi_connection *conn, uint8_t *buf, const uint32_t len); // Writes data for the specified iSCSI connection to its TCP socket static void iscsi_connection_login_response_reject(iscsi_pdu *login_response_pdu, const iscsi_pdu *pdu); // Initializes a rejecting login response packet -static iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint ahs_len, const int header_digest_size, const uint32_t ds_len, const int data_digest_size); +static iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint32_t ds_len); static void iscsi_connection_pdu_destroy(iscsi_pdu *pdu); // Destroys an iSCSI PDU structure used by connections -static iscsi_bhs_packet *iscsi_connection_pdu_append(iscsi_pdu *pdu, const uint ahs_len, const int header_digest_size, const uint32_t ds_len, const int data_digest_size); // Appends packet data to an iSCSI PDU structure used by connections - -static void iscsi_connection_pdu_digest_header_update(iscsi_header_digest *header_digest, const iscsi_bhs_packet *packet_data, const uint ahs_len); // Calculate and store iSCSI header digest (CRC32C) -static bool iscsi_connection_pdu_digest_header_verify(const iscsi_header_digest *header_digest, const iscsi_bhs_packet *packet_data, const uint ahs_len); // Validates a stored iSCSI header digest (CRC32C) with actual header data -static void iscsi_connection_pdu_digest_data_update(iscsi_data_digest *data_digest, const iscsi_scsi_ds_cmd_data *ds_cmd_data, const uint32_t ds_len); // Calculate iSCSI data digest (CRC32C) -static bool iscsi_connection_pdu_digest_data_verify(const iscsi_data_digest *data_digest, const iscsi_scsi_ds_cmd_data *ds_cmd_data, const uint32_t ds_len); // Validates a stored iSCSI data digest (CRC32C) with actual DataSegment +static iscsi_bhs_packet *iscsi_connection_pdu_resize(iscsi_pdu *pdu, const uint ahs_len, const uint32_t ds_len); // Appends packet data to an iSCSI PDU structure used by connections static bool iscsi_connection_pdu_write(iscsi_connection *conn, iscsi_pdu *pdu); - -/** - * @brief Allocates and appends a buffer and sprintf's it. - * - * Merges multiple strings using printf style formatting - * and allocates memory for holding the result. - * - * @param[in] buf Buffer to merge into. - * @param[in] format printf style format string. - * @param[in] args Values to fill in the format with. - * @return New buffer which holds the final result. - */ -static uint8_t *iscsi_vsprintf_append_realloc(char *buf, const char *format, va_list args) -{ - va_list args_copy; - uint orig_size = 0U; - - if ( buf != NULL ) - orig_size = (uint) strlen( (char *) buf ); - - va_copy( args_copy, args ); - uint new_size = vsnprintf( NULL, 0, format, args_copy ); - va_end( args_copy ); - - new_size += (uint) (orig_size + 1U); - - uint8_t *new_buf = realloc( buf, new_size ); - - if ( new_buf == NULL ) { - logadd( LOG_ERROR, "iscsi_vsprintf_append_realloc: Out of memory while allocating string buffer" ); - - return NULL; - } - - vsnprintf( (char *) (new_buf + orig_size), (new_size - orig_size), format, args ); - - return new_buf; -} - -/** - * @brief Allocates and appends a buffer and sprintf's it. - * - * Merges strings using printf style formatting and allocates - * memory for holding the result. - * - * @param[in] buf Buffer to merge into. - * @param[in] format printf style format string. - * @param[in] ... Values to fill in the format string with. - * @return New buffer which holds the final result. - */ -static uint8_t *iscsi_sprintf_append_realloc(char *buf, const char *format, ...) -{ - va_list args; - - va_start( args, format ); - uint8_t *ret_buf = iscsi_vsprintf_append_realloc( buf, format, args ); - va_end( args ); - - return ret_buf; -} - -/** - * @brief Allocates a buffer and sprintf's it. - * - * Merges strings using printf style formatting and allocates - * memory for holding the result. - * - * @param[in] format printf style format string. - * @param[in] args Values to fill in the format with. - * @return New buffer which holds the final result. - */ -static uint8_t *iscsi_vsprintf_alloc(const char *format, va_list args) -{ - return iscsi_vsprintf_append_realloc( NULL, format, args ); -} - -/** - * @brief Allocates a buffer and sprintf's it. - * - * Allocates a buffer large enough to hold printf style - * string concatenation and fills it using vspnrintf. - * - * @param[in] format Format string to allocate and fill. - * @param[in] ... Values to fill in the format string with. - * @return New buffer containing the formatted string. - */ -static uint8_t *iscsi_sprintf_alloc(const char *format, ... ) -{ - va_list args; - - va_start( args, format ); - uint8_t *buf = iscsi_vsprintf_alloc( format, args ); - va_end( args ); - - return buf; -} - /** * @brief Copies a string with additional padding character to fill in a specified size. * @@ -263,7 +148,7 @@ static void iscsi_copy_kvp_int(const char *name, int *dest, const char *src) const char *end = NULL; if ( *dest != -1 ) { - logadd( LOG_DEBUG1, "Received duplicate entry for key '%s', ignoring", name ); + logadd( LOG_DEBUG1, "Received duplicate entry for key '%s', ignoring (new: %s, old: %d)", name, src, *dest ); return; } @@ -271,7 +156,7 @@ static void iscsi_copy_kvp_int(const char *name, int *dest, const char *src) logadd( LOG_DEBUG1, "Empty value for numeric option '%s', ignoring", name ); return; } - res = strtoll( src, &end, 10 ); + res = strtoll( src, (char **)&end, 10 ); // WTF why is the second arg not const char ** if ( end == NULL ) { logadd( LOG_DEBUG1, "base 10 not valid! O.o" ); @@ -293,10 +178,10 @@ static void iscsi_copy_kvp_int(const char *name, int *dest, const char *src) static void iscsi_copy_kvp_str(const char *name, const char **dest, const char *src) { if ( *dest != NULL ) { - logadd( LOG_DEBUG1, "Received duplicate entry for key '%s', ignoring", name ); + logadd( LOG_DEBUG1, "Received duplicate entry for key '%s', ignoring (new: %s, old: %s)", name, src, *dest ); return; } - *dest = strdup( src ); + *dest = src; } /** @@ -315,12 +200,12 @@ static void iscsi_copy_kvp_str(const char *name, const char **dest, const char * * a negative value in case of an error. This can be used for * incrementing the offset to the next key / value pair. */ -static int iscsi_parse_text_key_value_pair(iscsi_login_kvp *key_value_pairs, const uint8_t *packet_data, const uint32_t len) +static int iscsi_parse_text_key_value_pair(iscsi_negotiation_kvp *key_value_pairs, const char *packet_data, const uint32_t len) { - uint key_val_len = (uint) strnlen( (char *) packet_data, len ); - const uint8_t *key_end = memchr( packet_data, '=', key_val_len ); + int key_val_len = (int) strnlen( packet_data, len ); + const char *key_end = memchr( packet_data, '=', key_val_len ); - if ( key_val_len == len ) { + if ( key_val_len == (int)len ) { logadd( LOG_DEBUG1, "iscsi_parse_text_key_value_pair: Final key/value pair not null-terminated, not spec compliant, aborting" ); return -1; } @@ -352,18 +237,26 @@ static int iscsi_parse_text_key_value_pair(iscsi_login_kvp *key_value_pairs, con } #define COPY_KVP(type, key) \ - else if ( strncmp( (const char *)packet_data, #key, key_len ) == 0 ) iscsi_copy_kvp_ ## type ( #key, &key_value_pairs->key, key_end + 1 ) + else if ( strncmp( packet_data, #key, key_len ) == 0 ) iscsi_copy_kvp_ ## type ( #key, &key_value_pairs->key, key_end + 1 ) if ( 0 ) {} COPY_KVP( int, MaxRecvDataSegmentLength ); COPY_KVP( int, MaxBurstLength ); COPY_KVP( int, FirstBurstLength ); + COPY_KVP( int, MaxConnections ); + COPY_KVP( int, ErrorRecoveryLevel ); + COPY_KVP( str, SessionType ); COPY_KVP( str, AuthMethod ); + COPY_KVP( str, SendTargets ); + COPY_KVP( str, HeaderDigest ); + COPY_KVP( str, DataDigest ); + COPY_KVP( str, InitiatorName ); + COPY_KVP( str, TargetName ); else { - logadd( LOG_DEBUG1, "iscsi_parse_text_key_value_pair: Unknown option: '%.*s'", (int)key_len, (const char*)packet_data ); + logadd( LOG_DEBUG1, "iscsi_parse_text_key_value_pair: Unknown option: '%.*s'", (int)key_len, packet_data ); } -#undef COPY_KVP_INT +#undef COPY_KVP return (int)key_val_len; } @@ -375,84 +268,35 @@ static int iscsi_parse_text_key_value_pair(iscsi_login_kvp *key_value_pairs, con * data amd puts the extracted data into a hash map to be used by * the iSCSI implementation. * - * @param[in] key_value_pairs Pointer to hash map that should contain all + * @param[in] pairs struct to write all key-value-pair options from packet to * extracted keys and pairs. May NOT be NULL, so take caution. * @param[in] packet_data Pointer to first key and value pair to * be parsed. NULL is an illegal value here, so be careful. * @param[in] len Length of the remaining packet data. - * @param[in] c_bit Non-zero value of C bit was set in previously. - * @param[in] partial_pairs Array of partial pair pointers in - * case C bit was set (multiple iSCSI packets for text data). * @retval -1 An error occured during parsing key. * @retval 0 Key and value pair was parsed successfully and was added to * hash map. */ -static int iscsi_parse_login_key_value_pairs(iscsi_login_kvp *key_value_pairs, const uint8_t *packet_data, uint len, int c_bit, uint8_t **partial_pairs) +static int iscsi_parse_login_key_value_pairs(iscsi_negotiation_kvp *pairs, const uint8_t *packet_data, uint len) { + memset( pairs, -1 , sizeof(*pairs) ); + pairs->SessionType = NULL; + pairs->AuthMethod = NULL; + pairs->SendTargets = NULL; + pairs->HeaderDigest = NULL; + pairs->DataDigest = NULL; + pairs->InitiatorName = NULL; + pairs->TargetName = NULL; + if ( len == 0U ) return 0; // iSCSI specs don't allow zero length - if ( (partial_pairs != NULL) && (*partial_pairs != NULL) ) { // Strip partial text parameters in case C bit was enabled previously - uint key_val_pair_len; - - for (key_val_pair_len = 0; (key_val_pair_len < len) && packet_data[key_val_pair_len] != '\0'; key_val_pair_len++) { - } - - uint8_t *tmp_partial_buf = iscsi_sprintf_alloc( "%s%s", *partial_pairs, (const char *) packet_data ); - - if ( tmp_partial_buf == NULL ) - return -1; - - const int rc = iscsi_parse_text_key_value_pair( key_value_pairs, tmp_partial_buf, (uint32_t) (key_val_pair_len + strlen( (char *) *partial_pairs )) ); - free( tmp_partial_buf ); - - if ( rc < 0 ) - return -1; - - free( *partial_pairs ); - *partial_pairs = NULL; - - packet_data += (key_val_pair_len + 1); - len -= (key_val_pair_len + 1); - } - - if ( c_bit ) { // Strip partial parameters in case C bit was enabled previousley - if ( partial_pairs == NULL ) { - logadd( LOG_ERROR, "iscsi_parse_key_value_pairs: C bit set but missing partial parameter" ); - - return -1; - } - - uint key_val_pair_len; - - for (key_val_pair_len = (len - 1U); (packet_data[key_val_pair_len] != '\0') && (key_val_pair_len > 0U); key_val_pair_len--) { - } - - if ( key_val_pair_len != 0U ) - key_val_pair_len++; // NUL char found, don't copy to target buffer' - - *partial_pairs = (uint8_t *) malloc( ((len - key_val_pair_len) + 1U) ); - - if ( *partial_pairs == NULL ) { - logadd( LOG_ERROR, "iscsi_parse_key_value_pairs: Out of memory allocating partial parameter" ); - - return -1; - } - - memcpy( *partial_pairs, &packet_data[key_val_pair_len], (len - key_val_pair_len) ); - - if ( key_val_pair_len != 0U ) - len = (key_val_pair_len - 1U); - else - return 0; - } - int offset = 0; while ( ((uint) offset < len) && (packet_data[offset] != '\0') ) { - const int rc = iscsi_parse_text_key_value_pair( key_value_pairs, (packet_data + offset), (len - offset) ); + const int rc = iscsi_parse_text_key_value_pair( pairs, (const char *)(packet_data + offset), (len - offset) ); - if ( rc < 0 ) + if ( rc <= 0 ) return -1; offset += rc; @@ -478,7 +322,7 @@ static int iscsi_parse_login_key_value_pairs(iscsi_login_kvp *key_value_pairs, c */ static iscsi_task *iscsi_task_create(iscsi_connection *conn) { - iscsi_task *task = (iscsi_task *) malloc( sizeof(struct iscsi_task) ); + iscsi_task *task = malloc( sizeof(struct iscsi_task) ); if ( task == NULL ) { logadd( LOG_ERROR, "iscsi_task_create: Out of memory while allocating iSCSI task" ); @@ -497,9 +341,6 @@ static iscsi_task *iscsi_task_create(iscsi_connection *conn) task->des_data_xfer_pos = 0UL; task->des_data_xfer_len = 0UL; task->data_sn = 0UL; - task->scsi_data_out_cnt = 0UL; - - conn->task_cnt++; iscsi_scsi_task_create( &task->scsi_task ); task->scsi_task.connection = conn; @@ -521,7 +362,6 @@ static void iscsi_task_destroy(iscsi_task *task) return; free( task->scsi_task.buf ); - task->conn->task_cnt--; free( task ); } @@ -548,7 +388,7 @@ static void iscsi_task_destroy(iscsi_task *task) */ static uint32_t iscsi_scsi_data_in_send(iscsi_connection *conn, iscsi_task *task, const uint32_t pos, const uint32_t len, const uint32_t res_cnt, const uint32_t data_sn, const int8_t flags, bool immediate) { - iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 0U, conn->header_digest, len, conn->data_digest ); + iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, len ); if ( response_pdu == NULL ) { logadd( LOG_ERROR, "iscsi_scsi_data_in_send: Out of memory while allocating iSCSI SCSI Data In response PDU" ); @@ -706,7 +546,7 @@ static void iscsi_task_response(iscsi_connection *conn, iscsi_task *task, iscsi_ } const uint32_t ds_len = (task->scsi_task.sense_data_len != 0U) ? (task->scsi_task.sense_data_len + offsetof(struct iscsi_scsi_ds_cmd_data, sense_data)) : 0UL; - iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 0U, conn->header_digest, ds_len, conn->data_digest ); + iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, ds_len ); if ( response_pdu == NULL ) { logadd( LOG_ERROR, "iscsi_task_response: Out of memory while allocating iSCSI SCSI response PDU" ); @@ -771,11 +611,7 @@ static void iscsi_task_response(iscsi_connection *conn, iscsi_task *task, iscsi_ } /** - * @brief Allocates and initializes a SCSI task. - * - * THis function assocates the callback - * functions to the SCSI task and sets - * the reference count to 1. + * @brief Initializes a SCSI task. * * @param[in] scsi_task Pointer to SCSI task. This * may NOT be NULL, so be careful. @@ -793,8 +629,6 @@ static void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task) scsi_task->xfer_len = 0UL; scsi_task->sense_data_len = 0U; scsi_task->status = ISCSI_SCSI_STATUS_GOOD; - scsi_task->task_mgmt_func = ISCSI_TASK_MGMT_FUNC_REQ_FUNC_ABORT_TASK; - scsi_task->task_mgmt_response = ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_COMPLETE; } /** @@ -892,44 +726,6 @@ static void iscsi_scsi_task_status_set(iscsi_scsi_task *scsi_task, const uint8_t } /** - * @brief Copies iSCSI SCSI task sense data and status code. - * - * This function allocates, if necessary, a - * SCSI sense data buffer and copies it over - * from source or deallocates the sense data - * buffer in case the source has no sense - * data. - * - * @param[in] dst_scsi_task Pointer to iSCSI SCSI task to copy to. - * May NOT be NULL, so be careful. - * @param[in] src_scsi_task Pointer to iSCSI SCSI task to copy from. - * NULL is NOT allowed here, take caution. - * @return 0 on successful copy operation, a negative - * error code otherwise. - */ -static int iscsi_scsi_task_status_copy(iscsi_scsi_task *dst_scsi_task, const iscsi_scsi_task *src_scsi_task) -{ - if ( dst_scsi_task->sense_data != NULL ) - free( dst_scsi_task->sense_data ); - - if ( src_scsi_task->sense_data != NULL ) { - dst_scsi_task->sense_data = malloc( src_scsi_task->sense_data_len ); - - if ( dst_scsi_task == NULL ) - return -1; - - memcpy( dst_scsi_task->sense_data, src_scsi_task->sense_data, src_scsi_task->sense_data_len ); - } else { - dst_scsi_task->sense_data = NULL; - } - - dst_scsi_task->sense_data_len = src_scsi_task->sense_data_len; - dst_scsi_task->status = src_scsi_task->status; - - return 0; -} - -/** * @brief Processes a iSCSI SCSI task with no LUN identifier. * * This function only generates a SCSI response @@ -1065,60 +861,6 @@ static void iscsi_scsi_lun_task_run( iscsi_scsi_task *scsi_task, iscsi_pdu *pdu) } /** - * @brief Checks whether an I/O feature is supported by a DNBD3 image. - * - * This function depends on DNBD3 image - * properties and queries only one I/O - * feature at once. - * - * @param[in] image Pointer to DNBD3 image to check I/O - * attributes for. May NOT be NULL, so be - * careful. - * @param[in] type I/O type to be checked for. - * @retval true The DNBD3 image supports the I/O feature. - * @retval false The I/O feature is NOT supported for the - * DNBD3 image. - */ -static inline bool iscsi_scsi_emu_io_type_is_supported(const dnbd3_image_t *image, const int type) -{ - switch ( type ) { - case ISCSI_SCSI_EMU_IO_TYPE_REMOVABLE : { - return true; - } - case ISCSI_SCSI_EMU_IO_TYPE_NO_ROTATION : { - return true; - } - case ISCSI_SCSI_EMU_IO_TYPE_PHYSICAL_READ_ONLY : { - return false; - } - case ISCSI_SCSI_EMU_IO_TYPE_WRITE_PROTECT : { - return true; - } - default : { - return false; - break; - } - } -} - -/** - * @brief Retrieves the bit shift of a physical block in bytes for a DNBD3 image. - * - * This function depends on DNBD3 image - * properties. - * - * @param[in] image Pointer to DNBD3 image to retrieve - * the physical bit shift size. May NOT - * be NULL, so be careful. - * @return The physical block size in bytes as a - * bit shift count. - */ -static inline uint32_t iscsi_scsi_emu_physical_block_get_size_shift(const dnbd3_image_t *image) -{ - return ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE_BITS; -} - -/** * @brief Retrieves the size of a physical block in bytes for a DNBD3 image. * * This function depends on DNBD3 image @@ -1147,77 +889,7 @@ static inline uint32_t iscsi_scsi_emu_physical_block_get_size(const dnbd3_image_ */ static inline uint64_t iscsi_scsi_emu_block_get_count(const dnbd3_image_t *image) { - return (image->virtualFilesize >> ISCSI_SCSI_EMU_BLOCK_SIZE_BITS); -} - -/** - * @brief Retrieves the bit shift of a logical block in bytes for a DNBD3 image. - * - * This function depends on DNBD3 image - * properties. - * - * @param[in] image Pointer to DNBD3 image to retrieve - * the logical block bit shift size. - * May NOT be NULL, so be careful. - * @return The logical block size in bytes as a - * bit shift count. - */ -static inline uint32_t iscsi_scsi_emu_block_get_size_shift(const dnbd3_image_t *image) -{ - return ISCSI_SCSI_EMU_BLOCK_SIZE_BITS; -} - -/** - * @brief Retrieves the size of a logical block in bytes for a DNBD3 image. - * - * This function depends on DNBD3 image - * properties. - * - * @param[in] image Pointer to DNBD3 image to retrieve - * the logical block size. May NOT be NULL, - * so be careful. - * @return The logical block size in bytes. - */ -static inline uint32_t iscsi_scsi_emu_block_get_size(const dnbd3_image_t *image) -{ - return ISCSI_SCSI_EMU_BLOCK_SIZE; -} - -/** - * @brief Retrieves the bit shift ratio between logical and physical block size for a DNBD3 image. - * - * This function depends on DNBD3 image - * properties. - * - * @param[in] image Pointer to DNBD3 image to retrieve - * the ratio between the logical and - * physical block size. May NOT be - * NULL, so be careful. - * @return The ratio between logical and physical - * block size as a logical bit shift - * count. - */ -static inline uint32_t iscsi_scsi_emu_block_get_ratio_shift(const dnbd3_image_t *image) -{ - return (iscsi_scsi_emu_physical_block_get_size_shift(image) - iscsi_scsi_emu_block_get_size_shift(image)); -} - -/** - * @brief Retrieves the ratio between logical and physical block size for a DNBD3 image. - * - * This function depends on DNBD3 image - * properties. - * - * @param[in] image Pointer to DNBD3 image to retrieve - * the ratio between the logical and - * physical block size. May NOT be - * NULL, so be careful. - * @return The ratio between logical logical and physical - * block size. - */ -static inline uint32_t iscsi_scsi_emu_block_get_ratio(const dnbd3_image_t *image) -{ - return (1UL << iscsi_scsi_emu_block_get_ratio_shift( image )); + return (image->virtualFilesize / ISCSI_SCSI_EMU_BLOCK_SIZE); } /** @@ -1239,21 +911,12 @@ static inline uint32_t iscsi_scsi_emu_block_get_ratio(const dnbd3_image_t *image * bytes is aligned to block size or a * positive value if unaligned. */ -static uint64_t iscsi_scsi_emu_bytes_to_blocks(uint64_t *offset_blocks, uint64_t *num_blocks, const uint64_t offset_bytes, const uint64_t num_bytes, const uint32_t block_size) +static uint64_t iscsi_scsi_emu_bytes_to_blocks(uint64_t *offset_blocks, uint64_t *num_blocks, const uint64_t offset_bytes, const uint64_t num_bytes) { - if ( iscsi_is_pow2( block_size ) ) { - const uint32_t shift = iscsi_get_log2_of_pow2( block_size ); - - *offset_blocks = (offset_bytes >> shift); - *num_blocks = (num_bytes >> shift); + *offset_blocks = (offset_bytes / ISCSI_SCSI_EMU_BLOCK_SIZE); + *num_blocks = (num_bytes / ISCSI_SCSI_EMU_BLOCK_SIZE); - return ((offset_bytes - (*offset_blocks << shift)) | (num_bytes - (*num_blocks << shift))); - } - - *offset_blocks = (offset_bytes / block_size); - *num_blocks = (num_bytes / block_size); - - return ((offset_bytes % block_size) | (num_bytes % block_size)); + return ((offset_bytes % ISCSI_SCSI_EMU_BLOCK_SIZE) | (num_bytes % ISCSI_SCSI_EMU_BLOCK_SIZE)); } /** @@ -1267,22 +930,13 @@ static uint64_t iscsi_scsi_emu_bytes_to_blocks(uint64_t *offset_blocks, uint64_t * careful. * @param[in] offset_blocks Offset in blocks. * @param[in] num_blocks Number of blocks. - * @param[in] block_size Block size in bytes. * @return Number of blocks in bytes. */ -static uint64_t iscsi_scsi_emu_blocks_to_bytes(uint64_t *offset_bytes, const uint64_t offset_blocks, const uint64_t num_blocks, const uint32_t block_size) +static uint64_t iscsi_scsi_emu_blocks_to_bytes(uint64_t *offset_bytes, const uint64_t offset_blocks, const uint64_t num_blocks) { - if ( iscsi_is_pow2( block_size ) ) { - const uint32_t shift = iscsi_get_log2_of_pow2( block_size ); - - *offset_bytes = (offset_blocks << shift); + *offset_bytes = (offset_blocks * ISCSI_SCSI_EMU_BLOCK_SIZE); - return (num_blocks << shift); - } - - *offset_bytes = (offset_blocks * block_size); - - return (num_blocks * block_size); + return (num_blocks * ISCSI_SCSI_EMU_BLOCK_SIZE); } /** @@ -1301,11 +955,11 @@ static uint64_t iscsi_scsi_emu_blocks_to_bytes(uint64_t *offset_bytes, const uin * uplink_request(). * @param[in] buffer Data for requested range. */ -static void iscsi_uplink_callback(void *data, uint64_t handle, uint64_t start UNUSED, uint32_t length, const char *buffer) +static void iscsi_uplink_callback(void *data, uint64_t handle UNUSED, uint64_t start UNUSED, uint32_t length, const char *buffer) { iscsi_scsi_task *scsi_task = (iscsi_scsi_task *) data; - memcpy( (uint8_t *) handle, buffer, length ); + memcpy( scsi_task->buf, buffer, length ); pthread_mutex_lock( &scsi_task->uplink_mutex ); pthread_cond_signal( &scsi_task->uplink_cond ); @@ -1323,9 +977,6 @@ static void iscsi_uplink_callback(void *data, uint64_t handle, uint64_t start UN * @param[in] scsi_task Pointer to iSCSI SCSI task which * executes the I/O read operation, may * NOT be NULL, so be careful. - * @param[in] buf Pointer to buffer where to store - * the read data. NULL is NOT allowed - * here, take caution. * @param[in] image Pointer to DNBD3 image to read * data from and may NOT be NULL, so * be careful. @@ -1335,10 +986,10 @@ static void iscsi_uplink_callback(void *data, uint64_t handle, uint64_t start UN * @return 0 on successful operation, a negative * error code otherwise. */ -static int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, uint8_t *buf, dnbd3_image_t *image, const uint64_t offset_blocks, const uint64_t num_blocks, const uint32_t block_size) +static int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, dnbd3_image_t *image, const uint64_t offset_blocks, const uint64_t num_blocks) { uint64_t offset_bytes; - const uint64_t num_bytes = iscsi_scsi_emu_blocks_to_bytes( &offset_bytes, offset_blocks, num_blocks, block_size ); + const uint64_t num_bytes = iscsi_scsi_emu_blocks_to_bytes( &offset_bytes, offset_blocks, num_blocks ); dnbd3_cache_map_t *cache = ref_get_cachemap( image ); bool readFromFile; @@ -1359,7 +1010,7 @@ static int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, uint8_t *bu pthread_cond_init( &scsi_task->uplink_cond, NULL ); pthread_mutex_lock( &scsi_task->uplink_mutex ); - if ( !uplink_request( image, scsi_task, iscsi_uplink_callback, (uint64_t) buf, offset_bytes, num_bytes ) ) { + if ( !uplink_request( image, scsi_task, iscsi_uplink_callback, 0, offset_bytes, num_bytes ) ) { pthread_mutex_unlock( &scsi_task->uplink_mutex ); logadd( LOG_DEBUG1, "Could not relay uncached request to upstream proxy for image %s:%d", @@ -1379,7 +1030,7 @@ static int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, uint8_t *bu bool success; if ( readFromFile ) { - const int64_t len = pread( image->readFd, buf, (size_t) num_bytes, offset_bytes ); + const int64_t len = pread( image->readFd, scsi_task->buf, (size_t) num_bytes, offset_bytes ); success = ((uint64_t) len == num_bytes); } else { success = true; @@ -1420,15 +1071,21 @@ static int iscsi_scsi_emu_block_read_write(dnbd3_image_t *image, iscsi_scsi_task { scsi_task->xfer_pos = 0UL; + if ( (flags & ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE) != 0 ) { + iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE ); + + return ISCSI_SCSI_TASK_RUN_COMPLETE; + } + if ( (scsi_task->flags & (ISCSI_SCSI_TASK_FLAGS_XFER_READ | ISCSI_SCSI_TASK_FLAGS_XFER_WRITE)) == (ISCSI_SCSI_TASK_FLAGS_XFER_READ | ISCSI_SCSI_TASK_FLAGS_XFER_WRITE) ) { iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE ); return ISCSI_SCSI_TASK_RUN_COMPLETE; } - const uint64_t block_count = iscsi_scsi_emu_block_get_count( image ); + const uint64_t imgBlockCount = iscsi_scsi_emu_block_get_count( image ); - if ( (block_count <= lba) || ((block_count - lba) < xfer_len) ) { + if ( (imgBlockCount <= lba) || ((imgBlockCount - lba) < xfer_len) ) { iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE ); return ISCSI_SCSI_TASK_RUN_COMPLETE; @@ -1440,8 +1097,7 @@ static int iscsi_scsi_emu_block_read_write(dnbd3_image_t *image, iscsi_scsi_task return ISCSI_SCSI_TASK_RUN_COMPLETE; } - const uint32_t block_size = iscsi_scsi_emu_block_get_size( image ); - const uint32_t max_xfer_len = ISCSI_MAX_DS_SIZE / block_size; + const uint32_t max_xfer_len = ISCSI_MAX_DS_SIZE / ISCSI_SCSI_EMU_BLOCK_SIZE; if ( xfer_len > max_xfer_len ) { iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE ); @@ -1449,16 +1105,10 @@ static int iscsi_scsi_emu_block_read_write(dnbd3_image_t *image, iscsi_scsi_task return ISCSI_SCSI_TASK_RUN_COMPLETE; } - if ( (flags & ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE) != 0 ) { - iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE ); - - return ISCSI_SCSI_TASK_RUN_COMPLETE; - } - uint64_t offset_blocks; uint64_t num_blocks; - if ( iscsi_scsi_emu_bytes_to_blocks( &offset_blocks, &num_blocks, scsi_task->pos, scsi_task->len, block_size ) != 0ULL ) { + if ( iscsi_scsi_emu_bytes_to_blocks( &offset_blocks, &num_blocks, scsi_task->pos, scsi_task->len ) != 0ULL ) { iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE ); return ISCSI_SCSI_TASK_RUN_COMPLETE; @@ -1468,23 +1118,17 @@ static int iscsi_scsi_emu_block_read_write(dnbd3_image_t *image, iscsi_scsi_task int rc; - if ( (flags & ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE) == 0 ) { - scsi_task->buf = (uint8_t *) malloc( scsi_task->len ); - - if ( scsi_task->buf == NULL ) { - iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_HARDWARE_ERR, - ISCSI_SCSI_ASC_INTERNAL_TARGET_FAIL, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE ); - - return ISCSI_SCSI_TASK_RUN_COMPLETE; - } + scsi_task->buf = (uint8_t *) malloc( scsi_task->len ); - rc = iscsi_scsi_emu_io_blocks_read( scsi_task, scsi_task->buf, image, offset_blocks, num_blocks, block_size ); - } else { - iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_DATA_PROTECT, ISCSI_SCSI_ASC_WRITE_PROTECTED, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE ); + if ( scsi_task->buf == NULL ) { + iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_HARDWARE_ERR, + ISCSI_SCSI_ASC_INTERNAL_TARGET_FAIL, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE ); return ISCSI_SCSI_TASK_RUN_COMPLETE; } + rc = iscsi_scsi_emu_io_blocks_read( scsi_task, image, offset_blocks, num_blocks ); + if ( rc < 0 ) { if ( rc == -ENOMEM ) { iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_HARDWARE_ERR, @@ -1532,8 +1176,6 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) xfer_len = 256UL; return iscsi_scsi_emu_block_read_write( image, scsi_task, lba, xfer_len, 0 ); - - break; } case ISCSI_SCSI_OPCODE_READ10 : { const iscsi_scsi_cdb_read_write_10 *cdb_read_write_10 = (iscsi_scsi_cdb_read_write_10 *) scsi_task->cdb; @@ -1542,8 +1184,6 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) xfer_len = iscsi_get_be16(cdb_read_write_10->xfer_len); return iscsi_scsi_emu_block_read_write( image, scsi_task, lba, xfer_len, 0 ); - - break; } case ISCSI_SCSI_OPCODE_READ12 : { const iscsi_scsi_cdb_read_write_12 *cdb_read_write_12 = (iscsi_scsi_cdb_read_write_12 *) scsi_task->cdb; @@ -1552,8 +1192,6 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) xfer_len = iscsi_get_be32(cdb_read_write_12->xfer_len); return iscsi_scsi_emu_block_read_write( image, scsi_task, lba, xfer_len, 0 ); - - break; } case ISCSI_SCSI_OPCODE_READ16 : { const iscsi_scsi_cdb_read_write_16 *cdb_read_write_16 = (iscsi_scsi_cdb_read_write_16 *) scsi_task->cdb; @@ -1562,8 +1200,6 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) xfer_len = iscsi_get_be32(cdb_read_write_16->xfer_len); return iscsi_scsi_emu_block_read_write( image, scsi_task, lba, xfer_len, 0 ); - - break; } case ISCSI_SCSI_OPCODE_READCAPACITY10 : { iscsi_scsi_read_capacity_10_parameter_data_packet *buf = (iscsi_scsi_read_capacity_10_parameter_data_packet *) malloc( sizeof(struct iscsi_scsi_read_capacity_10_parameter_data_packet) ); @@ -1581,9 +1217,7 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) else iscsi_put_be32( (uint8_t *) &buf->lba, (uint32_t) lba ); - xfer_len = iscsi_scsi_emu_block_get_size( image ); - - iscsi_put_be32( (uint8_t *) &buf->block_len, xfer_len ); + iscsi_put_be32( (uint8_t *) &buf->block_len, ISCSI_SCSI_EMU_BLOCK_SIZE ); uint len = scsi_task->len; @@ -1599,53 +1233,41 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) case ISCSI_SCSI_OPCODE_SERVICE_ACTION_IN_16 : { const iscsi_scsi_cdb_service_action_in_16 *cdb_servce_in_action_16 = (iscsi_scsi_cdb_service_action_in_16 *) scsi_task->cdb; - switch ( ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_GET_ACTION(cdb_servce_in_action_16->action) ) { - case ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_ACTION_READ_CAPACITY_16 : { - iscsi_scsi_service_action_in_16_parameter_data_packet *buf = (iscsi_scsi_service_action_in_16_parameter_data_packet *) malloc( sizeof(struct iscsi_scsi_service_action_in_16_parameter_data_packet) ); - - if ( buf == NULL ) { - iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NOT_READY, ISCSI_SCSI_ASC_LOGICAL_UNIT_NOT_READY, ISCSI_SCSI_ASCQ_BECOMING_READY ); - - return ISCSI_SCSI_TASK_RUN_COMPLETE; - } - - lba = iscsi_scsi_emu_block_get_count( image ) - 1ULL; - xfer_len = iscsi_scsi_emu_block_get_size( image ); + if ( ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_GET_ACTION(cdb_servce_in_action_16->action) + != ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_ACTION_READ_CAPACITY_16 ) { + return ISCSI_SCSI_TASK_RUN_UNKNOWN; + } + iscsi_scsi_service_action_in_16_parameter_data_packet *buf = (iscsi_scsi_service_action_in_16_parameter_data_packet *) malloc( sizeof(struct iscsi_scsi_service_action_in_16_parameter_data_packet) ); - iscsi_put_be64( (uint8_t *) &buf->lba, lba ); - iscsi_put_be32( (uint8_t *) &buf->block_len, xfer_len ); + if ( buf == NULL ) { + iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NOT_READY, ISCSI_SCSI_ASC_LOGICAL_UNIT_NOT_READY, ISCSI_SCSI_ASCQ_BECOMING_READY ); - buf->flags = 0; + return ISCSI_SCSI_TASK_RUN_COMPLETE; + } - const uint8_t exponent = (uint8_t) iscsi_scsi_emu_block_get_ratio_shift( image ); + lba = iscsi_scsi_emu_block_get_count( image ) - 1ULL; - buf->exponents = ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_PUT_LBPPB_EXPONENT((exponent <= ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LBPPB_EXPONENT_MASK) ? exponent : 0U); + iscsi_put_be64( (uint8_t *) &buf->lba, lba ); + iscsi_put_be32( (uint8_t *) &buf->block_len, ISCSI_SCSI_EMU_BLOCK_SIZE ); - if ( iscsi_scsi_emu_io_type_is_supported( image, ISCSI_SCSI_EMU_IO_TYPE_UNMAP ) ) - iscsi_put_be16( (uint8_t *) &buf->lbp_lalba, ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LBPME ); - else - buf->lbp_lalba = 0U; + buf->flags = 0; - buf->reserved[0] = 0ULL; - buf->reserved[1] = 0ULL; + const uint8_t exponent = ISCSI_SCSI_EMU_BLOCK_DIFF_SHIFT; - uint len = cdb_servce_in_action_16->alloc_len; + buf->exponents = ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_PUT_LBPPB_EXPONENT((exponent <= ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LBPPB_EXPONENT_MASK) ? exponent : 0U); - if ( len > sizeof(struct iscsi_scsi_service_action_in_16_parameter_data_packet) ) - len = sizeof(struct iscsi_scsi_service_action_in_16_parameter_data_packet); // TODO: Check whether scatter data is required + buf->lbp_lalba = 0U; + buf->reserved[0] = 0ULL; + buf->reserved[1] = 0ULL; - scsi_task->buf = (uint8_t *) buf; - scsi_task->xfer_pos = len; - scsi_task->status = ISCSI_SCSI_STATUS_GOOD; + uint len = cdb_servce_in_action_16->alloc_len; - break; - } - default : { - return ISCSI_SCSI_TASK_RUN_UNKNOWN; + if ( len > sizeof(struct iscsi_scsi_service_action_in_16_parameter_data_packet) ) + len = sizeof(struct iscsi_scsi_service_action_in_16_parameter_data_packet); // TODO: Check whether scatter data is required - break; - } - } + scsi_task->buf = (uint8_t *) buf; + scsi_task->xfer_pos = len; + scsi_task->status = ISCSI_SCSI_STATUS_GOOD; break; } @@ -1958,7 +1580,7 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task vpd_page_block_limits_inquiry_data_pkt->flags = 0; - uint32_t blocks = (ISCSI_MAX_DS_SIZE >> iscsi_scsi_emu_block_get_size_shift( image )); + uint32_t blocks = (ISCSI_MAX_DS_SIZE / ISCSI_SCSI_EMU_BLOCK_SIZE); vpd_page_block_limits_inquiry_data_pkt->max_cmp_write_len = (uint8_t) blocks; @@ -2027,7 +1649,7 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task const uint8_t pti = ISCSI_SCSI_BASIC_INQUIRY_DATA_PUT_PERIPHERAL_TYPE(ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_DIRECT) | ISCSI_SCSI_BASIC_INQUIRY_DATA_PUT_PERIPHERAL_ID(ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_ID_POSSIBLE); std_inquiry_data_pkt->basic_inquiry.peripheral_type_id = pti; - std_inquiry_data_pkt->basic_inquiry.peripheral_type_mod_flags = (int8_t) (iscsi_scsi_emu_io_type_is_supported( image, ISCSI_SCSI_EMU_IO_TYPE_REMOVABLE ) ? ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_MOD_FLAGS_REMOVABLE_MEDIA : 0); + std_inquiry_data_pkt->basic_inquiry.peripheral_type_mod_flags = 0; std_inquiry_data_pkt->basic_inquiry.version = ISCSI_SCSI_BASIC_INQUIRY_DATA_PUT_VERSION_ANSI(ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_SPC3); std_inquiry_data_pkt->basic_inquiry.response_data_fmt_flags = ISCSI_SCSI_BASIC_INQUIRY_DATA_PUT_RESPONSE_DATA_FMT_FLAGS(ISCSI_SCSI_BASIC_INQUIRY_DATA_RESPONSE_DATA_FMT_FLAGS_SCSI_2) | ISCSI_SCSI_BASIC_INQUIRY_DATA_RESPONSE_DATA_FMT_FLAGS_HISUP; @@ -2329,9 +1951,6 @@ static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_sc iscsi_scsi_emu_primary_mode_sense_page_init( mode_sense_mode_page_pkt, page_len, page, sub_page ); - if ( (mode_sense_mode_page_pkt != NULL) && iscsi_scsi_emu_io_type_is_supported( image, ISCSI_SCSI_EMU_IO_TYPE_WRITE_CACHE ) && (pc != ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_CHG_VALUES) ) - mode_sense_caching_mode_page_pkt->flags |= ISCSI_SCSI_MODE_SENSE_CACHING_MODE_PAGE_FLAGS_WCE; - if ( (mode_sense_mode_page_pkt != NULL) && (pc != ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_CHG_VALUES) ) mode_sense_caching_mode_page_pkt->flags |= ISCSI_SCSI_MODE_SENSE_CACHING_MODE_PAGE_FLAGS_RCD; @@ -2492,21 +2111,24 @@ static int iscsi_scsi_emu_primary_mode_sense(dnbd3_image_t *image, iscsi_scsi_ta if ( hdr_len == sizeof(struct iscsi_scsi_mode_sense_6_parameter_header_data_packet) ) { mode_sense_6_parameter_hdr_data_pkt->mode_data_len = (uint8_t) (alloc_len - sizeof(uint8_t)); mode_sense_6_parameter_hdr_data_pkt->medium_type = 0U; - mode_sense_6_parameter_hdr_data_pkt->flags = (int8_t) ((iscsi_scsi_emu_io_type_is_supported( image, ISCSI_SCSI_EMU_IO_TYPE_PHYSICAL_READ_ONLY ) || iscsi_scsi_emu_io_type_is_supported( image, ISCSI_SCSI_EMU_IO_TYPE_WRITE_PROTECT )) ? ISCSI_SCSI_MODE_SENSE_6_PARAM_HDR_DATA_FLAGS_WP : 0); + mode_sense_6_parameter_hdr_data_pkt->flags = ISCSI_SCSI_MODE_SENSE_6_PARAM_HDR_DATA_FLAGS_WP; mode_sense_6_parameter_hdr_data_pkt->block_desc_len = (uint8_t) block_desc_len; - } else { + } else if ( hdr_len == sizeof(struct iscsi_scsi_mode_sense_10_parameter_header_data_packet) ) { iscsi_scsi_mode_sense_10_parameter_header_data_packet *mode_sense_10_parameter_hdr_data_pkt = (iscsi_scsi_mode_sense_10_parameter_header_data_packet *) mode_sense_6_parameter_hdr_data_pkt; iscsi_put_be16( (uint8_t *) &mode_sense_10_parameter_hdr_data_pkt->mode_data_len, (uint16_t) (alloc_len - sizeof(uint16_t)) ); mode_sense_10_parameter_hdr_data_pkt->medium_type = 0U; - mode_sense_10_parameter_hdr_data_pkt->flags = (int8_t) ((iscsi_scsi_emu_io_type_is_supported( image, ISCSI_SCSI_EMU_IO_TYPE_PHYSICAL_READ_ONLY ) || iscsi_scsi_emu_io_type_is_supported( image, ISCSI_SCSI_EMU_IO_TYPE_WRITE_PROTECT )) ? ISCSI_SCSI_MODE_SENSE_10_PARAM_HDR_DATA_FLAGS_WP : 0); + mode_sense_10_parameter_hdr_data_pkt->flags = ISCSI_SCSI_MODE_SENSE_10_PARAM_HDR_DATA_FLAGS_WP; mode_sense_10_parameter_hdr_data_pkt->long_lba = (uint8_t) long_lba; mode_sense_10_parameter_hdr_data_pkt->reserved = 0U; iscsi_put_be16( (uint8_t *) &mode_sense_10_parameter_hdr_data_pkt->block_desc_len, (uint16_t) block_desc_len ); + } else { + logadd( LOG_DEBUG1, "iscsi_scsi_emu_primary_mode_sense: invalid parameter header length %u", hdr_len ); + return -1; } const uint64_t num_blocks = iscsi_scsi_emu_block_get_count( image ); - const uint32_t block_size = iscsi_scsi_emu_block_get_size( image ); + const uint32_t block_size = ISCSI_SCSI_EMU_BLOCK_SIZE; if ( block_desc_len == sizeof(struct iscsi_scsi_mode_sense_lba_parameter_block_desc_data_packet) ) { iscsi_scsi_mode_sense_lba_parameter_block_desc_data_packet *lba_parameter_block_desc = (iscsi_scsi_mode_sense_lba_parameter_block_desc_data_packet *) (((uint8_t *) mode_sense_6_parameter_hdr_data_pkt) + hdr_len); @@ -2641,62 +2263,6 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) break; } - case ISCSI_SCSI_OPCODE_MODESELECT6 : { - const iscsi_scsi_cdb_mode_select_6 *cdb_mode_select_6 = (iscsi_scsi_cdb_mode_select_6 *) scsi_task->cdb; - - alloc_len = cdb_mode_select_6->param_list_len; - - if ( alloc_len == 0U ) - break; - - rc = iscsi_scsi_emu_check_len( scsi_task, alloc_len, sizeof(struct iscsi_scsi_mode_select_6_parameter_list_packet) ); - - if ( rc < 0 ) - break; - - len = scsi_task->len; - - if ( alloc_len < sizeof(struct iscsi_scsi_mode_select_6_parameter_list_packet) ) - alloc_len = sizeof(struct iscsi_scsi_mode_select_6_parameter_list_packet); - - rc = iscsi_scsi_emu_check_len( scsi_task, len, alloc_len ); - - if ( rc < 0 ) - break; - - scsi_task->xfer_pos = alloc_len; - scsi_task->status = ISCSI_SCSI_STATUS_GOOD; - - break; - } - case ISCSI_SCSI_OPCODE_MODESELECT10 : { - const iscsi_scsi_cdb_mode_select_10 *cdb_mode_select_10 = (iscsi_scsi_cdb_mode_select_10 *) scsi_task->cdb; - - alloc_len = iscsi_get_be16(cdb_mode_select_10->param_list_len); - - if ( alloc_len == 0U ) - break; - - rc = iscsi_scsi_emu_check_len( scsi_task, alloc_len, sizeof(struct iscsi_scsi_mode_select_10_parameter_list_packet) ); - - if ( rc < 0 ) - break; - - len = scsi_task->len; - - if ( alloc_len < sizeof(struct iscsi_scsi_mode_select_10_parameter_list_packet) ) - alloc_len = sizeof(struct iscsi_scsi_mode_select_10_parameter_list_packet); - - rc = iscsi_scsi_emu_check_len( scsi_task, len, alloc_len ); - - if ( rc < 0 ) - break; - - scsi_task->xfer_pos = alloc_len; - scsi_task->status = ISCSI_SCSI_STATUS_GOOD; - - break; - } case ISCSI_SCSI_OPCODE_MODESENSE6 : { const iscsi_scsi_cdb_mode_sense_6 *cdb_mode_sense_6 = (iscsi_scsi_cdb_mode_sense_6 *) scsi_task->cdb; @@ -2796,53 +2362,9 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) break; } - case ISCSI_SCSI_OPCODE_REQUESTSENSE : { - const iscsi_scsi_cdb_req_sense *cdb_req_sense = (iscsi_scsi_cdb_req_sense *) scsi_task->cdb; - - if ( (cdb_req_sense->flags & ISCSI_SCSI_CDB_REQ_SENSE_FLAGS_DESC) != 0 ) { - iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE ); - - break; - } - - alloc_len = cdb_req_sense->alloc_len; - - iscsi_scsi_task_sense_data_build( scsi_task, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE ); - - len = scsi_task->sense_data_len; - - if ( len > 0U ) { - iscsi_scsi_sense_data_check_cond_packet *sense_data = malloc( len ); - - if ( sense_data == NULL ) { - iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NOT_READY, ISCSI_SCSI_ASC_LOGICAL_UNIT_NOT_READY, ISCSI_SCSI_ASCQ_BECOMING_READY ); - - break; - } - - memcpy( sense_data, scsi_task->sense_data, len ); - - if ( len > alloc_len ) - len = alloc_len; - - scsi_task->buf = (uint8_t *) sense_data; - } - - scsi_task->xfer_pos = len; - scsi_task->status = ISCSI_SCSI_STATUS_GOOD; - - break; - } - case ISCSI_SCSI_OPCODE_LOGSELECT : - case ISCSI_SCSI_OPCODE_LOGSENSE : { - iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_COMMAND_OPERATION_CODE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE ); - - break; - } case ISCSI_SCSI_OPCODE_TESTUNITREADY : { scsi_task->xfer_pos = 0UL; scsi_task->status = ISCSI_SCSI_STATUS_GOOD; - logadd( LOG_WARNING, "UNIT READYYYYY" ); break; } @@ -2887,87 +2409,6 @@ static uint64_t iscsi_target_node_wwn_get(const uint8_t *name) } /** - * @brief Extracts the DNBD3 image out of an iSCSI IQN string and opens the DNBD3 image. - * - * This function uses the : separator as - * specified by the IQN standard.\n - * If no colons are in the IQN string, - * the complete string will be - * considered the image file name.\n - * The image file name is assumed - * before the last colon and is - * either directly opened or if - * that fails, a WWN name by - * IEEE Extended NAA is tried as - * well.\n - * The image revision is assumed - * after the last colon. - * @param[in] iqn Pointer to iSCSI IQN string. This - * is not allowed to be NULL, so be careful. - * @return Pointer to DNBD3 image if successful - * operation or NULL if failed. - */ -static dnbd3_image_t *iscsi_target_node_image_get(uint8_t *iqn) -{ - uint8_t *image_name = iqn; - uint8_t *image_rev = NULL; - uint8_t *tmp = (uint8_t *) strchr( (char *) iqn, ':' ); - - while ( tmp != NULL ) { - tmp++; - - image_name = image_rev; - image_rev = tmp; - tmp = (uint8_t *) strchr( (char *) tmp, ':' ); - } - - if ( image_rev == NULL ) - image_rev = image_name; - - const uint len = (uint) (image_rev - image_name); - - if ( len > 0U ) { - tmp = (uint8_t *) malloc( len ); - - if ( tmp == NULL ) { - logadd( LOG_ERROR, "iscsi_target_node_image_get: Out of memory while allocating DNBD3 image name for iSCSI target node" ); - - return NULL; - } - - memcpy( tmp, image_name, (len - 1) ); - tmp[len - 1] = '\0'; - } else { - tmp = image_name; - } - - const uint16_t rev = (uint16_t) ((len > 0U) ? atoi( (char *) image_rev ) : 0); - dnbd3_image_t *image = image_getOrLoad( (char *) image_name, rev ); - - if ( image == NULL ) { - image = image_getOrLoad( (char *) tmp, rev ); - - if ( image == NULL ) { - if ( strncasecmp( (char *) image_name, ISCSI_TARGET_NODE_WWN_NAME_PREFIX, ISCSI_STRLEN(ISCSI_TARGET_NODE_WWN_NAME_PREFIX) ) == 0 ) { - uint64_t wwn = strtoull( (char *) (image_name + ISCSI_STRLEN(ISCSI_TARGET_NODE_WWN_NAME_PREFIX)), NULL, 16 ); - - image = image_getByWwn( wwn, rev, true ); - - if ( image == NULL ) { - wwn = strtoull( (char *) (tmp + ISCSI_STRLEN(ISCSI_TARGET_NODE_WWN_NAME_PREFIX)), NULL, 16 ); - image = image_getByWwn( wwn, rev, true ); - } - } - } - } - - if ( len > 0U ) - free( tmp ); - - return image; -} - -/** * @brief Creates and initializes an iSCSI session. * * This function creates and initializes all relevant @@ -2991,8 +2432,6 @@ static iscsi_session *iscsi_session_create(iscsi_connection *conn, const int ty return NULL; } - session->flags = (ISCSI_SESSION_FLAGS_INIT_R2T | ISCSI_SESSION_FLAGS_DATA_PDU_IN_ORDER | ISCSI_SESSION_FLAGS_DATA_SEQ_IN_ORDER); - session->tsih = 0ULL; session->type = type; session->exp_cmd_sn = 0UL; @@ -3037,26 +2476,9 @@ static iscsi_connection *iscsi_connection_create(dnbd3_client_t *client) return NULL; } - conn->session = NULL; - - if ( conn->key_value_pairs == NULL ) { - logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI login text key / value pair hash map" ); - - free( conn ); - - return NULL; - } - - conn->partial_pairs = NULL; - conn->text_partial_pairs = NULL; + conn->session = NULL; conn->pdu_processing = NULL; conn->login_response_pdu = NULL; - conn->target_send_total_size = 0U; - conn->scsi_data_in_cnt = 0U; - conn->scsi_data_out_cnt = 0U; - conn->task_cnt = 0U; - conn->header_digest = 0; - conn->data_digest = 0; conn->id = 0; conn->client = client; conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_READY; @@ -3070,7 +2492,6 @@ static iscsi_connection *iscsi_connection_create(dnbd3_client_t *client) conn->init_task_tag = 0UL; conn->target_xfer_tag = 0UL; conn->stat_sn = 0UL; - conn->exp_stat_sn = 0UL; return conn; } @@ -3092,13 +2513,7 @@ static void iscsi_connection_destroy(iscsi_connection *conn) { if ( conn != NULL ) { iscsi_session_destroy( conn->session ); - - free ( conn->text_partial_pairs ); - - if ( conn->partial_pairs != NULL ) { - free ( conn->partial_pairs ); - } - + iscsi_connection_pdu_destroy( conn->pdu_processing ); free( conn ); } } @@ -3120,31 +2535,14 @@ static int32_t iscsi_connection_read(const iscsi_connection *conn, uint8_t *buf, if ( len == 0UL ) return 0L; - const int32_t rc = (int32_t) recv( conn->client->sock, buf, (size_t) len, MSG_WAITALL ); - - return ((rc > 0L) ? rc : (int32_t) ISCSI_CONNECT_PDU_READ_ERR_FATAL); -} - -/** - * @brief Writes data for the specified iSCSI connection to its TCP socket. - * - * The TCP socket is marked as non-blocking, so this function may not read - * all data requested. - * - * Returns ISCSI_CONNECT_PDU_READ_ERR_FATAL if the operation - * indicates a fatal error with the TCP connection (including - * if the TCP connection was closed unexpectedly). - * - * Otherwise returns the number of bytes successfully written. - */ -static int32_t iscsi_connection_write(const iscsi_connection *conn, uint8_t *buf, const uint32_t len) -{ - if ( len == 0UL ) - return 0L; - - const int32_t rc = (int32_t) sock_sendAll( conn->client->sock, buf, (size_t) len, ISCSI_CONNECT_SOCKET_WRITE_RETRIES ); + int32_t rc; + do { + rc = (int32_t) recv( conn->client->sock, buf, (size_t) len, MSG_WAITALL ); + } while ( rc == -1 && errno == EINTR ); - return ((rc > 0L) ? rc : (int32_t) ISCSI_CONNECT_PDU_READ_ERR_FATAL); + if ( rc == 0 ) + return -1; // EOF + return rc; } /** @@ -3154,8 +2552,7 @@ static int32_t iscsi_connection_write(const iscsi_connection *conn, uint8_t *buf * and value pair to an output DataSegment * buffer and truncates if necessary. * - * @param[in] key_value_pair Pointer to key and value pair containing - * its attributes. + * @param[in] number true = int, false = char* * @param[in] key Pointer to key to be written to output * buffer. NULL is NOT allowed, take caution. * @param[in] value Pointer to value of the key that should @@ -3167,20 +2564,20 @@ static int32_t iscsi_connection_write(const iscsi_connection *conn, uint8_t *buf * @param[in] pos Position of buffer in bytes to start * writing to. * @param[in] buflen Total length of buffer in bytes. - * @return Bytes added to buffer, or negative - * error code. + * @return -1 if buffer is already full, otherwise the number + * of bytes that are written or would have been written to + * the buffer. */ -static int iscsi_append_key_value_pair_packet(const char *key, const char *value, uint8_t *buf, const uint32_t pos, const uint32_t buflen) +static int iscsi_append_key_value_pair_packet(const bool number, const char *key, const char *value, char *buf, const uint32_t pos, const uint32_t buflen) { if ( pos >= buflen ) return -1; const ssize_t maxlen = buflen - pos; - const ssize_t ret = snprintf( (char *) (buf + pos), maxlen, "%s=%s", key, value ) + 1; - - if ( ret > maxlen ) - return -1; - return (int)ret; + if ( number ) { + return (int)snprintf( (buf + pos), maxlen, "%s=%d", key, (const int)(const size_t)value ) + 1; + } + return (int)snprintf( (buf + pos), maxlen, "%s=%s", key, value ) + 1; } @@ -3203,28 +2600,11 @@ static int iscsi_append_key_value_pair_packet(const char *key, const char *value * @retval 0 All values have been updated successfully and * the socket is still alive. */ -static int iscsi_connection_update_key_value_pairs(iscsi_connection *conn, iscsi_login_kvp *pairs) +static void iscsi_connection_update_key_value_pairs(iscsi_connection *conn, iscsi_negotiation_kvp *pairs) { conn->session->opts.MaxBurstLength = CLAMP(pairs->MaxBurstLength, 512, ISCSI_MAX_DS_SIZE); conn->session->opts.FirstBurstLength = CLAMP(pairs->FirstBurstLength, 512, pairs->MaxBurstLength); conn->session->opts.MaxRecvDataSegmentLength = CLAMP(pairs->MaxRecvDataSegmentLength, 512, ISCSI_MAX_DS_SIZE); - - if ( conn->client->sock == -1 ) - return -1; - - int recv_buf_len = conn->session->opts.FirstBurstLength; - - if ( recv_buf_len < 4096 ) - recv_buf_len = 4096; - else if ( recv_buf_len > 8192 ) - recv_buf_len = 8192; - - recv_buf_len += (sizeof(struct iscsi_bhs_packet) + ISCSI_MAX_AHS_SIZE + conn->header_digest + conn->data_digest); // BHS + maximum AHS size + header and data digest overhead - recv_buf_len *= 16; // Receive up to 16 streams at once. - - setsockopt( conn->client->sock, SOL_SOCKET, SO_RCVBUF, &recv_buf_len, sizeof(recv_buf_len)); // Not being able to set the buffer is NOT fatal, so ignore error. - - return 0; } /** @@ -3236,42 +2616,35 @@ static int iscsi_connection_update_key_value_pairs(iscsi_connection *conn, iscsi * @param[in] conn Pointer to ISCSI connection to send the TCP/IP * packet with. May NOT be NULL, so be * careful. - * @param[in] login_response_pdu Pointer to login response PDU to + * @param[in] resp_pdu Pointer to login response PDU to * be sent via TCP/IP. NULL is NOT * allowed here, take caution. * @return 0 if the login response has been sent * successfully, a negative error code otherwise. */ -static int iscsi_connection_pdu_login_response(iscsi_connection *conn, iscsi_pdu *login_response_pdu) +static int iscsi_connection_pdu_login_response(iscsi_connection *conn, iscsi_pdu *resp_pdu) { - const uint32_t ds_len = login_response_pdu->ds_len; - - login_response_pdu->ds_len = login_response_pdu->len; - - iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) iscsi_connection_pdu_append( login_response_pdu, login_response_pdu->ahs_len, 0, ds_len, 0 ); + iscsi_login_response_packet *login_response_pkt = + (iscsi_login_response_packet *) iscsi_connection_pdu_resize( resp_pdu, resp_pdu->ahs_len, resp_pdu->ds_write_pos ); login_response_pkt->version_max = ISCSI_VERSION_MAX; login_response_pkt->version_active = ISCSI_VERSION_MAX; - iscsi_put_be32( (uint8_t *) &login_response_pkt->total_ahs_len, ds_len ); // TotalAHSLength is always 0 and DataSegmentLength is 24-bit, so write in one step. + iscsi_put_be32( (uint8_t *) &login_response_pkt->total_ahs_len, resp_pdu->ds_len ); // TotalAHSLength is always 0 and DataSegmentLength is 24-bit, so write in one step. iscsi_put_be32( (uint8_t *) &login_response_pkt->stat_sn, conn->stat_sn++ ); if ( conn->session != NULL ) { // TODO: Needed? MC/S? iscsi_put_be32( (uint8_t *) &login_response_pkt->exp_cmd_sn, conn->session->exp_cmd_sn ); iscsi_put_be32( (uint8_t *) &login_response_pkt->max_cmd_sn, conn->session->max_cmd_sn ); } else { - iscsi_put_be32( (uint8_t *) &login_response_pkt->exp_cmd_sn, login_response_pdu->cmd_sn ); - iscsi_put_be32( (uint8_t *) &login_response_pkt->max_cmd_sn, login_response_pdu->cmd_sn ); + iscsi_put_be32( (uint8_t *) &login_response_pkt->exp_cmd_sn, resp_pdu->cmd_sn ); + iscsi_put_be32( (uint8_t *) &login_response_pkt->max_cmd_sn, resp_pdu->cmd_sn ); } if ( login_response_pkt->status_class != ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SUCCESS ) login_response_pkt->flags &= (int8_t) ~(ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT | ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_MASK | ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_MASK ); - iscsi_connection_pdu_write( conn, login_response_pdu ); - - if ( conn->state < ISCSI_CONNECT_STATE_EXITING && (conn->flags & ISCSI_CONNECT_FLAGS_FULL_FEATURE) != 0 ) { - iscsi_connection_update_key_value_pairs( conn ); - } + iscsi_connection_pdu_write( conn, resp_pdu ); return ISCSI_CONNECT_PDU_READ_OK; } @@ -3295,8 +2668,6 @@ static int iscsi_connection_pdu_login_response_init(iscsi_pdu *login_response_pd iscsi_login_req_packet *login_req_pkt = (iscsi_login_req_packet *) pdu->bhs_pkt; iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - login_response_pdu->ds_len = 0UL; - login_response_pkt->opcode = ISCSI_OPCODE_SERVER_LOGIN_RES; login_response_pkt->flags = (int8_t) (login_req_pkt->flags & (ISCSI_LOGIN_REQ_FLAGS_TRANSIT | ISCSI_LOGIN_REQ_FLAGS_CONTINUE | ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_MASK)); @@ -3342,41 +2713,6 @@ static int iscsi_connection_pdu_login_response_init(iscsi_pdu *login_response_pd } /** - * @brief Saves incoming key / value pairs from the client of a login request PDU. - * - * The login response structure has status detail - * invalid login request type set in case of an error. - * - * @param[in] conn Pointer to iSCSI connection - * used for key and value pair extraction. - * @param[out] key_value_pairs Pointer to hash map which - * stores all the parsed key and value pairs. - * @param[in] login_response_pdu Pointer to iSCSI login response - * PDU, may NOT be NULL, so be careful. - * @param[in] pdu Pointer to iSCSI login request packet - * PDU, may NOT be NULL, so be careful. - * @retval -1 An error occured during parse of - * key and value pairs (memory exhaustion). - * @retval 0 All key and value pairs have been parsed successfully. - */ -static int iscsi_connection_save_incoming_key_value_pairs(iscsi_connection *conn, iscsi_login_kvp *key_value_pairs, iscsi_pdu *login_response_pdu, const iscsi_pdu *pdu) -{ - iscsi_login_req_packet *login_req_pkt = (iscsi_login_req_packet *) pdu->bhs_pkt; - iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - const int rc = iscsi_parse_login_key_value_pairs( key_value_pairs, (uint8_t *) pdu->ds_cmd_data, pdu->ds_len, - ((login_req_pkt->flags & ISCSI_LOGIN_REQ_FLAGS_CONTINUE) != 0), &conn->partial_pairs ); - - if ( rc < 0 ) { - login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; - login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; - - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER; - } - - return ISCSI_CONNECT_PDU_READ_OK; -} - -/** * @brief Determines the session type of login. * * This function is used to retrieve the @@ -3393,13 +2729,17 @@ static int iscsi_connection_save_incoming_key_value_pairs(iscsi_connection *conn * otherwise. The output session 'type' is unchanged, if * an invalid session type value was retrieved. */ -static int iscsi_login_check_session_type(iscsi_pdu *login_response_pdu, const char *type_str ) +static int iscsi_login_parse_session_type(iscsi_pdu *login_response_pdu, const char *type_str, int *type) { iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - if ( type_str != NULL && strcasecmp( type_str, "Normal" ) == 0 ) + if ( type_str != NULL && strcasecmp( type_str, "Normal" ) == 0 ) { + *type = ISCSI_SESSION_TYPE_NORMAL; return ISCSI_CONNECT_PDU_READ_OK; + } + *type = ISCSI_SESSION_TYPE_INVALID; + logadd( LOG_DEBUG1, "Unsupported session type: %s", type_str ); login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISSING_PARAMETER; @@ -3426,11 +2766,60 @@ static int iscsi_login_check_session_type(iscsi_pdu *login_response_pdu, const c * @return 0 if the check was successful or a negative * error code otherwise. */ -static int iscsi_connection_login_check_target(iscsi_connection *conn, iscsi_pdu *login_response_pdu, uint8_t *target_name) +static int iscsi_image_from_target(iscsi_connection *conn, iscsi_pdu *login_response_pdu, const char *target_name) { iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - dnbd3_image_t *image = iscsi_target_node_image_get( target_name ); + char *image_rev = NULL; + char *tmpbuf = strdup( target_name ); + char *image_name = tmpbuf; + char *tmp = strchr( tmpbuf, ':' ); + + if ( tmpbuf == NULL ) { + logadd( LOG_ERROR, "iscsi_target_node_image_get: Out of memory while allocating DNBD3 image name for iSCSI target node" ); + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SERVER_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_OUT_OF_RESOURCES; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } + + while ( tmp != NULL ) { + *tmp++ = '\0'; + if ( image_rev != NULL ) { + image_name = image_rev; + } + image_rev = tmp; + tmp = strchr( tmp, ':' ); + } + + uint16_t rev = 0; + if ( image_rev != NULL ) { + char *end = NULL; + long rid = strtol( image_rev, &end, 10 ); + if ( end == NULL || *end != '\0' || rid < 0 || rid > 0xFFFF ) { + logadd( LOG_DEBUG1, "iscsi_image_from_target: Invalid revision number (%s) in iSCSI target node name: '%s'", image_rev, target_name ); + } else { + rev = (uint16_t)rid; + } + } + dnbd3_image_t *image = image_getOrLoad( image_name, rev ); + + if ( image == NULL && image_rev != NULL ) { + image = image_getOrLoad( image_rev, rev ); + } + + if ( image == NULL && strncasecmp( image_name, ISCSI_TARGET_NODE_WWN_NAME_PREFIX, ISCSI_STRLEN(ISCSI_TARGET_NODE_WWN_NAME_PREFIX) ) == 0 ) { + uint64_t wwn = strtoull( (image_name + ISCSI_STRLEN(ISCSI_TARGET_NODE_WWN_NAME_PREFIX)), NULL, 16 ); + + image = image_getByWwn( wwn, rev, true ); + + if ( image == NULL ) { + wwn = strtoull( (tmp + ISCSI_STRLEN(ISCSI_TARGET_NODE_WWN_NAME_PREFIX)), NULL, 16 ); + image = image_getByWwn( wwn, rev, true ); + } + } + + free( tmpbuf ); if ( image == NULL ) { login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; @@ -3485,57 +2874,51 @@ static void iscsi_connection_login_response_reject(iscsi_pdu *login_response_pdu * @param[in] conn Pointer to connection to link the PDU with. * If this is NULL the connection has to be * linked later. - * @param[in] ahs_len Length of AHS packet data to be appended. - * @param[in] header_digest_size Length of header digest. Currently, - * only 0, in which case the header digest will - * be removed, or 4 for CRC32C are allowed. * @param[in] ds_len Length of DataSegment packet data to be appended. * May not exceed 16MiB - 1 (16777215 bytes). - * @param[in] data_digest_size Length of optional data digest (0 or - * 4 for now) to add. * @return Pointer to allocated and zero filled PDU or NULL * in case of an error (usually memory exhaustion). */ -static iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint ahs_len, const int header_digest_size, const uint32_t ds_len, const int data_digest_size) +static iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint32_t ds_len) { - if ( (ahs_len > ISCSI_MAX_AHS_SIZE) || ((header_digest_size != 0) && (header_digest_size != ISCSI_DIGEST_SIZE)) || ((data_digest_size != 0) && data_digest_size != ISCSI_DIGEST_SIZE) || (ds_len > ISCSI_MAX_DS_SIZE) ) + if ( ds_len > ISCSI_MAX_DS_SIZE ) { + logadd( LOG_ERROR, "iscsi_pdu_create: Invalid DS length" ); return NULL; + } - const uint32_t pkt_ds_len = ISCSI_ALIGN(ds_len, ISCSI_ALIGN_SIZE); - const uint32_t len = (uint32_t) (sizeof(struct iscsi_bhs_packet) + (uint32_t) ahs_len + header_digest_size + pkt_ds_len + ((pkt_ds_len != 0UL) ? (uint32_t) data_digest_size : 0UL)); - - iscsi_pdu *pdu = malloc( sizeof(struct iscsi_pdu) + len ); + const uint32_t pkt_ds_len = ISCSI_ALIGN( ds_len, ISCSI_ALIGN_SIZE ); + const uint32_t len = (uint32_t) ( sizeof(struct iscsi_bhs_packet) + pkt_ds_len ); + iscsi_pdu *pdu = malloc( sizeof(struct iscsi_pdu) ); if ( pdu == NULL ) { logadd( LOG_ERROR, "iscsi_pdu_create: Out of memory while allocating iSCSI PDU" ); return NULL; } - iscsi_bhs_packet *bhs_pkt = (iscsi_bhs_packet *)(pdu + 1); + iscsi_bhs_packet *bhs_pkt = malloc( len ); + if ( bhs_pkt == NULL ) { + free( pdu ); + logadd( LOG_ERROR, "iscsi_pdu_create: Out of memory while allocating iSCSI BHS packet" ); + + return NULL; + } pdu->bhs_pkt = bhs_pkt; - pdu->ahs_pkt = (ahs_len != 0U) ? (iscsi_ahs_packet *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet) ) : NULL; - pdu->header_digest = (header_digest_size != 0) ? (iscsi_header_digest *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet) + ahs_len) : NULL; - pdu->ds_cmd_data = (pkt_ds_len != 0UL) ? (iscsi_scsi_ds_cmd_data *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet) + ahs_len + header_digest_size) : NULL; - pdu->data_digest = ((pkt_ds_len != 0uL) && (data_digest_size != 0)) ? (iscsi_data_digest *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet) + ahs_len + header_digest_size + ISCSI_ALIGN(pkt_ds_len, ISCSI_ALIGN_SIZE)) : NULL; + pdu->ahs_pkt = NULL; + pdu->ds_cmd_data = (pkt_ds_len != 0UL) ? (iscsi_scsi_ds_cmd_data *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet)) : NULL; pdu->task = NULL; pdu->flags = 0; pdu->bhs_pos = 0U; - pdu->ahs_pos = 0U; - pdu->ahs_len = ahs_len; - pdu->header_digest_pos = 0U; - pdu->header_digest_size = header_digest_size; - pdu->ds_len = pkt_ds_len; - pdu->pos = 0UL; - pdu->len = pkt_ds_len; - pdu->data_digest_pos = 0U; - pdu->data_digest_size = data_digest_size; - pdu->task_ref_cnt = 0U; + pdu->ahs_len = 0; + pdu->ds_len = ds_len; + pdu->ds_write_pos = 0; pdu->cmd_sn = 0UL; + pdu->recv_pos = 0; - if ( pkt_ds_len != 0UL ) + if ( pkt_ds_len != 0UL ) { memset( (((uint8_t *) pdu->ds_cmd_data) + ds_len), 0, (pkt_ds_len - ds_len) ); + } return pdu; } @@ -3552,6 +2935,9 @@ static iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint */ static void iscsi_connection_pdu_destroy(iscsi_pdu *pdu) { + if ( pdu == NULL ) + return; + free( pdu->bhs_pkt ); free( pdu ); } @@ -3566,32 +2952,29 @@ static void iscsi_connection_pdu_destroy(iscsi_pdu *pdu) * the packet data to. May NOT be NULL, so * be careful. * @param[in] ahs_len Length of AHS packet data to be appended. - * @param[in] header_digest_size Length of header digest. Currently, - * only 0, in which case the header digest will - * be removed, or 4 for CRC32C are allowed. * @param[in] ds_len Length of DataSegment packet data to be appended. * May not exceed 16MiB - 1 (16777215 bytes). - * @param[in] data_digest_size Length of optional data digest (0 or - * 4 for now) to add. * @return Pointer to allocated and zero filled PDU or NULL * in case of an error (usually memory exhaustion). */ -static iscsi_bhs_packet *iscsi_connection_pdu_append(iscsi_pdu *pdu, const uint ahs_len, const int header_digest_size, const uint32_t ds_len, const int data_digest_size) +static iscsi_bhs_packet *iscsi_connection_pdu_resize(iscsi_pdu *pdu, const uint ahs_len, const uint32_t ds_len) { - if ( (ahs_len > ISCSI_MAX_AHS_SIZE) || ((header_digest_size != 0) && (header_digest_size != ISCSI_DIGEST_SIZE)) || ((data_digest_size != 0) && data_digest_size != ISCSI_DIGEST_SIZE) || (ds_len > ISCSI_MAX_DS_SIZE) ) + if ( (ahs_len > ISCSI_MAX_AHS_SIZE) || (ds_len > ISCSI_MAX_DS_SIZE) || (ahs_len % 4 != 0) ) { + logadd( LOG_ERROR, "iscsi_connection_pdu_resize: Invalid AHS or DataSegment packet size" ); return NULL; + } - if ( (ahs_len != pdu->ahs_len) || (header_digest_size != pdu->header_digest_size) || (ds_len != pdu->ds_len) || (data_digest_size != pdu->data_digest_size) ) { + if ( (ahs_len != pdu->ahs_len) || (ds_len != pdu->ds_len) ) { iscsi_bhs_packet *bhs_pkt; const uint32_t pkt_ds_len = ISCSI_ALIGN(ds_len, ISCSI_ALIGN_SIZE); - const uint32_t old_len = (uint32_t) (sizeof(struct iscsi_bhs_packet) + (uint32_t) pdu->ahs_len + pdu->header_digest_size + pdu->ds_len + ((pdu->ds_len != 0UL) ? (uint32_t) pdu->data_digest_size : 0UL)); - const uint32_t new_len = (uint32_t) (sizeof(struct iscsi_bhs_packet) + (uint32_t) ahs_len + header_digest_size + pkt_ds_len + ((pkt_ds_len != 0UL) ? (uint32_t) data_digest_size : 0UL)); + const uint32_t old_len = (uint32_t) (sizeof(struct iscsi_bhs_packet) + (uint32_t) pdu->ahs_len + ISCSI_ALIGN(pdu->ds_len, ISCSI_ALIGN_SIZE)); + const uint32_t new_len = (uint32_t) (sizeof(struct iscsi_bhs_packet) + (uint32_t) ahs_len + pkt_ds_len); if ( new_len > old_len ) { bhs_pkt = realloc( pdu->bhs_pkt, new_len ); if ( bhs_pkt == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_pdu_append: Out of memory while reallocating iSCSI PDU packet data" ); + logadd( LOG_ERROR, "iscsi_connection_pdu_resize: Out of memory while reallocating iSCSI PDU packet data" ); return NULL; } @@ -3601,182 +2984,19 @@ static iscsi_bhs_packet *iscsi_connection_pdu_append(iscsi_pdu *pdu, const uint bhs_pkt = pdu->bhs_pkt; } - pdu->ahs_pkt = (ahs_len != 0U) ? (iscsi_ahs_packet *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet) ) : NULL; - pdu->header_digest = (header_digest_size != 0) ? (iscsi_header_digest *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet) + ahs_len) : NULL; - pdu->ds_cmd_data = (pkt_ds_len != 0UL) ? (iscsi_scsi_ds_cmd_data *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet) + ahs_len + header_digest_size) : NULL; - pdu->data_digest = ((pkt_ds_len != 0UL) && (data_digest_size != 0)) ? (iscsi_data_digest *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet) + ahs_len + header_digest_size + pkt_ds_len) : NULL; + pdu->ahs_pkt = (ahs_len != 0U) ? (iscsi_ahs_packet *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet)) : NULL; + pdu->ds_cmd_data = (pkt_ds_len != 0UL) ? (iscsi_scsi_ds_cmd_data *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet) + ahs_len) : NULL; pdu->ahs_len = ahs_len; - pdu->header_digest_size = header_digest_size; - pdu->ds_len = pkt_ds_len; - pdu->len = pkt_ds_len; - pdu->data_digest_size = data_digest_size; + pdu->ds_len = ds_len; - if ( pkt_ds_len != 0UL ) + if ( pkt_ds_len != 0UL ) { memset( (((uint8_t *) pdu->ds_cmd_data) + ds_len), 0, (pkt_ds_len - ds_len) ); + } } return pdu->bhs_pkt; } -/// CRC32C lookup table. Created with a polynomial reflect value of 0x82F63B78. -static const uint32_t crc32c_lut[] = { - 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, - 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, - 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, - 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, - 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, - 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, - 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, - 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, - 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, - 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, - 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, - 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, - 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, - 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, - 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, - 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, - 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, - 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, - 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, - 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, - 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, - 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, - 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, - 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, - 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, - 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, - 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, - 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, - 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, - 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, - 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, - 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351}; - -/** - * @brief Calculates digest (CRC32C). - * - * Calculates CRC32C with 0x82F63B78 polynomial - * reflect according to iSCSI specs.\n - * TODO: Implement optimized SSE4.2 and ARM versions - * - * @param[in] data Pointer to data to calculate CRC32C for. - * @param[in] len Length of data to be calculated. Must be - * divisable by 4 which is guaranteed by iSCSI standard. - * @param[in] crc32c Previous CRC32C in case of multiple passes. - * @return CRC32C value. THis function cannot fail. - */ -static inline uint32_t iscsi_crc32c_update(const uint8_t *data, const uint len, uint32_t crc32c) -{ - for ( uint i = 0; i < len - 3; i += 4 ) { - crc32c = (crc32c >> 8UL) ^ crc32c_lut[(crc32c ^ data[i]) & 0xFF]; - crc32c = (crc32c >> 8UL) ^ crc32c_lut[(crc32c ^ data[i + 1]) & 0xFF]; - crc32c = (crc32c >> 8UL) ^ crc32c_lut[(crc32c ^ data[i + 2]) & 0xFF]; - crc32c = (crc32c >> 8UL) ^ crc32c_lut[(crc32c ^ data[i + 3]) & 0xFF]; - } - - return crc32c; -} - -/** - * @brief Calculate and store iSCSI header digest (CRC32C). - * - * Calculates header digest (CRC32C) with - * 0x82F63B78 polynomial reflect according - * to iSCSI specs and stores the result in - * the iSCSI packet data. This function - * cannot fail. - * - * @param[out] header_digest Pointer to iSCSI header digest - * packet data to put CRC32C into. - * May NOT be NULL, so be careful. - * @param[in] packet_data Pointer to ISCSI BHS packet to - * calculate CRC32C for. NULL is NOT - * allowed here, take caution. - * @param[in] ahs_len AHS segment length in bytes. - */ -static void iscsi_connection_pdu_digest_header_update(iscsi_header_digest *header_digest, const iscsi_bhs_packet *packet_data, const uint ahs_len) -{ - const uint32_t crc32c = iscsi_crc32c_update( (uint8_t *) packet_data, (sizeof(struct iscsi_bhs_packet) + ahs_len), ISCSI_CRC32C_INITIAL ) ^ ISCSI_CRC32C_XOR; - - iscsi_put_le32( (uint8_t *) &header_digest->crc32c, crc32c ); -} - -/** - * @brief Validates a stored iSCSI header digest (CRC32C) with actual header data. - * - * Verifies header digest (CRC32C) with - * 0x82F63B78 polynomial reflect according - * to iSCSI specs. This function cannot - * fail. - * - * @param[in] header_digest Pointer to iSCSI header digest - * packet data to compare CRC32C with. - * May NOT be NULL, so be careful. - * @param[in] packet_data Pointer to ISCSI BHS packet to - * validate CRC32C for. May NOT be NULL, - * so be careful. - * @param[in] ahs_len AHS segment length in bytes. - * @retval true CRC32C matches the stored value. - * @retval false CRC32C does NOT match the stored value. - */ -static bool iscsi_connection_pdu_digest_header_verify(const iscsi_header_digest *header_digest, const iscsi_bhs_packet *packet_data, const uint ahs_len) -{ - const uint32_t crc32c = iscsi_crc32c_update( (uint8_t *) packet_data, (sizeof(struct iscsi_bhs_packet) + ahs_len), ISCSI_CRC32C_INITIAL ) ^ ISCSI_CRC32C_XOR; - - return (iscsi_get_le32(crc32c) == header_digest->crc32c); -} - -/** - * @brief Calculate iSCSI data digest (CRC32C). - * - * Calculates data digest (CRC32) with - * 0x82F63B78 polynomial reflect of a - * whole DataSegment (CRC32C) according - * to the iSCSI specs.\n - * The resulting CRC32C will be stored - * in the iSCSI packet. - * - * @param[out] data_digest Pointer to iSCSI data digest - * packet data to put CRC32C into. - * May NOT be NULL, so be careful. - * @param[in] ds_cmd_data Pointer to iSCSI DataSegment packet to - * calculate CRC32C for. NULL is NOT - * allowed here, take caution. - * @param[in] ds_len Data segment length in bytes. - */ -static void iscsi_connection_pdu_digest_data_update(iscsi_data_digest *data_digest, const iscsi_scsi_ds_cmd_data *ds_cmd_data, const uint32_t ds_len) -{ - const uint32_t crc32c = iscsi_crc32c_update( (uint8_t *) ds_cmd_data, ISCSI_ALIGN(ds_len, ISCSI_DIGEST_SIZE), ISCSI_CRC32C_INITIAL ) ^ ISCSI_CRC32C_XOR; - - iscsi_put_le32( (uint8_t *) &data_digest->crc32c, crc32c ); -} - -/** - * @brief Validates a stored iSCSI data digest (CRC32C) with actual DataSegment. - * - * Verifies data digest (CRC32C) with - * 0x82F63B78 polynomial reflect according - * to iSCSI specs. This function cannot - * fail. - * - * @param[out] data_digest Pointer to iSCSI data digest - * packet data to compare CRC32C with. - * May NOT be NULL, so be careful. - * @param[in] ds_cmd_data Pointer to iSCSI DataSegment - * packet to calculate CRC32C for. May NOT - * be NULL, so be careful. - * @param[in] ds_len Data segment length in bytes. - * @retval true CRC32C matches the stored value. - * @retval false CRC32C does NOT match the stored value. - */ -static bool iscsi_connection_pdu_digest_data_verify(const iscsi_data_digest *data_digest, const iscsi_scsi_ds_cmd_data *ds_cmd_data, const uint32_t ds_len) -{ - const uint32_t crc32c = iscsi_crc32c_update( (uint8_t *) ds_cmd_data, ISCSI_ALIGN(ds_len, ISCSI_DIGEST_SIZE), ISCSI_CRC32C_INITIAL ) ^ ISCSI_CRC32C_XOR; - - return (iscsi_get_le32(crc32c) == data_digest->crc32c); -} - /** * @brief Writes and sends a response PDU to the client. * @@ -3793,27 +3013,23 @@ static bool iscsi_connection_pdu_digest_data_verify(const iscsi_data_digest *dat */ static bool iscsi_connection_pdu_write(iscsi_connection *conn, iscsi_pdu *pdu) { - if ( ISCSI_GET_OPCODE(pdu->bhs_pkt->opcode) != ISCSI_OPCODE_CLIENT_LOGIN_REQ ) { - if ( pdu->header_digest != NULL ) - iscsi_connection_pdu_digest_header_update( pdu->header_digest, pdu->bhs_pkt, pdu->ahs_len ); - - if ( pdu->data_digest != NULL ) - iscsi_connection_pdu_digest_data_update( pdu->data_digest, pdu->ds_cmd_data, pdu->ds_len ); - } - if ( conn->state >= ISCSI_CONNECT_STATE_EXITING ) { iscsi_connection_pdu_destroy( pdu ); return false; } - const uint len = (uint) (sizeof(struct iscsi_bhs_packet) + pdu->ahs_len + conn->header_digest + ISCSI_ALIGN(pdu->ds_len, ISCSI_ALIGN_SIZE) + conn->data_digest); + // During allocation we already round up to ISCSI_ALIGN_SIZE, but store the requested size in the ds_len + // member, so it's safe to round up here before sending, the accessed memory will be valid and zeroed + const size_t len = (sizeof(struct iscsi_bhs_packet) + pdu->ahs_len + ISCSI_ALIGN(pdu->ds_len, ISCSI_ALIGN_SIZE)); + const ssize_t rc = sock_sendAll( conn->client->sock, pdu->bhs_pkt, len, ISCSI_CONNECT_SOCKET_WRITE_RETRIES ); - bool ok = iscsi_connection_write( conn, (uint8_t *) pdu->bhs_pkt, len ) > 0; iscsi_connection_pdu_destroy( pdu ); - if (!ok) { + + if ( rc != (ssize_t)len ) { conn->state = ISCSI_CONNECT_STATE_EXITING; + return false; } - return ok; + return true; } /** @@ -3874,7 +3090,7 @@ static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu pdu->flags |= ISCSI_PDU_FLAGS_REJECTED; const uint32_t ds_len = (uint32_t) sizeof(struct iscsi_bhs_packet) + ((uint32_t) pdu->bhs_pkt->total_ahs_len << 2UL); - iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 0U, conn->header_digest, ds_len, conn->data_digest ); + iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, ds_len ); if ( response_pdu == NULL ) { logadd( LOG_ERROR, "iscsi_connection_handle_reject: Out of memory while allocating iSCSI reject response PDU" ); @@ -3990,7 +3206,7 @@ static int iscsi_connection_pdu_header_handle_login_req(iscsi_connection *conn, if ( pdu->ds_len > ISCSI_DEFAULT_RECV_DS_LEN ) return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); - iscsi_pdu *login_response_pdu = iscsi_connection_pdu_create( conn, 0U, 0, ISCSI_DEFAULT_RECV_DS_LEN, 0 ); + iscsi_pdu *login_response_pdu = iscsi_connection_pdu_create( conn, 8192 ); if ( login_response_pdu == NULL ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; @@ -4097,11 +3313,10 @@ static int iscsi_connection_pdu_header_handle_scsi_cmd(iscsi_connection *conn, i task->scsi_task.flags |= ISCSI_SCSI_TASK_FLAGS_XFER_READ; if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_WRITE) != 0 ) { - return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); + return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_COMMAND_NOT_SUPPORTED ); } pdu->task = task; - pdu->task_ref_cnt++; return ISCSI_CONNECT_PDU_READ_OK; } @@ -4123,8 +3338,7 @@ static int iscsi_connection_pdu_header_handle_scsi_cmd(iscsi_connection *conn, i */ static int iscsi_connection_pdu_header_handle_text_req(iscsi_connection *conn, iscsi_pdu *pdu) { - if ( pdu->ds_len > (uint) (sizeof(struct iscsi_bhs_packet) + ISCSI_MAX_AHS_SIZE + conn->header_digest - + ISCSI_SESSION_DEFAULT_FIRST_BURST_LEN + conn->data_digest) ) + if ( pdu->ds_len > (uint) (sizeof(struct iscsi_bhs_packet) + ISCSI_DEFAULT_RECV_DS_LEN) ) return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); iscsi_text_req_packet *text_req_pkt = (iscsi_text_req_packet *) pdu->bhs_pkt; @@ -4169,7 +3383,7 @@ static int iscsi_connection_pdu_header_handle_logout_req(iscsi_connection *conn, if ( (conn->session != NULL) && (conn->session->type == ISCSI_SESSION_TYPE_DISCOVERY) && (logout_req_pkt->reason_code != ISCSI_LOGOUT_REQ_REASON_CODE_CLOSE_SESSION) ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 0U, conn->header_digest, 0UL, conn->data_digest ); + iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 0UL ); if ( response_pdu == NULL ) { logadd( LOG_ERROR, "iscsi_connection_pdu_header_handle_logout_req: Out of memory while allocating iSCSI logout response PDU" ); @@ -4245,7 +3459,7 @@ static int iscsi_connection_pdu_header_handle(iscsi_connection *conn, iscsi_pdu return iscsi_connection_pdu_header_handle_login_req( conn, pdu ); if ( ((conn->flags & ISCSI_CONNECT_FLAGS_FULL_FEATURE) == 0) && (conn->state == ISCSI_CONNECT_STATE_RUNNING) ) { - iscsi_pdu *login_response_pdu = iscsi_connection_pdu_create( conn, 0U, 0, 0UL, 0 ); + iscsi_pdu *login_response_pdu = iscsi_connection_pdu_create( conn, 0UL ); if ( login_response_pdu == NULL ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; @@ -4326,7 +3540,7 @@ static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscs if ( init_task_tag == 0xFFFFFFFFUL ) return ISCSI_CONNECT_PDU_READ_OK; - iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 0U, conn->header_digest, ds_len, conn->data_digest ); + iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, ds_len ); if ( response_pdu == NULL ) { logadd( LOG_ERROR, "iscsi_connection_pdu_data_handle_nop_out: Out of memory while allocating iSCSI NOP-In response PDU" ); @@ -4401,34 +3615,6 @@ static int iscsi_connection_pdu_data_handle_scsi_cmd(iscsi_connection *conn, isc return ISCSI_CONNECT_PDU_READ_OK; } -/** - * @brief Determines iSCSI session login steps for normal authentication. - * - * This function also does related validation checks. - * - * @param[in] conn Pointer to iSCSI connection, may NOT be - * NULL, so take caution. - * @param[in] login_response_pdu Pointer to login response PDU where - * NULL is not allowed, so be careful. - * @param[in] target_name Pointer to hash map containing the login - * request key and value pairs and may NOT be NULL, so - * take caution. - * @return 0 on successful operation, a negative error code - * otherwise. - */ -static int iscsi_connection_login_session_normal(iscsi_connection *conn, iscsi_pdu *login_response_pdu, const char *target_name) -{ - int rc = iscsi_connection_login_check_target( conn, login_response_pdu, target_name ); - - if ( rc != 0 ) - return rc; - - conn->flags &= ~(ISCSI_CONNECT_FLAGS_CHAP_REQUIRE | ISCSI_CONNECT_FLAGS_CHAP_MUTUAL); - conn->flags |= ISCSI_CONNECT_FLAGS_CHAP_DISABLE; - - return rc; -} - /* * This function is used to set the info in the connection data structure * return @@ -4470,23 +3656,23 @@ static int iscsi_connection_login_set_info(iscsi_connection *conn, iscsi_pdu *lo * may NOT be NULL, so be careful. * @param[in] login_response_pdu Pointer to login response PDU. * NULL is not allowed here, so take caution. - * @param[in] key_value_pairs Pointer to key and value pairs. + * @param[in] kvpairs Pointer to key and value pairs. * which may NOT be NULL, so take caution. * @param[in] cid Connection ID (CID). * @return 0 on success, a negative error code otherwise. */ -static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_login_kvp *key_value_pairs, uint cid) +static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_negotiation_kvp *kvpairs, uint cid) { int type, rc; iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - rc = iscsi_login_check_session_type( login_response_pdu, key_value_pairs->AuthMethod ); + rc = iscsi_login_parse_session_type( login_response_pdu, kvpairs->SessionType, &type ); if ( rc < 0 ) return rc; - if ( type == ISCSI_SESSION_TYPE_NORMAL ) { - rc = iscsi_connection_login_session_normal( conn, login_response_pdu, key_value_pairs ); + if ( kvpairs->TargetName != NULL && type == ISCSI_SESSION_TYPE_NORMAL ) { + rc = iscsi_image_from_target( conn, login_response_pdu, kvpairs->TargetName ); } else { login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISSING_PARAMETER; @@ -4500,120 +3686,105 @@ static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscs return iscsi_connection_login_set_info( conn, login_response_pdu, type, cid ); } +static int iscsi_write_login_options_to_pdu( iscsi_connection *conn, iscsi_negotiation_kvp *pairs, iscsi_pdu *response_pdu ) +{ + uint payload_len = response_pdu->ds_write_pos; + +# define ADD_KV_INTERNAL(num, key, value) do { \ +int rc = iscsi_append_key_value_pair_packet( num, key, value, (char *)response_pdu->ds_cmd_data, payload_len, response_pdu->ds_len ); \ +if ( rc < 0 ) return -1; \ +payload_len += rc; \ +} while (0) +# define ADD_KV_OPTION_INT(key) do { \ +if ( pairs->key != -1 ) ADD_KV_INTERNAL( true, #key, (const char *)(size_t)conn->session->opts.key ); \ +} while (0) +# define ADD_KV_OPTION_STR(key) do { \ +if ( pairs->key != NULL ) ADD_KV_INTERNAL( false, #key, conn->session->opts.key ); \ +} while (0) +# define ADD_KV_PLAIN_INT(key, value) do { \ +if ( pairs->key != -1 ) ADD_KV_INTERNAL( true, #key, (const char *)(size_t)(value) ); \ +} while (0) +# define ADD_KV_PLAIN_STR(key, value) do { \ +if ( pairs->key != NULL ) ADD_KV_INTERNAL( false, #key, value ); \ +} while (0) + ADD_KV_OPTION_INT( MaxRecvDataSegmentLength ); + ADD_KV_OPTION_INT( MaxBurstLength ); + ADD_KV_OPTION_INT( FirstBurstLength ); + ADD_KV_PLAIN_INT( MaxConnections, 1 ); + ADD_KV_PLAIN_INT( ErrorRecoveryLevel, 0 ); + ADD_KV_PLAIN_STR( HeaderDigest, "None" ); + ADD_KV_PLAIN_STR( DataDigest, "None" ); +# undef ADD_KV_PLAIN +# undef ADD_KV_OPTION_INT +# undef ADD_KV_OPTION_STR + + if ( payload_len <= response_pdu->ds_len ) { + response_pdu->ds_write_pos = payload_len; + } else { + response_pdu->ds_write_pos = response_pdu->ds_len; + } + return (int)payload_len; +} + /** - * @brief Handles the Current Stage (CSG) bit of login response. + * @brief Handles iSCSI connection login response. * - * This function determines the authentication method - * and processes the various authentication stages. + * This function negotiates the login parameters + * and determines the authentication method. * - * @param[in] conn Pointer to iSCSI connection, may NOT be - * NULL, so take caution. + * @param[in] conn Pointer to iSCSI connection, + * may NOT be NULL, so be careful. * @param[in] login_response_pdu Pointer to login response PDU. - * NULL is NOT an allowed value here, so be careful. - * @param[in] auth_method auth method requested, or NULL if not specified - * @return 0 if authentication has been handled successfully, - * a negative error code otherwise. + * NULL is not allowed here, so take caution. + * @return 0 on success, a negative error code otherwise. */ -static int iscsi_connecction_handle_login_response_csg_bit(iscsi_connection *conn, iscsi_pdu *login_response_pdu, char *auth_method) +static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_negotiation_kvp *pairs) { iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; + iscsi_connection_update_key_value_pairs( conn, pairs ); + int payload_len = iscsi_write_login_options_to_pdu( conn, pairs, login_response_pdu ); - switch ( ISCSI_LOGIN_RESPONSE_FLAGS_GET_CURRENT_STAGE(login_response_pkt->flags) ) { - case ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_SECURITY_NEGOTIATION : { - if ( auth_method != NULL && strcasecmp( auth_method, "None" ) == 0 ) { - conn->flags |= ISCSI_CONNECT_FLAGS_AUTH; - } else { - login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; - login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_ERR; - - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - } - - break; - } - case ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION : { - if ( conn->state == ISCSI_CONNECT_STATE_INVALID ) { - if ( conn->flags & ISCSI_CONNECT_FLAGS_CHAP_REQUIRE ) { - login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; - login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_ERR; - - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - } else { - conn->flags |= ISCSI_CONNECT_FLAGS_AUTH; - } - } - - if ( (conn->flags & ISCSI_CONNECT_FLAGS_AUTH) == 0 ) { - login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; - login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_ERR; + if ( payload_len < 0 || payload_len > login_response_pdu->ds_len ) { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SERVER_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_OUT_OF_RESOURCES; - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - } + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } - break; - } - case ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FULL_FEATURE_PHASE : - default : { + // Handle current stage (CSG bits) + switch ( ISCSI_LOGIN_RESPONSE_FLAGS_GET_CURRENT_STAGE(login_response_pkt->flags) ) { + case ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_SECURITY_NEGOTIATION : { + if ( pairs->AuthMethod != NULL && strcasecmp( pairs->AuthMethod, "None" ) == 0 ) { + conn->flags |= ISCSI_CONNECT_FLAGS_AUTH; + } else { + // Only "None" supported login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; - login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_ERR; return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - - break; } - } - return ISCSI_CONNECT_PDU_READ_OK; -} - -/** - * - * @brief Checks whether the session type is valid. - * - * This function also can be used to output - * debugging info about the session, which - * is currently not implemented. - * - * @param[in] conn Pointer to iSCSI connection to be checked for - * validity. May NOT be NULL, so be careful. - * @param[in] login_response_pdu Pointer to login response PDU to - * set status class and detail in case of an error. - * NULL is not an allowed value here, take caution. - * @return 0 if the session type is valid, a negative error code - * otherwise. - */ -static int iscsi_connection_login_session_info_notify(iscsi_connection *conn, iscsi_pdu *login_response_pdu) -{ - if ( (conn->session->type != ISCSI_SESSION_TYPE_NORMAL) && (conn->session->type != ISCSI_SESSION_TYPE_DISCOVERY) ) { - iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; + break; + } + case ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION : { + if ( conn->state == ISCSI_CONNECT_STATE_INVALID ) { + conn->flags |= ISCSI_CONNECT_FLAGS_AUTH; + } + break; + } + case ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FULL_FEATURE_PHASE : + default : { login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; } + } - return ISCSI_CONNECT_PDU_READ_OK; -} - -/** - * @brief Handles the Transit (T) bit of login response. - * - * This function handles the transitional stages - * of the authentication process. - * - * @param[in] conn Pointer to iSCSI connection, may NOT be - * NULL, so take caution. - * @param[in] login_response_pdu Pointer to login response PDU. - * NULL is NOT an allowed value here, so be careful. - * @return 0 if transition has been handled successfully, - * a negative error code otherwise. - */ -static int iscsi_connecction_handle_login_response_t_bit(iscsi_connection *conn, iscsi_pdu *login_response_pdu) -{ - iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - - switch ( ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) ) { + if ( (login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0 ) { + // Client set the transition bit - requests to move on to next stage + switch ( ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) ) { case ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION : { conn->login_phase = ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION; @@ -4629,10 +3800,15 @@ static int iscsi_connecction_handle_login_response_t_bit(iscsi_connection *conn, iscsi_put_be16( (uint8_t *) &login_response_pkt->tsih, (uint16_t) conn->session->tsih ); - const int rc = iscsi_connection_login_session_info_notify( conn, login_response_pdu ); + if ( (conn->session->type != ISCSI_SESSION_TYPE_NORMAL) && (conn->session->type != ISCSI_SESSION_TYPE_DISCOVERY) ) { + logadd( LOG_DEBUG1, "Unsupported session type %d, rejecting", conn->session->type ); + iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - if ( rc < 0 ) - return rc; + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } conn->flags |= ISCSI_CONNECT_FLAGS_FULL_FEATURE; @@ -4643,8 +3819,7 @@ static int iscsi_connecction_handle_login_response_t_bit(iscsi_connection *conn, login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - - break; + } } } @@ -4652,48 +3827,6 @@ static int iscsi_connecction_handle_login_response_t_bit(iscsi_connection *conn, } /** - * @brief Handles iSCSI connection login response. - * - * This function negotiates the login parameters - * and determines the authentication method. - * - * @param[in] conn Pointer to iSCSI connection, - * may NOT be NULL, so be careful. - * @param[in] login_response_pdu Pointer to login response PDU. - * NULL is not allowed here, so take caution. - * @param[in] key_value_pairs Pointer to key and value pairs. - * which may NOT be NULL, so take caution. - * @return 0 on success, a negative error code otherwise. - */ -static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_login_kvp *key_value_pairs) -{ - iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - - - // TODO: Handle KVPs - int ds_len = -1; - - if ( ds_len < 0L ) { - login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; - login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_ERR; - - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - } - - login_response_pdu->ds_len = (uint32_t) ds_len; - - int rc = iscsi_connecction_handle_login_response_csg_bit( conn, login_response_pdu, key_value_pairs->AuthMethod ); - - 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; -} - -/** * @brief Handles an incoming iSCSI payload data login request PDU. * * This function handles login request payload @@ -4710,6 +3843,7 @@ static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi */ static int iscsi_connection_pdu_data_handle_login_req(iscsi_connection *conn, iscsi_pdu *pdu) { + int rc; iscsi_pdu *login_response_pdu = (iscsi_pdu *) conn->login_response_pdu; if ( login_response_pdu == NULL ) @@ -4717,16 +3851,20 @@ static int iscsi_connection_pdu_data_handle_login_req(iscsi_connection *conn, is iscsi_login_req_packet *login_req_pkt = (iscsi_login_req_packet *) pdu->bhs_pkt; uint cid = iscsi_get_be16(login_req_pkt->cid); - int rc = iscsi_connection_save_incoming_key_value_pairs( conn, NULL, login_response_pdu, pdu ); - if ( rc < 0 ) { - iscsi_connection_pdu_login_response( conn, login_response_pdu ); + iscsi_negotiation_kvp pairs; + iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; + rc = iscsi_parse_login_key_value_pairs( &pairs, (uint8_t *) pdu->ds_cmd_data, pdu->ds_len ); - return ISCSI_CONNECT_PDU_READ_OK; + if ( rc < 0 || rc > login_response_pdu->ds_len ) { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_ERR; + + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; } if ( conn->state == ISCSI_CONNECT_STATE_INVALID ) { - rc = iscsi_connection_handle_login_phase_none( conn, login_response_pdu, NULL, cid ); + rc = iscsi_connection_handle_login_phase_none( conn, login_response_pdu, &pairs, cid ); if ( (rc == ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE) || (rc == ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER) ) { iscsi_connection_pdu_login_response( conn, login_response_pdu ); @@ -4735,7 +3873,7 @@ static int iscsi_connection_pdu_data_handle_login_req(iscsi_connection *conn, is } } - rc = iscsi_connecction_handle_login_response( conn, login_response_pdu, NULL ); + rc = iscsi_connecction_handle_login_response( conn, login_response_pdu, &pairs ); if ( rc == ISCSI_CONNECT_PDU_READ_OK ) { conn->state = ISCSI_CONNECT_STATE_RUNNING; @@ -4764,14 +3902,14 @@ static int iscsi_connection_pdu_data_handle_login_req(iscsi_connection *conn, is static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, iscsi_pdu *pdu) { iscsi_text_req_packet *text_req_pkt = (iscsi_text_req_packet *) pdu->bhs_pkt; - int rc = iscsi_parse_login_key_value_pairs( NULL, (uint8_t *) pdu->ds_cmd_data, pdu->ds_len, ((text_req_pkt->flags & ISCSI_TEXT_REQ_FLAGS_CONTINUE) != 0), &conn->text_partial_pairs ); + iscsi_negotiation_kvp pairs; + int rc = iscsi_parse_login_key_value_pairs( &pairs, (uint8_t *) pdu->ds_cmd_data, pdu->ds_len ); if ( rc < 0 ) { return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } - iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 0U, conn->header_digest, - conn->session->opts.MaxRecvDataSegmentLength, conn->data_digest ); + iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 8192 ); if ( response_pdu == NULL ) { logadd( LOG_ERROR, "iscsi_connection_pdu_data_handle_text_req: Out of memory while allocating iSCSI text response PDU" ); @@ -4779,33 +3917,24 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } - response_pdu->ds_len = 0UL; + iscsi_connection_update_key_value_pairs( conn, &pairs ); - // TODO: Write stuff to reply packet - int ds_len = -1; + int payload_len = iscsi_write_login_options_to_pdu( conn, &pairs, response_pdu ); - if ( ds_len < 0L ) { + if ( payload_len < 0 || payload_len > response_pdu->ds_len ) { iscsi_connection_pdu_destroy( response_pdu ); return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } - iscsi_text_response_packet *text_response_pkt = (iscsi_text_response_packet *) response_pdu->bhs_pkt; + iscsi_text_response_packet *text_response_pkt = (iscsi_text_response_packet *) iscsi_connection_pdu_resize( response_pdu, 0, response_pdu->ds_write_pos ); text_response_pkt->opcode = ISCSI_OPCODE_SERVER_TEXT_RES; - text_response_pkt->flags = 0; - - if ( (text_req_pkt->flags & ISCSI_TEXT_REQ_FLAGS_CONTINUE) != 0 ) - text_response_pkt->flags |= (int8_t) ISCSI_TEXT_RESPONSE_FLAGS_CONTINUE; - - if ( (text_req_pkt->flags & ISCSI_TEXT_REQ_FLAGS_FINAL) != 0 ) - text_response_pkt->flags |= (int8_t) ISCSI_TEXT_RESPONSE_FLAGS_FINAL; + text_response_pkt->flags = (int8_t) ISCSI_TEXT_RESPONSE_FLAGS_FINAL; - text_response_pkt->reserved = 0U; + text_response_pkt->reserved = 0; - text_response_pkt = (iscsi_text_response_packet *) iscsi_connection_pdu_append( response_pdu, response_pdu->ahs_len, conn->header_digest, ds_len, conn->data_digest ); - - iscsi_put_be32( (uint8_t *) &text_response_pkt->total_ahs_len, ds_len ); // TotalAHSLength is always 0 and DataSegmentLength is 24-bit, so write in one step. + iscsi_put_be32( (uint8_t *) &text_response_pkt->total_ahs_len, payload_len ); // TotalAHSLength is always 0 and DataSegmentLength is 24-bit, so write in one step. text_response_pkt->lun = text_req_pkt->lun; // Copying over doesn't change endianess. text_response_pkt->init_task_tag = text_req_pkt->init_task_tag; // Copying over doesn't change endianess. @@ -4828,7 +3957,6 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc text_response_pkt->reserved2[1] = 0ULL; iscsi_connection_pdu_write( conn, response_pdu ); - iscsi_connection_update_key_value_pairs( conn ); return ISCSI_CONNECT_PDU_READ_OK; } @@ -4897,12 +4025,14 @@ static int iscsi_connection_pdu_data_handle(iscsi_connection *conn, iscsi_pdu *p * @brief Retrieves and merges splitted iSCSI PDU data read from TCP/IP socket. * * This function handles partial reads of data - * packet.\n - * Since iSCSI data can span multiple packets, not - * only by TCP/IP itself, but also by iSCSI protocol - * specifications, multiple calls are needed in order - * to be sure that all data packets have been - * received. + * packets.\n + * The function is guaranteed to read as many bytes as indicated by the + * PDU struct, unless the read timeout is reached, or the connection + * is closed/reset.\n + * Care is also taken for padding bytes that have to be read. It is + * assumed the according buffer will have enough space for the padding + * bytes, which is always guaranteed when using the create and resize + * helpers for allocating PDUs. * * @param[in] conn Pointer to iSCSI connection to read TCP/IP data from. * @param[in] pdu Pointer to iSCSI PDU to read TCP/IP data into. @@ -4912,36 +4042,19 @@ static int iscsi_connection_pdu_data_handle(iscsi_connection *conn, iscsi_pdu *p */ static int iscsi_connection_pdu_data_read(iscsi_connection *conn, iscsi_pdu *pdu) { - const uint32_t ds_len = pdu->ds_len; + const uint32_t ds_len = ISCSI_ALIGN( pdu->ds_len, ISCSI_ALIGN_SIZE ); - if ( pdu->pos < ds_len ) { - const int32_t len = iscsi_connection_read( conn, (((uint8_t *) pdu->ds_cmd_data) + pdu->pos), (ds_len - pdu->pos) ); + if ( pdu->recv_pos < ds_len ) { + const int32_t len = iscsi_connection_read( conn, (((uint8_t *) pdu->ds_cmd_data) + pdu->recv_pos), (ds_len - pdu->recv_pos) ); if ( len < 0L ) return len; - pdu->pos += len; + pdu->recv_pos += len; } - if ( pdu->pos < ds_len ) - return ISCSI_CONNECT_PDU_READ_PROCESSED; - - if ( pdu->data_digest != NULL ) { - if ( (int) pdu->data_digest_pos < pdu->data_digest_size ) { - const int32_t len = iscsi_connection_read( conn, (((uint8_t *) pdu->data_digest) + pdu->data_digest_pos), (pdu->data_digest_size - pdu->data_digest_pos) ); - - if ( len < 0L ) - return len; - - pdu->data_digest_pos += len; - - if ( (int) pdu->data_digest_pos < pdu->data_digest_size ) - return ISCSI_CONNECT_PDU_READ_OK; - } - - if ( !iscsi_connection_pdu_digest_data_verify( pdu->data_digest, pdu->ds_cmd_data, ds_len ) ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } + if ( pdu->recv_pos < ds_len ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; return ISCSI_CONNECT_PDU_READ_OK; } @@ -4974,7 +4087,7 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn) switch ( conn->pdu_recv_state ) { case ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_READY : { assert( conn->pdu_processing == NULL ); - conn->pdu_processing = iscsi_connection_pdu_create( conn, 0U, conn->header_digest, 0UL, conn->data_digest ); + conn->pdu_processing = iscsi_connection_pdu_create( conn, 0UL ); if ( conn->pdu_processing == NULL ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; @@ -4984,75 +4097,47 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn) break; } case ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_HDR : { - if ( pdu->bhs_pos < sizeof(struct iscsi_bhs_packet) ) { + while ( pdu->bhs_pos < sizeof(struct iscsi_bhs_packet) ) { const int32_t len = iscsi_connection_read( conn, (((uint8_t *) pdu->bhs_pkt) + pdu->bhs_pos), (sizeof(struct iscsi_bhs_packet) - pdu->bhs_pos) ); if ( len < 0L ) { conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; - break; + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } pdu->bhs_pos += len; - - if ( pdu->bhs_pos < sizeof(struct iscsi_bhs_packet) ) - return ISCSI_CONNECT_PDU_READ_OK; } if ( (conn->flags & ISCSI_CONNECT_FLAGS_LOGGED_OUT) != 0 ) { conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; - break; + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } iscsi_bhs_packet *bhs_pkt = pdu->bhs_pkt; - const uint ahs_len = ((uint) bhs_pkt->total_ahs_len << 2U); + const uint ahs_len = ((uint) bhs_pkt->total_ahs_len * 4); const uint32_t ds_len = iscsi_get_be24(bhs_pkt->ds_len); - bhs_pkt = iscsi_connection_pdu_append( pdu, ahs_len, conn->header_digest, ds_len, conn->data_digest ); + bhs_pkt = iscsi_connection_pdu_resize( pdu, ahs_len, ds_len ); if ( bhs_pkt == NULL ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - if ( pdu->ahs_pos < ahs_len ) { - const int32_t len = iscsi_connection_read( conn, (((uint8_t *) pdu->ahs_pkt) + pdu->ahs_pos), (ahs_len - pdu->ahs_pos) ); + int pos = 0; + while ( pos < ahs_len ) { + const int32_t len = iscsi_connection_read( conn, (((uint8_t *) pdu->ahs_pkt) + pos), (ahs_len - pos) ); if ( len < 0L ) { conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; - break; - } - - pdu->ahs_pos += len; - - if ( pdu->ahs_pos < ahs_len ) - return ISCSI_CONNECT_PDU_READ_OK; - } - - if ( pdu->header_digest != NULL ) { - if ( (int) pdu->header_digest_pos < pdu->header_digest_size ) { - const int32_t len = iscsi_connection_read( conn, (((uint8_t *) pdu->header_digest) + pdu->header_digest_pos), (pdu->header_digest_size - pdu->header_digest_pos) ); - - if ( len < 0L ) { - conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; - - break; - } - - pdu->header_digest_pos += len; - - if ( (int) pdu->header_digest_pos < pdu->header_digest_size ) - return ISCSI_CONNECT_PDU_READ_OK; + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } - if ( !iscsi_connection_pdu_digest_header_verify( pdu->header_digest, bhs_pkt, ahs_len ) ) { - conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; - - break; - } + pos += len; } - if ((iscsi_connection_pdu_header_handle( conn, pdu ) < 0)) { + if ( iscsi_connection_pdu_header_handle( conn, pdu ) < 0 ) { conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; } else { conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_DATA; @@ -5067,7 +4152,7 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn) if ( len < 0 ) { conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; - break; + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } else if ( len > 0 ) { return ISCSI_CONNECT_PDU_READ_OK; } @@ -5138,7 +4223,7 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ return; } - conn->pdu_processing = iscsi_connection_pdu_create( conn, 0U, 0, 0UL, 0 ); + conn->pdu_processing = iscsi_connection_pdu_create( conn, 0UL ); if ( conn->pdu_processing == NULL ) { iscsi_connection_destroy( conn ); |
