diff options
| -rw-r--r-- | inc/dnbd3/shared/sockhelper.h | 2 | ||||
| -rw-r--r-- | src/server/iscsi.c | 1540 | ||||
| -rw-r--r-- | src/server/iscsi.h | 129 | ||||
| -rw-r--r-- | src/shared/sockhelper.c | 2 |
4 files changed, 617 insertions, 1056 deletions
diff --git a/inc/dnbd3/shared/sockhelper.h b/inc/dnbd3/shared/sockhelper.h index aa482fa..728ebfc 100644 --- a/inc/dnbd3/shared/sockhelper.h +++ b/inc/dnbd3/shared/sockhelper.h @@ -120,6 +120,6 @@ ssize_t sock_recv(const int sock, void *buffer, const size_t len); /** * Send a desired number of nullbytes to socket. */ -bool sock_sendPadding(int fd, uint32_t bytes); +bool sock_sendPadding(int fd, size_t bytes); #endif /* SOCKHELPER_H_ */ diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 668b8a0..9734a4b 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -69,6 +69,8 @@ //#define malloc(x) (rand() % 100 == 0 ? NULL : malloc(x)) +// Use for stack-allocated iscsi_pdu +#define CLEANUP_PDU __attribute__((cleanup(iscsi_connection_pdu_destroy))) static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task); @@ -77,7 +79,7 @@ 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 +static void iscsi_scsi_task_xfer_complete(iscsi_scsi_task *scsi_task, iscsi_pdu *request_pdu); // Callback function when an iSCSI SCSI task completed the data transfer static void iscsi_scsi_task_lun_process_none(iscsi_scsi_task *scsi_task); // Processes a iSCSI SCSI task with no LUN identifier @@ -85,33 +87,25 @@ static void iscsi_scsi_task_lun_process_none(iscsi_scsi_task *scsi_task); // Pro static uint64_t iscsi_scsi_lun_get_from_scsi(const int lun_id); // Converts an internal representation of a LUN identifier to an iSCSI LUN required for packet data static int iscsi_scsi_lun_get_from_iscsi(const uint64_t lun); // Converts an iSCSI LUN from packet data to internal SCSI LUN identifier -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, 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 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 - static iscsi_task *iscsi_task_create(iscsi_connection *conn); // Allocates and initializes an iSCSI task structure static void iscsi_task_destroy(iscsi_task *task); // Deallocates resources acquired by iscsi_task_create -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 iscsi_session *iscsi_session_create(iscsi_connection *conn, const int type); // Creates and initializes an iSCSI session +static iscsi_session *iscsi_session_create(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 *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 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 uint32_t ds_len, bool no_ds_alloc); -static void iscsi_connection_pdu_destroy(iscsi_pdu *pdu); // Destroys an iSCSI PDU structure used by connections +static bool iscsi_connection_pdu_init(iscsi_pdu *pdu, const uint32_t ds_len, bool no_ds_alloc); +static void iscsi_connection_pdu_destroy(iscsi_pdu *pdu); 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 @@ -145,6 +139,23 @@ static void iscsi_strcpy_pad(char *dst, const char *src, const size_t size, cons } } +/** + * @brief Parses a string representation of an integer and assigns the result to + * the provided destination variable, ensuring it is within valid range. + * + * This function checks for duplicate entries, empty strings, non-numeric + * characters, and out-of-range values. Logs debug messages for invalid or + * duplicate inputs and ensures values are clamped between 0 and INT_MAX. + * + * @param[in] name The name of the key associated with the integer value. + * Used for logging purposes. + * @param[in, out] dest Pointer to the destination integer variable where the + * parsed value will be stored. Must not be NULL. If the pointed + * value is -1, the parsed value will be assigned; otherwise, + * the function considers it a duplicate and does not update it. + * @param[in] src Pointer to the string containing the numeric representation + * of the value to parse. Must not be NULL or empty. + */ static void iscsi_copy_kvp_int(const char *name, int *dest, const char *src) { long long res = 0; @@ -178,6 +189,18 @@ static void iscsi_copy_kvp_int(const char *name, int *dest, const char *src) *dest = (int)res; } +/** + * @brief Copies a key-value pair string to the destination if it hasn't been copied already. + * + * This function ensures that a key has a single corresponding value by + * checking if the destination pointer has already been assigned. If assigned, + * a debug log entry is created, and the new value is ignored. + * + * @param[in] name The name of the key being assigned. Used for logging. + * @param[in,out] dest Pointer to the destination where the string is to be copied. + * If the destination is already assigned, the function will log and return. + * @param[in] src Pointer to the source string to be assigned to the destination. + */ static void iscsi_copy_kvp_str(const char *name, const char **dest, const char *src) { if ( *dest != NULL ) { @@ -278,7 +301,7 @@ static int iscsi_parse_text_key_value_pair(iscsi_negotiation_kvp *key_value_pair * @param[in] len Length of the remaining packet data. * @retval -1 An error occured during parsing key. * @retval 0 Key and value pair was parsed successfully and was added to - * hash map. + * kvp struct. */ static int iscsi_parse_login_key_value_pairs(iscsi_negotiation_kvp *pairs, const uint8_t *packet_data, uint len) { @@ -389,22 +412,16 @@ static void iscsi_task_destroy(iscsi_task *task) * @pararm[in] res_snt Residual Count. * @pararm[in] data_sn Data Sequence Number (DataSN). * @pararm[in] flags Flags for this data packet. - * @return Next Data Sequence Number (DataSN) on success, - * the same DataSN as passed on error. + * @return true success, false error */ -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) +static bool 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, len, true ); - - if ( response_pdu == NULL ) { - logadd( LOG_ERROR, "iscsi_scsi_data_in_send: Out of memory while allocating iSCSI SCSI Data In response PDU" ); - - return data_sn; - } - - response_pdu->task = task; + iscsi_pdu CLEANUP_PDU response_pdu; + if ( !iscsi_connection_pdu_init( &response_pdu, len, true ) ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - iscsi_scsi_data_in_response_packet *scsi_data_in_pkt = (iscsi_scsi_data_in_response_packet *) response_pdu->bhs_pkt; + iscsi_scsi_data_in_response_packet *scsi_data_in_pkt = (iscsi_scsi_data_in_response_packet *) response_pdu.bhs_pkt; scsi_data_in_pkt->opcode = ISCSI_OPCODE_SERVER_SCSI_DATA_IN; scsi_data_in_pkt->flags = (flags & ~(ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_UNDERFLOW | ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_OVERFLOW)); @@ -441,22 +458,18 @@ static uint32_t iscsi_scsi_data_in_send(iscsi_connection *conn, iscsi_task *task iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->buf_offset, pos ); - iscsi_connection_pdu_write( conn, response_pdu ); + iscsi_connection_pdu_write( conn, &response_pdu ); if ( task->scsi_task.buf != NULL ) { - if ( !sock_sendAll( conn->client->sock, (task->scsi_task.buf + pos), len, ISCSI_CONNECT_SOCKET_WRITE_RETRIES ) ) { - // Set error - return data_sn; - } - if ( len % ISCSI_ALIGN_SIZE != 0 ) { - const size_t padding = ISCSI_ALIGN_SIZE - (len % ISCSI_ALIGN_SIZE); - if ( !sock_sendPadding( conn->client->sock, padding ) ) { - // Set error - return data_sn; - } + if ( !sock_sendAll( conn->client->sock, (task->scsi_task.buf + pos), len, ISCSI_CONNECT_SOCKET_WRITE_RETRIES ) ) + return false; + const size_t padding = ISCSI_ALIGN( len, ISCSI_ALIGN_SIZE ) - len; + if ( padding != 0 ) { + if ( !sock_sendPadding( conn->client->sock, padding ) ) + return false; } } else { - const off_t off = task->scsi_task.file_offset + pos; + const uint64_t off = task->scsi_task.file_offset + pos; size_t padding = 0; size_t realBytes = len; if ( off >= conn->client->image->realFilesize ) { @@ -467,20 +480,16 @@ static uint32_t iscsi_scsi_data_in_send(iscsi_connection *conn, iscsi_task *task realBytes -= padding; } bool ret = sendfile_all( conn->client->image->readFd, conn->client->sock, - off, realBytes ); - if ( !ret ) { - // Set error - return data_sn; - } + (off_t)off, realBytes ); + if ( !ret ) + return false; if ( padding > 0 ) { - if ( !sock_sendPadding( conn->client->sock, padding ) ) { - // Set error - return data_sn; - } + if ( !sock_sendPadding( conn->client->sock, padding ) ) + return false; } } - return (data_sn + 1UL); + return true; } /** @@ -525,7 +534,7 @@ static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, iscsi_task *task uint32_t data_sn = task->data_sn; uint32_t max_burst_offset = 0UL; const uint32_t max_burst_len = conn->session->opts.MaxBurstLength; - const uint32_t data_in_seq_count = ((xfer_len - 1UL) / max_burst_len) + 1UL; + const uint32_t data_in_seq_count = ((xfer_len - 1) / max_burst_len) + 1; int8_t status = 0; for ( uint32_t i = 0UL; i < data_in_seq_count; i++ ) { @@ -551,7 +560,10 @@ static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, iscsi_task *task } } - data_sn = iscsi_scsi_data_in_send( conn, task, offset, len, res_cnt, data_sn, flags, immediate ); + if ( !iscsi_scsi_data_in_send( conn, task, offset, len, res_cnt, data_sn, flags, immediate ) ) + return -1; + + data_sn++; } max_burst_offset += max_burst_len; @@ -563,63 +575,82 @@ static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, iscsi_task *task } /** - * @brief Creates, initializes and sends an iSCSI task reponse PDU. + * @brief Initializes a SCSI task. * - * This function also receives any remaining - * incoming data in case the task is reading. + * @param[in] scsi_task Pointer to SCSI task. This + * may NOT be NULL, so be careful. + */ +static void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task) +{ + scsi_task->cdb = NULL; + scsi_task->sense_data = NULL; + scsi_task->buf = NULL; + scsi_task->must_free = true; + scsi_task->len = 0UL; + scsi_task->id = 0ULL; + scsi_task->flags = 0; + scsi_task->xfer_pos = 0UL; + scsi_task->xfer_len = 0UL; + scsi_task->sense_data_len = 0U; + scsi_task->status = ISCSI_SCSI_STATUS_GOOD; +} + +/** + * @brief Callback function when an iSCSI SCSI task completed the data transfer. * - * @param[in] conn Pointer to iSCSI connection to handle the - * task resnponse for and may NOT be NULL, + * This function post-processes a task upon + * finish of data transfer. + * + * @param[in] scsi_task Pointer to iSCSI SCSI task which finished + * the data transfer and may NOT be NULL, * so be careful. - * @param[in] task Pointer to iSSCI task to create the - * response PDU from. NULL is NOT allowed - * here, take caution. + * @param request_pdu */ -static void iscsi_task_response(iscsi_connection *conn, iscsi_task *task, iscsi_pdu *pdu) +static void iscsi_scsi_task_xfer_complete(iscsi_scsi_task *scsi_task, iscsi_pdu *request_pdu) { - iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) pdu->bhs_pkt; - const uint32_t xfer_len = task->scsi_task.xfer_len; + iscsi_task *task = container_of( scsi_task, iscsi_task, scsi_task ); + iscsi_connection *conn = task->conn; + + task->des_data_xfer_pos += scsi_task->len; + + iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) request_pdu->bhs_pkt; + const uint32_t xfer_len = scsi_task->xfer_len; if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_READ) != 0 ) { - const int rc = iscsi_task_xfer_scsi_data_in( conn, task, (pdu->bhs_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) != 0 ); + const int rc = iscsi_task_xfer_scsi_data_in( conn, task, (scsi_cmd_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) != 0 ); - if ( (rc > 0) || (task->des_data_xfer_pos != task->scsi_task.xfer_len) ) + if ( (rc > 0) || (task->des_data_xfer_pos != scsi_task->xfer_len) ) return; } - 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)) + const uint32_t ds_len = (scsi_task->sense_data_len != 0U) + ? (scsi_task->sense_data_len + offsetof(struct iscsi_scsi_ds_cmd_data, sense_data)) : 0UL; - iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, ds_len, false ); - - if ( response_pdu == NULL ) { - logadd( LOG_ERROR, "iscsi_task_response: Out of memory while allocating iSCSI SCSI response PDU" ); + iscsi_pdu CLEANUP_PDU response_pdu; + if ( !iscsi_connection_pdu_init( &response_pdu, ds_len, false ) ) return; - } - iscsi_scsi_response_packet *scsi_response_pkt = (iscsi_scsi_response_packet *) response_pdu->bhs_pkt; + iscsi_scsi_response_packet *scsi_response_pkt = (iscsi_scsi_response_packet *) response_pdu.bhs_pkt; - if ( task->scsi_task.sense_data_len != 0U ) { - iscsi_scsi_ds_cmd_data *ds_cmd_data_pkt = response_pdu->ds_cmd_data; + if ( scsi_task->sense_data_len != 0U ) { + iscsi_scsi_ds_cmd_data *ds_cmd_data_pkt = response_pdu.ds_cmd_data; - iscsi_put_be16( (uint8_t *) &ds_cmd_data_pkt->len, task->scsi_task.sense_data_len ); - memcpy( ds_cmd_data_pkt->sense_data, task->scsi_task.sense_data, task->scsi_task.sense_data_len ); + iscsi_put_be16( (uint8_t *) &ds_cmd_data_pkt->len, scsi_task->sense_data_len ); + memcpy( ds_cmd_data_pkt->sense_data, scsi_task->sense_data, scsi_task->sense_data_len ); iscsi_put_be32( (uint8_t *) &scsi_response_pkt->total_ahs_len, ds_len ); // TotalAHSLength is always 0 and DataSegmentLength is 24-bit, so write in one step. } else { *(uint32_t *) &scsi_response_pkt->total_ahs_len = 0UL; // TotalAHSLength and DataSegmentLength are always 0, so write in one step. } - response_pdu->task = task; - scsi_response_pkt->opcode = ISCSI_OPCODE_SERVER_SCSI_RESPONSE; scsi_response_pkt->flags = -0x80; scsi_response_pkt->response = ISCSI_SCSI_RESPONSE_CODE_OK; - const uint32_t pos = task->scsi_task.xfer_pos; + const uint32_t pos = scsi_task->xfer_pos; - if ( (xfer_len != 0UL) && (task->scsi_task.status == ISCSI_SCSI_STATUS_GOOD) ) { + if ( (xfer_len != 0UL) && (scsi_task->status == ISCSI_SCSI_STATUS_GOOD) ) { if ( pos < xfer_len ) { const uint32_t res_cnt = (xfer_len - pos); @@ -637,7 +668,7 @@ static void iscsi_task_response(iscsi_connection *conn, iscsi_task *task, iscsi_ scsi_response_pkt->res_cnt = 0UL; } - scsi_response_pkt->status = task->scsi_task.status; + scsi_response_pkt->status = scsi_task->status; scsi_response_pkt->reserved = 0ULL; iscsi_put_be32( (uint8_t *) &scsi_response_pkt->init_task_tag, task->init_task_tag ); scsi_response_pkt->snack_tag = 0UL; @@ -651,50 +682,7 @@ static void iscsi_task_response(iscsi_connection *conn, iscsi_task *task, iscsi_ scsi_response_pkt->exp_data_sn = 0UL; scsi_response_pkt->bidi_read_res_cnt = 0UL; - iscsi_connection_pdu_write( conn, response_pdu ); -} - -/** - * @brief Initializes a SCSI task. - * - * @param[in] scsi_task Pointer to SCSI task. This - * may NOT be NULL, so be careful. - */ -static void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task) -{ - scsi_task->cdb = NULL; - scsi_task->sense_data = NULL; - scsi_task->buf = NULL; - scsi_task->must_free = true; - scsi_task->len = 0UL; - scsi_task->id = 0ULL; - scsi_task->flags = 0; - scsi_task->xfer_pos = 0UL; - scsi_task->xfer_len = 0UL; - scsi_task->sense_data_len = 0U; - scsi_task->status = ISCSI_SCSI_STATUS_GOOD; -} - -/** - * @brief Callback function when an iSCSI SCSI task completed the data transfer. - * - * This function post-processes a task upon - * finish of data transfer. - * - * @param[in] scsi_task Pointer to iSCSI SCSI task which finished - * the data transfer and may NOT be NULL, - * so be careful. - * @param pdu - */ -static void iscsi_scsi_task_xfer_complete(iscsi_scsi_task *scsi_task, iscsi_pdu *pdu) -{ - iscsi_task *task = container_of( scsi_task, iscsi_task, scsi_task ); - iscsi_connection *conn = task->conn; - - task->des_data_xfer_pos += task->scsi_task.len; - - iscsi_task_response( conn, task, pdu ); - iscsi_task_destroy( task ); + iscsi_connection_pdu_write( conn, &response_pdu ); } /** @@ -783,13 +771,8 @@ static void iscsi_scsi_task_status_set(iscsi_scsi_task *scsi_task, const uint8_t */ static void iscsi_scsi_task_lun_process_none(iscsi_scsi_task *scsi_task) { - iscsi_scsi_std_inquiry_data_packet std_inquiry_data_pkt; - iscsi_scsi_cdb_inquiry *cdb = (iscsi_scsi_cdb_inquiry *) scsi_task->cdb; - scsi_task->len = scsi_task->xfer_len; - iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_LU_NOT_SUPPORTED, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE ); - scsi_task->xfer_pos = 0UL; } @@ -850,58 +833,6 @@ static int iscsi_scsi_lun_get_from_iscsi(const uint64_t lun) } /** - * @brief Runs an iSCSI SCSI task for a specified iSCSI SCSI LUN. - * - * This function moves the task back to the - * iSCSI SCSI LUN tasks hash map prior - * execution.\n - * Errors are nandled according to the SCSI - * standard. - * - * @param[in] scsi_task Pointer to iSCSI SCSI task to be run. - * NULL is NOT valid here, take caution. - * @param pdu - */ -static void iscsi_scsi_lun_task_run( iscsi_scsi_task *scsi_task, iscsi_pdu *pdu) -{ - int rc; - - scsi_task->status = ISCSI_SCSI_STATUS_GOOD; - - rc = iscsi_scsi_emu_block_process( scsi_task ); - - if ( rc == ISCSI_SCSI_TASK_RUN_UNKNOWN ) { - rc = iscsi_scsi_emu_primary_process( scsi_task ); - - if ( rc == ISCSI_SCSI_TASK_RUN_UNKNOWN ) { - 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 ); - // TODO: Free task - rc = ISCSI_SCSI_TASK_RUN_COMPLETE; - } - } - - if ( rc == ISCSI_SCSI_TASK_RUN_COMPLETE ) { - iscsi_scsi_task_xfer_complete( scsi_task, pdu ); - } -} - -/** - * @brief Retrieves the size 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 block size. May NOT be NULL, - * so be careful. - * @return The physical block size in bytes. - */ -static inline uint32_t iscsi_scsi_emu_physical_block_get_size(const dnbd3_image_t *image) -{ - return ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE; -} - -/** * @brief Retrieves the number of total logical blocks for a DNBD3 image. * * This function depends on DNBD3 image @@ -1038,7 +969,7 @@ static int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, dnbd3_imag pthread_cond_init( &scsi_task->uplink_cond, NULL ); pthread_mutex_lock( &scsi_task->uplink_mutex ); - if ( !uplink_request( image, scsi_task, iscsi_uplink_callback, 0, offset_bytes, num_bytes ) ) { + if ( !uplink_request( image, scsi_task, iscsi_uplink_callback, 0, offset_bytes, (uint32_t)num_bytes ) ) { pthread_mutex_unlock( &scsi_task->uplink_mutex ); logadd( LOG_DEBUG1, "Could not relay uncached request to upstream proxy for image %s:%d", @@ -2429,7 +2360,7 @@ static uint64_t iscsi_target_node_wwn_get(const uint8_t *name) * @return Pointer to initialized iSCSI session or NULL in case an error * occured (usually due to memory exhaustion). */ -static iscsi_session *iscsi_session_create(iscsi_connection *conn, const int type) +static iscsi_session *iscsi_session_create(const int type) { iscsi_session *session = malloc( sizeof(struct iscsi_session) ); @@ -2443,7 +2374,6 @@ static iscsi_session *iscsi_session_create(iscsi_connection *conn, const int ty session->type = type; session->exp_cmd_sn = 0UL; session->max_cmd_sn = 0UL; - session->current_text_init_task_tag = 0xFFFFFFFFUL; return session; } @@ -2484,13 +2414,10 @@ static iscsi_connection *iscsi_connection_create(dnbd3_client_t *client) } conn->session = NULL; - conn->pdu_processing = NULL; - conn->login_response_pdu = NULL; conn->id = 0; conn->client = client; - conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_READY; conn->flags = 0; - conn->state = ISCSI_CONNECT_STATE_INVALID; + conn->state = ISCSI_CONNECT_STATE_NEW; conn->login_phase = ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION; conn->tsih = 0U; conn->cid = 0U; @@ -2519,41 +2446,12 @@ static iscsi_connection *iscsi_connection_create(dnbd3_client_t *client) static void iscsi_connection_destroy(iscsi_connection *conn) { if ( conn != NULL ) { - iscsi_connection_pdu_destroy( conn->login_response_pdu ); iscsi_session_destroy( conn->session ); - iscsi_connection_pdu_destroy( conn->pdu_processing ); free( conn ); } } /** - * @brief Reads data for the specified iSCSI connection from 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 read. - */ -static int32_t iscsi_connection_read(const iscsi_connection *conn, uint8_t *buf, const uint32_t len) -{ - if ( len == 0UL ) - return 0L; - - int32_t rc; - do { - rc = (int32_t) recv( conn->client->sock, buf, (size_t) len, MSG_WAITALL ); - } while ( rc == -1 && errno == EINTR ); - - if ( rc == 0 ) - return -1; // EOF - return rc; -} - -/** * @brief Appends a key and value pair to DataSegment packet data. * * This function adds any non-declarative key @@ -2630,7 +2528,7 @@ static void iscsi_connection_update_key_value_pairs(iscsi_connection *conn, iscs * @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 *resp_pdu) +static int iscsi_send_login_response_pdu(iscsi_connection *conn, iscsi_pdu *resp_pdu) { 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 ); @@ -2649,15 +2547,11 @@ static int iscsi_connection_pdu_login_response(iscsi_connection *conn, iscsi_pdu 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 ) + 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, resp_pdu ); - if ( conn->login_response_pdu == resp_pdu ) { - conn->login_response_pdu = NULL; } - return ISCSI_CONNECT_PDU_READ_OK; + return iscsi_connection_pdu_write( conn, resp_pdu ) ? 0 : -1; } /** @@ -2686,41 +2580,36 @@ static int iscsi_connection_pdu_login_response_init(iscsi_pdu *login_response_pd login_response_pkt->flags |= (login_req_pkt->flags & ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_MASK); login_response_pkt->isid = login_req_pkt->isid; - login_response_pkt->tsih = login_req_pkt->tsih; // Copying over doesn't change endianess.' + login_response_pkt->tsih = 0; login_response_pkt->init_task_tag = login_req_pkt->init_task_tag; // Copying over doesn't change endianess. login_response_pkt->reserved = 0UL; login_response_pdu->cmd_sn = iscsi_get_be32(login_req_pkt->cmd_sn); + login_response_pkt->stat_sn = 0UL; + login_response_pkt->reserved2 = 0U; + login_response_pkt->reserved3 = 0ULL; - if ( login_response_pkt->tsih != 0U ) - login_response_pkt->stat_sn = login_req_pkt->exp_stat_sn; // Copying over doesn't change endianess.' - else - login_response_pkt->stat_sn = 0UL; - - login_response_pkt->reserved2 = 0U; - login_response_pkt->reserved3 = 0ULL; - - if ( ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE) != 0) ) { + if ( login_req_pkt->tsih != 0 ) { + // Session resumption, not 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_SESSION_NO_EXIST; + } else if ( ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE) != 0) ) { login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; - - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - } else if ( (ISCSI_VERSION_MIN < login_req_pkt->version_min) || (ISCSI_VERSION_MAX > login_req_pkt->version_max) ) { + } else if ( (ISCSI_VERSION_MAX < login_req_pkt->version_min) || (ISCSI_VERSION_MIN > login_req_pkt->version_max) ) { login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_WRONG_VERSION; - - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; } else if ( (ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_RESERVED) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0) ) { login_response_pkt->flags &= (int8_t) ~(ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_MASK | ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT | ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_MASK); login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC; + } else { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SUCCESS; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SUCCESS; - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + return ISCSI_CONNECT_PDU_READ_OK; } - login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SUCCESS; - login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SUCCESS; - - return ISCSI_CONNECT_PDU_READ_OK; + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; } /** @@ -2876,82 +2765,78 @@ static void iscsi_connection_login_response_reject(iscsi_pdu *login_response_pdu } /** - * @brief Creates an iSCSI PDU structure used by connections. + * @brief Initializes an iSCSI Protocol Data Unit (PDU) object for use in iSCSI communication. * - * The PDU structure is used for allowing partial - * reading from the TCP/IP socket and correctly - * filling the data until everything has been read. + * Allocates and assigns the required memory for the Basic Header Segment (BHS) packet + * and optionally for the aligned data segment (DS). Resets and initializes various fields + * within the given PDU structure. Ensures proper memory alignment for data segment if + * applicable, and zeroes out unused buffer regions. * - * @param[in] conn Pointer to connection to link the PDU with. - * If this is NULL the connection has to be - * linked later. - * @param[in] ds_len Length of DataSegment packet data to be appended. - * May not exceed 16MiB - 1 (16777215 bytes). - * @param no_ds_alloc Do not allocate buffer space for DS, only set - * value for header - for sending DS manually later - * @return Pointer to allocated and zero filled PDU or NULL - * in case of an error (usually memory exhaustion). + * @param[in,out] pdu Pointer to the iSCSI PDU structure to initialize. Must not be NULL. + * @param[in] ds_len Length of the Data Segment (DS) in bytes. Must not exceed ISCSI_MAX_DS_SIZE. + * @param[in] no_ds_alloc If true, the Data Segment memory allocation is skipped. + * + * @retval true if initialization is successful. + * @retval false if memory allocation for the BHS packet fails or ds_len exceeds the maximum allowed size. */ -static iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint32_t ds_len, bool no_ds_alloc) +static bool iscsi_connection_pdu_init(iscsi_pdu *pdu, const uint32_t ds_len, bool no_ds_alloc) { + // Always set this pointer to NULL before any sanity checks, + // so the attribute-cleanup magic won't screw up if init fails + pdu->big_alloc = NULL; + if ( ds_len > ISCSI_MAX_DS_SIZE ) { - logadd( LOG_ERROR, "iscsi_pdu_create: Invalid DS length" ); - return NULL; + logadd( LOG_ERROR, "iscsi_pdu_init: Invalid DS length" ); + return false; } const uint32_t pkt_ds_len = no_ds_alloc ? 0 : 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 = malloc( len ); - if ( bhs_pkt == NULL ) { - free( pdu ); - logadd( LOG_ERROR, "iscsi_pdu_create: Out of memory while allocating iSCSI BHS packet" ); + const uint32_t alloc_len = (uint32_t) ( sizeof(struct iscsi_bhs_packet) + pkt_ds_len ); - return NULL; + if ( alloc_len > ISCSI_INTERNAL_BUFFER_SIZE ) { + pdu->bhs_pkt = pdu->big_alloc = malloc( alloc_len ); + if ( pdu->bhs_pkt == NULL ) { + logadd( LOG_ERROR, "iscsi_pdu_init: Out of memory while allocating iSCSI BHS packet" ); + return false; + } + } else { + pdu->bhs_pkt = (iscsi_bhs_packet *)pdu->internal_buffer; } - pdu->bhs_pkt = bhs_pkt; 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->ds_cmd_data = (pkt_ds_len != 0UL) + ? (iscsi_scsi_ds_cmd_data *) (((uint8_t *) pdu->bhs_pkt) + sizeof(struct iscsi_bhs_packet)) + : NULL; pdu->flags = 0; pdu->bhs_pos = 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 > ds_len ) { memset( (((uint8_t *) pdu->ds_cmd_data) + ds_len), 0, (pkt_ds_len - ds_len) ); } - return pdu; + return true; } /** - * @brief Destroys an iSCSI PDU structure used by connections. + * @brief Frees resources associated with an iSCSI PDU (Protocol Data Unit). * - * All associated data which has been read so - * far will be freed as well. + * This function releases memory allocated for certain members of the iSCSI + * PDU structure. It ensures that the allocated resources are properly freed. + * If the provided PDU pointer is NULL, the function returns immediately without + * performing any operations. * - * @param[in] pdu Pointer to PDU structure to be deallocated, - * may be NULL in which case this function - * does nothing. + * @param[in] pdu Pointer to the iSCSI PDU structure to be destroyed. + * If NULL, the function has no effect. */ static void iscsi_connection_pdu_destroy(iscsi_pdu *pdu) { if ( pdu == NULL ) return; - free( pdu->bhs_pkt ); - free( pdu ); + free( pdu->big_alloc ); } /** @@ -2972,34 +2857,54 @@ static void iscsi_connection_pdu_destroy(iscsi_pdu *pdu) */ 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) || (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 ( pdu->ds_len != 0 && pdu->ds_cmd_data == NULL ) { - // If you really ever need this, handle it properly below (old_len, no copying, etc.) - logadd( LOG_ERROR, "iscsi_connection_pdu_resize: Cannot resize PDU with virtual DS" ); - return NULL; - } - if ( (ahs_len != pdu->ahs_len) || (ds_len != pdu->ds_len) ) { + if ( (ahs_len > ISCSI_MAX_AHS_SIZE) || (ds_len > ISCSI_MAX_DS_SIZE) || (ahs_len % ISCSI_ALIGN_SIZE != 0) ) { + logadd( LOG_ERROR, "iscsi_connection_pdu_resize: Invalid AHS or DataSegment packet size" ); + return NULL; + } + if ( pdu->ds_len != 0 && pdu->ds_cmd_data == NULL ) { + // If you really ever need this, handle it properly below (old_len, no copying, etc.) + logadd( LOG_ERROR, "iscsi_connection_pdu_resize: Cannot resize PDU with virtual DS" ); + return NULL; + } + if ( pdu->ds_len != 0 && pdu->ahs_len != ahs_len && ds_len != 0 ) { + // Cannot resize the AHS of a PDU that already has a DS and should keep the DS - we'd need to move the data + // around. Implement this when needed (and make sure it works). + logadd( LOG_ERROR, "iscsi_connection_pdu_resize: Cannot resize PDU's AHS that also has a DS" ); + return NULL; + } + iscsi_bhs_packet *bhs_pkt; const uint32_t pkt_ds_len = ISCSI_ALIGN(ds_len, ISCSI_ALIGN_SIZE); - const size_t old_len = (sizeof(struct iscsi_bhs_packet) + (uint32_t) pdu->ahs_len + ISCSI_ALIGN(pdu->ds_len, ISCSI_ALIGN_SIZE)); - const size_t new_len = (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_resize: Out of memory while reallocating iSCSI PDU packet data" ); - - return NULL; - } + const size_t old_len = (sizeof(struct iscsi_bhs_packet) + (uint32_t) pdu->ahs_len + ISCSI_ALIGN(pdu->ds_len, ISCSI_ALIGN_SIZE)); + const size_t new_len = (sizeof(struct iscsi_bhs_packet) + (uint32_t) ahs_len + pkt_ds_len); + const bool old_alloced = pdu->big_alloc != NULL; + const bool new_alloced = new_len > ISCSI_INTERNAL_BUFFER_SIZE; - pdu->bhs_pkt = bhs_pkt; - } else { + if ( new_len == old_len ) { + // Nothing changed bhs_pkt = pdu->bhs_pkt; + } else { + if ( new_alloced ) { + // New block doesn't fit in internal buffer - (re)allocate big buffer + bhs_pkt = realloc( pdu->big_alloc, new_len ); + if ( bhs_pkt == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_pdu_resize: Out of memory while reallocating iSCSI PDU packet data" ); + return NULL; + } + if ( !old_alloced ) { + // Old was in internal buffer, copy contents + memcpy( bhs_pkt, pdu->internal_buffer, MIN(new_len, old_len) ); + } + // Update PDU's BHS pointer + pdu->big_alloc = bhs_pkt; + pdu->bhs_pkt = bhs_pkt; + } else { + // New block fits into internal buffer - ignore for now and keep in big buffer + // to avoid needless overhead - PDUs are short-lived anyways. + // Keep using old BHS pointer + bhs_pkt = pdu->bhs_pkt; + } } pdu->ahs_pkt = (ahs_len != 0U) ? (iscsi_ahs_packet *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet)) : NULL; @@ -3032,7 +2937,6 @@ static iscsi_bhs_packet *iscsi_connection_pdu_resize(iscsi_pdu *pdu, const uint static bool iscsi_connection_pdu_write(iscsi_connection *conn, iscsi_pdu *pdu) { if ( conn->state >= ISCSI_CONNECT_STATE_EXITING ) { - iscsi_connection_pdu_destroy( pdu ); return false; } @@ -3042,8 +2946,6 @@ static bool iscsi_connection_pdu_write(iscsi_connection *conn, iscsi_pdu *pdu) + (pdu->ds_cmd_data == NULL ? 0 : 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 ); - iscsi_connection_pdu_destroy( pdu ); - if ( rc != (ssize_t)len ) { conn->state = ISCSI_CONNECT_STATE_EXITING; return false; @@ -3108,16 +3010,12 @@ 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, ds_len, false ); - - if ( response_pdu == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_handle_reject: Out of memory while allocating iSCSI reject response PDU" ); - + const uint32_t ds_len = (uint32_t) sizeof(struct iscsi_bhs_packet) + (uint32_t) (pdu->bhs_pkt->total_ahs_len * ISCSI_ALIGN_SIZE); + iscsi_pdu CLEANUP_PDU response_pdu; + if ( !iscsi_connection_pdu_init( &response_pdu, ds_len, false ) ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } - iscsi_reject_packet *reject_pkt = (iscsi_reject_packet *) response_pdu->bhs_pkt; + iscsi_reject_packet *reject_pkt = (iscsi_reject_packet *) response_pdu.bhs_pkt; reject_pkt->opcode = ISCSI_OPCODE_SERVER_REJECT; reject_pkt->flags = -0x80; @@ -3139,58 +3037,52 @@ static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu reject_pkt->reserved4 = 0ULL; - memcpy( response_pdu->ds_cmd_data, pdu->bhs_pkt, ds_len ); + memcpy( response_pdu.ds_cmd_data, pdu->bhs_pkt, ds_len ); - iscsi_connection_pdu_write( conn, response_pdu ); + iscsi_connection_pdu_write( conn, &response_pdu ); return ISCSI_CONNECT_PDU_READ_OK; } /** - * @brief Updates Command Sequence Number (CmdSN) of an incoming iSCSI PDU request. + * @brief Updates the expected command sequence number (ExpCmdSN) and validates sequence number bounds. * - * This function updates the Command Sequence - * Number (CmdSN) for incoming data sent by - * the client. + * This function extracts the CmdSN and checks whether it fits within the session's + * expected command sequence range, considering session type and iSCSI operation types. + * Also updates session-related sequence numbers as needed based on the received command. * - * @param[in] conn Pointer to iSCSI connection to handle. May - * NOT be NULL, so take caution. - * @param[in] pdu Pointer to iSCSI client request PDU to handle. - * May be NULL in which case an error is returned. - * @return 0 on success. A negative value indicates - * an error. A positive value a warning. + * @param[in] conn Pointer to the iSCSI connection. Must not be NULL, and its session pointer should also be valid. + * @param[in] request_pdu Pointer to the iSCSI PDU (Protocol Data Unit) containing command information. Must not be NULL. + * + * @return Returns `ISCSI_CONNECT_PDU_READ_OK` (0) on success or + * `ISCSI_CONNECT_PDU_READ_ERR_FATAL` (-1) if sequence numbers or other data are invalid. */ -static int iscsi_connection_update_cmd_sn(iscsi_connection *conn, iscsi_pdu *pdu) +static int iscsi_connection_handle_cmd_sn(iscsi_connection *conn, iscsi_pdu *request_pdu) { iscsi_session *session = conn->session; if ( session == NULL ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) pdu->bhs_pkt; + iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) request_pdu->bhs_pkt; const int opcode = ISCSI_GET_OPCODE(scsi_cmd_pkt->opcode); - pdu->cmd_sn = iscsi_get_be32(scsi_cmd_pkt->cmd_sn); + request_pdu->cmd_sn = iscsi_get_be32(scsi_cmd_pkt->cmd_sn); if ( (scsi_cmd_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) { - if ( (iscsi_seq_num_cmp_lt( pdu->cmd_sn, session->exp_cmd_sn ) - || iscsi_seq_num_cmp_gt( pdu->cmd_sn, session->max_cmd_sn )) + if ( (iscsi_seq_num_cmp_lt( request_pdu->cmd_sn, session->exp_cmd_sn ) + || iscsi_seq_num_cmp_gt( request_pdu->cmd_sn, session->max_cmd_sn )) && ((session->type == ISCSI_SESSION_TYPE_NORMAL) && (opcode != ISCSI_OPCODE_CLIENT_SCSI_DATA_OUT)) ) { logadd( LOG_WARNING, "Seqnum messup. Is: %u, want >= %u, < %u", - pdu->cmd_sn, session->exp_cmd_sn, session->max_cmd_sn ); + request_pdu->cmd_sn, session->exp_cmd_sn, session->max_cmd_sn ); return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } - } else if ( (pdu->cmd_sn != session->exp_cmd_sn) && (opcode != ISCSI_OPCODE_CLIENT_NOP_OUT) ) { + } else if ( (request_pdu->cmd_sn != session->exp_cmd_sn) && (opcode != ISCSI_OPCODE_CLIENT_NOP_OUT) ) { logadd( LOG_WARNING, "Seqnum messup. Is: %u, want: %u", - pdu->cmd_sn, session->exp_cmd_sn ); + request_pdu->cmd_sn, session->exp_cmd_sn ); return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } - uint32_t exp_stat_sn = iscsi_get_be32(scsi_cmd_pkt->exp_stat_sn); - - if ( iscsi_seq_num_cmp_gt( exp_stat_sn, conn->stat_sn ) ) - exp_stat_sn = conn->stat_sn; - if ( ((scsi_cmd_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0) && (opcode != ISCSI_OPCODE_CLIENT_SCSI_DATA_OUT) ) session->exp_cmd_sn++; @@ -3198,191 +3090,6 @@ static int iscsi_connection_update_cmd_sn(iscsi_connection *conn, iscsi_pdu *pdu } /** - * @brief Handles an incoming iSCSI header login request PDU. - * - * This function handles login request header - * data sent by the client.\n - * If a response needs to be sent, this will - * be done as well. - * - * @param[in] conn Pointer to iSCSI connection to handle. May - * NOT be NULL, so take caution. - * @param[in] pdu Pointer to iSCSI client request PDU to handle. - * May be NULL in which case an error is returned. - * @return 0 on success. A negative value indicates - * an error. A positive value a warning. - */ -static int iscsi_connection_pdu_header_handle_login_req(iscsi_connection *conn, iscsi_pdu *pdu) -{ - if ( ((conn->flags & ISCSI_CONNECT_FLAGS_FULL_FEATURE) != 0) && (conn->session != NULL) - && (conn->session->type == ISCSI_SESSION_TYPE_DISCOVERY) ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - - const iscsi_login_req_packet *login_req_pkt = (iscsi_login_req_packet *) pdu->bhs_pkt; - - pdu->cmd_sn = iscsi_get_be32(login_req_pkt->cmd_sn); - - 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 = conn->login_response_pdu != NULL - ? conn->login_response_pdu - : iscsi_connection_pdu_create( conn, 8192, false ); - - if ( login_response_pdu == NULL ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - - const int rc = iscsi_connection_pdu_login_response_init( login_response_pdu, pdu ); - - if ( rc < 0 ) { - iscsi_connection_pdu_login_response( conn, login_response_pdu ); - - return ISCSI_CONNECT_PDU_READ_OK; - } - - conn->login_response_pdu = login_response_pdu; - - return ISCSI_CONNECT_PDU_READ_OK; -} - -/** - * @brief Handles an incoming iSCSI header NOP-Out request PDU. - * - * This function handles NOP-Out request header - * data sent by the client.\n - * If a response needs to be sent, this will - * be done as well. - * - * @param[in] conn Pointer to iSCSI connection to handle. May - * NOT be NULL, so take caution. - * @param[in] pdu Pointer to iSCSI client request PDU to handle. - * May be NULL in which case an error is returned. - * @return 0 on success. A negative value indicates - * an error. A positive value a warning. - */ -static int iscsi_connection_pdu_header_handle_nop_out(iscsi_connection *conn, iscsi_pdu *pdu) -{ - if ( conn->session->type == ISCSI_SESSION_TYPE_DISCOVERY ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - - if ( pdu->ds_len > ISCSI_DEFAULT_MAX_RECV_DS_LEN ) - return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); - - iscsi_nop_out_packet *nop_out_pkt = (iscsi_nop_out_packet *) pdu->bhs_pkt; - const uint32_t init_task_tag = iscsi_get_be32(nop_out_pkt->init_task_tag); - const uint32_t target_xfer_tag = iscsi_get_be32(nop_out_pkt->target_xfer_tag); - - if ( (target_xfer_tag != 0xFFFFFFFFUL) && (target_xfer_tag != (uint32_t) conn->id) ) - return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_INVALID_PDU_FIELD ); // TODO: Check if this is the correct error code. - - if ( (init_task_tag == 0xFFFFFFFFUL) && (nop_out_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - - return ISCSI_CONNECT_PDU_READ_OK; -} - -/** - * @brief Handles an incoming iSCSI header SCSI command request PDU. - * - * This function handles SCSI command request - * header data sent by the client.\n - * If a response needs to be sent, this will - * be done as well. - * - * @param[in] conn Pointer to iSCSI connection to handle. May - * NOT be NULL, so take caution. - * @param[in] pdu Pointer to iSCSI client request PDU to handle. - * May be NULL in which case an error is returned. - * @return 0 on success. A negative value indicates - * an error. A positive value a warning. - */ -static int iscsi_connection_pdu_header_handle_scsi_cmd(iscsi_connection *conn, iscsi_pdu *pdu) -{ - if ( conn->session->type != ISCSI_SESSION_TYPE_NORMAL ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - - iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) pdu->bhs_pkt; - - if ( (scsi_cmd_pkt->flags_task & (ISCSI_SCSI_CMD_FLAGS_TASK_READ | ISCSI_SCSI_CMD_FLAGS_TASK_WRITE)) - == (ISCSI_SCSI_CMD_FLAGS_TASK_READ | ISCSI_SCSI_CMD_FLAGS_TASK_WRITE) ) { // Bidirectional transfer is not supported - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } - - iscsi_task *task = iscsi_task_create( conn ); - - if ( task == NULL ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - - uint32_t exp_xfer_len = iscsi_get_be32(scsi_cmd_pkt->exp_xfer_len); - - task->scsi_task.len = (uint) (((uint8_t *) pdu->ds_cmd_data) - ((uint8_t *) pdu->bhs_pkt)); - task->scsi_task.cdb = &scsi_cmd_pkt->scsi_cdb; - task->scsi_task.xfer_len = exp_xfer_len; - task->init_task_tag = iscsi_get_be32(scsi_cmd_pkt->init_task_tag); - - const uint64_t lun = iscsi_get_be64(scsi_cmd_pkt->lun); - task->lun_id = iscsi_scsi_lun_get_from_iscsi( lun ); - - if ( ((scsi_cmd_pkt->flags_task & (ISCSI_SCSI_CMD_FLAGS_TASK_READ | ISCSI_SCSI_CMD_FLAGS_TASK_WRITE)) == 0) && (exp_xfer_len > 0UL) ) { - iscsi_task_destroy( task ); - - return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_INVALID_PDU_FIELD ); - } - - if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_READ) != 0 ) - task->scsi_task.flags |= ISCSI_SCSI_TASK_FLAGS_XFER_READ; - - if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_WRITE) != 0 ) { - iscsi_task_destroy( task ); - return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_COMMAND_NOT_SUPPORTED ); - } - - pdu->task = task; - - return ISCSI_CONNECT_PDU_READ_OK; -} - -/** - * @brief Handles an incoming iSCSI header text request PDU. - * - * This function handles text request header - * data sent by the client.\n - * If a response needs to be sent, this will - * be done as well. - * - * @param[in] conn Pointer to iSCSI connection to handle. May - * NOT be NULL, so take caution. - * @param[in] pdu Pointer to iSCSI client request PDU to handle. - * May be NULL in which case an error is returned. - * @return 0 on success. A negative value indicates - * an error. A positive value a warning. - */ -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_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; - - const uint32_t init_task_tag = iscsi_get_be32(text_req_pkt->init_task_tag); - const uint32_t exp_stat_sn = iscsi_get_be32(text_req_pkt->exp_stat_sn); - - if ( exp_stat_sn != conn->stat_sn ) - conn->stat_sn = exp_stat_sn; - - if ( (text_req_pkt->flags & (ISCSI_TEXT_REQ_FLAGS_CONTINUE | ISCSI_TEXT_REQ_FLAGS_FINAL)) - == (ISCSI_TEXT_REQ_FLAGS_CONTINUE | ISCSI_TEXT_REQ_FLAGS_FINAL) ) { - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } - - if ( conn->session->current_text_init_task_tag == 0xFFFFFFFFUL ) { - conn->session->current_text_init_task_tag = init_task_tag; - return ISCSI_CONNECT_PDU_READ_OK; - } - return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); -} - -/** * @brief Handles an incoming iSCSI header logout request PDU. * * This function handles logout request header @@ -3392,27 +3099,23 @@ static int iscsi_connection_pdu_header_handle_text_req(iscsi_connection *conn, i * * @param[in] conn Pointer to iSCSI connection to handle. May * NOT be NULL, so take caution. - * @param[in] pdu Pointer to iSCSI client request PDU to handle. + * @param[in] request_pdu Pointer to iSCSI client request PDU to handle. * May be NULL in which case an error is returned. * @return 0 on success. A negative value indicates * an error. A positive value a warning. */ -static int iscsi_connection_pdu_header_handle_logout_req(iscsi_connection *conn, iscsi_pdu *pdu) +static int iscsi_connection_handle_logout_req(iscsi_connection *conn, iscsi_pdu *request_pdu) { - iscsi_logout_req_packet *logout_req_pkt = (iscsi_logout_req_packet *) pdu->bhs_pkt; + iscsi_logout_req_packet *logout_req_pkt = (iscsi_logout_req_packet *) request_pdu->bhs_pkt; 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, 0UL, false ); - - if ( response_pdu == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_pdu_header_handle_logout_req: Out of memory while allocating iSCSI logout response PDU" ); - + iscsi_pdu CLEANUP_PDU response_pdu; + if ( !iscsi_connection_pdu_init( &response_pdu, 0, false ) ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } - iscsi_logout_response_packet *logout_response_pkt = (iscsi_logout_response_packet *) response_pdu->bhs_pkt; + iscsi_logout_response_packet *logout_response_pkt = (iscsi_logout_response_packet *) response_pdu.bhs_pkt; logout_response_pkt->opcode = ISCSI_OPCODE_SERVER_LOGOUT_RES; logout_response_pkt->flags = -0x80; @@ -3420,8 +3123,6 @@ static int iscsi_connection_pdu_header_handle_logout_req(iscsi_connection *conn, const uint16_t cid = iscsi_get_be16(logout_req_pkt->cid); if ( cid == conn->cid ) { - conn->flags |= ISCSI_CONNECT_FLAGS_LOGGED_OUT; - logout_response_pkt->response = ISCSI_LOGOUT_RESPONSE_CLOSED_SUCCESSFULLY; } else { logout_response_pkt->response = ISCSI_LOGOUT_RESPONSE_CID_NOT_FOUND; @@ -3440,8 +3141,8 @@ static int iscsi_connection_pdu_header_handle_logout_req(iscsi_connection *conn, iscsi_put_be32( (uint8_t *) &logout_response_pkt->exp_cmd_sn, conn->session->exp_cmd_sn ); iscsi_put_be32( (uint8_t *) &logout_response_pkt->max_cmd_sn, conn->session->max_cmd_sn ); } else { - iscsi_put_be32( (uint8_t *) &logout_response_pkt->exp_cmd_sn, pdu->cmd_sn ); - iscsi_put_be32( (uint8_t *) &logout_response_pkt->max_cmd_sn, pdu->cmd_sn ); + iscsi_put_be32( (uint8_t *) &logout_response_pkt->exp_cmd_sn, request_pdu->cmd_sn ); + iscsi_put_be32( (uint8_t *) &logout_response_pkt->max_cmd_sn, request_pdu->cmd_sn ); } logout_response_pkt->reserved4 = 0UL; @@ -3449,85 +3150,13 @@ static int iscsi_connection_pdu_header_handle_logout_req(iscsi_connection *conn, logout_response_pkt->time_retain = 0U; logout_response_pkt->reserved5 = 0UL; - iscsi_connection_pdu_write( conn, response_pdu ); - - return ISCSI_CONNECT_PDU_READ_OK; -} - -/** - * @brief Handles an incoming iSCSI header PDU. - * - * This function handles all header data sent - * by the client, including authentication.\n - * If a response needs to be sent, this will - * be done as well. - * - * @param[in] conn Pointer to iSCSI connection to handle. May - * NOT be NULL, so take caution. - * @param[in] pdu Pointer to iSCSI client request PDU to handle. - * May be NULL in which case an error is returned. - * @return 0 on success. A negative value indicates - * an error. A positive value a warning. - */ -static int iscsi_connection_pdu_header_handle(iscsi_connection *conn, iscsi_pdu *pdu) -{ - if ( pdu == NULL ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + bool ret = iscsi_connection_pdu_write( conn, &response_pdu ); - const int opcode = ISCSI_GET_OPCODE(pdu->bhs_pkt->opcode); - - if ( opcode == ISCSI_OPCODE_CLIENT_LOGIN_REQ ) - 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, 0UL, false ); - - if ( login_response_pdu == NULL ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - - iscsi_connection_login_response_reject( login_response_pdu, pdu ); - iscsi_connection_pdu_write( conn, login_response_pdu ); - - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; - } - if ( conn->state == ISCSI_CONNECT_STATE_INVALID ) { - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } - - int rc = iscsi_connection_update_cmd_sn( conn, pdu ); - - if ( rc != 0 ) - return rc; - - switch ( opcode ) { - case ISCSI_OPCODE_CLIENT_NOP_OUT : { - rc = iscsi_connection_pdu_header_handle_nop_out( conn, pdu ); - - break; - } - case ISCSI_OPCODE_CLIENT_SCSI_CMD : { - rc = iscsi_connection_pdu_header_handle_scsi_cmd( conn, pdu ); - - break; - } - case ISCSI_OPCODE_CLIENT_TEXT_REQ : { - rc = iscsi_connection_pdu_header_handle_text_req( conn, pdu ); - - break; - } - case ISCSI_OPCODE_CLIENT_LOGOUT_REQ : { - rc = iscsi_connection_pdu_header_handle_logout_req( conn, pdu ); - - break; - } - default : { - rc = iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_COMMAND_NOT_SUPPORTED ); - - break; - } + if ( cid == conn->cid ) { + conn->state = ISCSI_CONNECT_STATE_EXITING; } - return rc; + return ret ? ISCSI_CONNECT_PDU_READ_OK : ISCSI_CONNECT_PDU_READ_ERR_FATAL; } /** @@ -3540,36 +3169,42 @@ static int iscsi_connection_pdu_header_handle(iscsi_connection *conn, iscsi_pdu * * @param[in] conn Pointer to iSCSI connection to handle. May * NOT be NULL, so take caution. - * @param[in] pdu Pointer to iSCSI client request PDU to handle. + * @param[in] request_pdu Pointer to iSCSI client request PDU to handle. * May be NULL in which case an error is returned. + * @param response_pdu * @return 0 on success. A negative value indicates * an error. A positive value a warning. */ -static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscsi_pdu *pdu) +static int iscsi_connection_handle_nop_out(iscsi_connection *conn, iscsi_pdu *request_pdu) { - iscsi_nop_out_packet *nop_out_pkt = (iscsi_nop_out_packet *) pdu->bhs_pkt; - uint32_t ds_len = pdu->ds_len; - - if ( ds_len > conn->session->opts.MaxRecvDataSegmentLength ) - ds_len = conn->session->opts.MaxRecvDataSegmentLength; + if ( conn->session->type == ISCSI_SESSION_TYPE_DISCOVERY ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - const uint64_t lun = iscsi_get_be64(nop_out_pkt->lun); - const uint32_t init_task_tag = iscsi_get_be32(nop_out_pkt->init_task_tag); + if ( request_pdu->ds_len > ISCSI_DEFAULT_MAX_RECV_DS_LEN ) + return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); - conn->flags &= ~ISCSI_CONNECT_FLAGS_NOP_OUTSTANDING; + iscsi_nop_out_packet *nop_out_pkt = (iscsi_nop_out_packet *) request_pdu->bhs_pkt; + const uint32_t target_xfer_tag = iscsi_get_be32(nop_out_pkt->target_xfer_tag); + uint32_t ds_len = request_pdu->ds_len; + const uint64_t lun = iscsi_get_be64(nop_out_pkt->lun); - if ( init_task_tag == 0xFFFFFFFFUL ) + if ( nop_out_pkt->init_task_tag == 0xFFFFFFFFUL ) // Was response to a NOP by us - do not reply return ISCSI_CONNECT_PDU_READ_OK; - iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, ds_len, false ); + if ( (target_xfer_tag != 0xFFFFFFFFUL) && (target_xfer_tag != (uint32_t) conn->id) ) + return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_INVALID_PDU_FIELD ); // TODO: Check if this is the correct error code. - if ( response_pdu == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_pdu_data_handle_nop_out: Out of memory while allocating iSCSI NOP-In response PDU" ); + if ( (nop_out_pkt->init_task_tag == 0xFFFFFFFFUL) && (nop_out_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + if ( ds_len > (uint32_t)conn->session->opts.MaxRecvDataSegmentLength ) + ds_len = conn->session->opts.MaxRecvDataSegmentLength; + + iscsi_pdu CLEANUP_PDU response_pdu; + if ( !iscsi_connection_pdu_init( &response_pdu, ds_len, false ) ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } - iscsi_nop_in_packet *nop_in_pkt = (iscsi_nop_in_packet *) response_pdu->bhs_pkt; + iscsi_nop_in_packet *nop_in_pkt = (iscsi_nop_in_packet *) response_pdu.bhs_pkt; nop_in_pkt->opcode = ISCSI_OPCODE_SERVER_NOP_IN; nop_in_pkt->flags = -0x80; @@ -3577,7 +3212,7 @@ static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscs iscsi_put_be32( (uint8_t *) &nop_in_pkt->total_ahs_len, ds_len ); // TotalAHSLength is always 0 and DataSegmentLength is 24-bit, so write in one step. iscsi_put_be64( (uint8_t *) &nop_in_pkt->lun, lun ); nop_in_pkt->target_xfer_tag = 0xFFFFFFFFUL; // Minus one does not require endianess conversion - iscsi_put_be32( (uint8_t *) &nop_in_pkt->init_task_tag, init_task_tag ); + nop_in_pkt->init_task_tag = nop_out_pkt->init_task_tag; // Copyed from request packet, no endian conversion required iscsi_put_be32( (uint8_t *) &nop_in_pkt->stat_sn, conn->stat_sn++ ); if ( (nop_out_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) @@ -3588,10 +3223,11 @@ static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscs nop_in_pkt->reserved2 = 0UL; nop_in_pkt->reserved3 = 0ULL; - if ( ds_len != 0UL ) - memcpy( response_pdu->ds_cmd_data, pdu->ds_cmd_data, ds_len ); + if ( ds_len != 0UL ) { + memcpy( response_pdu.ds_cmd_data, request_pdu->ds_cmd_data, ds_len ); + } - iscsi_connection_pdu_write( conn, response_pdu ); + iscsi_connection_pdu_write( conn, &response_pdu ); return ISCSI_CONNECT_PDU_READ_OK; } @@ -3606,63 +3242,82 @@ static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscs * * @param[in] conn Pointer to iSCSI connection to handle. May * NOT be NULL, so take caution. - * @param[in] pdu Pointer to iSCSI client request PDU to handle. + * @param[in] request_pdu Pointer to iSCSI client request PDU to handle. * May be NULL in which case an error is returned. * @return 0 on success. A negative value indicates * an error. A positive value a warning. */ -static int iscsi_connection_pdu_data_handle_scsi_cmd(iscsi_connection *conn, iscsi_pdu *pdu) +static int iscsi_connection_handle_scsi_cmd(iscsi_connection *conn, iscsi_pdu *request_pdu) { - iscsi_task *task = pdu->task; + iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) request_pdu->bhs_pkt; - if ( task == NULL ) - return ISCSI_CONNECT_PDU_READ_OK; + if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_WRITE) != 0 ) { // Bidirectional transfer is not supported + logadd( LOG_DEBUG1, "Received SCSI write command from %s", conn->client->hostName ); + // Should really return a write protect error on SCSI layer, but a well-behaving client shouldn't ever + // send a write command anyways, since we declare the device read only. + return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_COMMAND_NOT_SUPPORTED ); + } - if ( task->lun_id != ISCSI_DEFAULT_LUN ) { - logadd( LOG_WARNING, "Received SCSI command for unknown LUN %d", task->lun_id ); - iscsi_scsi_task_lun_process_none( &task->scsi_task ); - iscsi_scsi_task_xfer_complete( &task->scsi_task, pdu ); + iscsi_task *task = iscsi_task_create( conn ); - return ISCSI_CONNECT_PDU_READ_OK; + if ( task == NULL ) { + return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_OUT_OF_RESOURCES ); } - if ( (task->scsi_task.flags & ISCSI_SCSI_TASK_FLAGS_XFER_READ) != 0 ) { - task->scsi_task.buf = NULL; - task->scsi_task.len = task->scsi_task.xfer_len; - } - iscsi_scsi_lun_task_run( &task->scsi_task, pdu ); + uint32_t exp_xfer_len = iscsi_get_be32(scsi_cmd_pkt->exp_xfer_len); - return ISCSI_CONNECT_PDU_READ_OK; -} + task->scsi_task.len = (uint) (((uint8_t *) request_pdu->ds_cmd_data) - ((uint8_t *) request_pdu->bhs_pkt)); // Length of BHS + AHS + task->scsi_task.cdb = &scsi_cmd_pkt->scsi_cdb; + task->scsi_task.xfer_len = exp_xfer_len; + task->init_task_tag = iscsi_get_be32(scsi_cmd_pkt->init_task_tag); -/* - * This function is used to set the info in the connection data structure - * return - * 0: success - * otherwise: error - */ -static int iscsi_connection_login_set_info(iscsi_connection *conn, iscsi_pdu *login_response_pdu, const int type, const uint cid) -{ - conn->flags &= ~ISCSI_CONNECT_FLAGS_AUTH; - conn->cid = (uint16_t) cid; + const uint64_t lun = iscsi_get_be64(scsi_cmd_pkt->lun); + task->lun_id = iscsi_scsi_lun_get_from_iscsi( lun ); - if ( conn->session == NULL ) { - iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; - conn->session = iscsi_session_create( conn, type ); + if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_READ) == 0 ) { + if ( exp_xfer_len != 0UL ) { + // Not a read request, but expecting data - not valid + iscsi_task_destroy( task ); - if ( conn->session == NULL ) { - 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_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_INVALID_PDU_FIELD ); + } + } else { + task->scsi_task.flags |= ISCSI_SCSI_TASK_FLAGS_XFER_READ; + } - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + int rc; + + if ( task->lun_id != ISCSI_DEFAULT_LUN ) { + logadd( LOG_WARNING, "Received SCSI command for unknown LUN %d", task->lun_id ); + iscsi_scsi_task_lun_process_none( &task->scsi_task ); + rc = ISCSI_CONNECT_PDU_READ_OK; + } else { + if ( (task->scsi_task.flags & ISCSI_SCSI_TASK_FLAGS_XFER_READ) != 0 ) { + task->scsi_task.buf = NULL; + task->scsi_task.len = task->scsi_task.xfer_len; } - conn->stat_sn = iscsi_get_be32(login_response_pkt->stat_sn); + task->scsi_task.status = ISCSI_SCSI_STATUS_GOOD; - conn->session->exp_cmd_sn = login_response_pdu->cmd_sn; - conn->session->max_cmd_sn = (uint32_t) (login_response_pdu->cmd_sn + ISCSI_DEFAULT_QUEUE_DEPTH - 1UL); + rc = iscsi_scsi_emu_block_process( &task->scsi_task ); + + if ( rc == ISCSI_SCSI_TASK_RUN_UNKNOWN ) { + rc = iscsi_scsi_emu_primary_process( &task->scsi_task ); + + if ( rc == ISCSI_SCSI_TASK_RUN_UNKNOWN ) { + iscsi_scsi_task_status_set( &task->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 ); + // TODO: Free task + rc = ISCSI_SCSI_TASK_RUN_COMPLETE; + } + } } + if ( rc == ISCSI_SCSI_TASK_RUN_COMPLETE ) { + iscsi_scsi_task_xfer_complete( &task->scsi_task, request_pdu ); + } + + iscsi_task_destroy( task ); + return ISCSI_CONNECT_PDU_READ_OK; } @@ -3681,7 +3336,7 @@ static int iscsi_connection_login_set_info(iscsi_connection *conn, iscsi_pdu *lo * @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_negotiation_kvp *kvpairs, uint cid) +static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_negotiation_kvp *kvpairs) { int type, rc; iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; @@ -3691,21 +3346,59 @@ static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscs if ( rc < 0 ) return rc; - if ( kvpairs->TargetName != NULL && type == ISCSI_SESSION_TYPE_NORMAL ) { + if ( type != ISCSI_SESSION_TYPE_NORMAL ) { + login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR; + login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_SESSION_NO_SUPPORT; + rc = ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } else if ( kvpairs->TargetName != NULL ) { 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; - - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + rc = ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; } if ( rc < 0 ) return rc; - return iscsi_connection_login_set_info( conn, login_response_pdu, type, cid ); + if ( conn->session == NULL ) { + iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt; + conn->session = iscsi_session_create( type ); + + if ( conn->session == NULL ) { + 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; + } + + conn->stat_sn = iscsi_get_be32(login_response_pkt->stat_sn); + + conn->session->exp_cmd_sn = login_response_pdu->cmd_sn; + conn->session->max_cmd_sn = (uint32_t) (login_response_pdu->cmd_sn + ISCSI_DEFAULT_QUEUE_DEPTH - 1UL); + } + + return ISCSI_CONNECT_PDU_READ_OK; } +/** + * @brief Writes login options to a PDU (Protocol Data Unit). + * + * This function processes key-value pairs of login negotiation options and + * appends them to the specified PDU. The function ensures the payload of the + * response PDU does not exceed its designated length. + * + * @param[in] conn Pointer to the iSCSI connection structure containing session + * options and other connection-specific information. + * @param[in] pairs Pointer to the iSCSI negotiation key-value pairs structure + * that holds applicable key-value options for the login phase. + * @param[in,out] response_pdu Pointer to the PDU where the login options should + * be added. The PDU's fields, such as data segment and payload length, are + * updated within the function. + * + * @return The updated payload length of the response PDU if successful. + * Returns -1 if an error occurs during key-value pair appending. + */ 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; @@ -3760,11 +3453,15 @@ if ( pairs->key != NULL ) ADD_KV_INTERNAL( false, #key, value ); \ */ static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_negotiation_kvp *pairs) { + if ( iscsi_connection_pdu_resize( login_response_pdu, 0, ISCSI_DEFAULT_RECV_DS_LEN ) == NULL ) { + return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + } 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 ); - if ( payload_len < 0 || payload_len > login_response_pdu->ds_len ) { + if ( payload_len < 0 || (uint32_t)payload_len > login_response_pdu->ds_len ) { + logadd( LOG_DEBUG1, "iscsi_connecction_handle_login_response: Invalid payload length %d, ds_len: %u, write_pos: %u", + payload_len, login_response_pdu->ds_len, login_response_pdu->ds_write_pos ); 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; @@ -3774,9 +3471,8 @@ static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi // 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 { + logadd( LOG_DEBUG1, "security nego" ); + if ( pairs->AuthMethod == NULL || strcasecmp( pairs->AuthMethod, "None" ) != 0 ) { // 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_AUTH_ERR; @@ -3787,10 +3483,7 @@ static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi break; } case ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION : { - if ( conn->state == ISCSI_CONNECT_STATE_INVALID ) { - conn->flags |= ISCSI_CONNECT_FLAGS_AUTH; - } - + // Nothing to do, expect client to request transition to full feature phase break; } case ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FULL_FEATURE_PHASE : @@ -3805,32 +3498,13 @@ static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi 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; - - break; - } - case ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION : { - conn->login_phase = ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_LOGIN_OPERATIONAL_NEGOTIATION; - - break; - } case ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FULL_FEATURE_PHASE : { conn->login_phase = ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FULL_FEATURE_PHASE; - iscsi_put_be16( (uint8_t *) &login_response_pkt->tsih, (uint16_t) conn->session->tsih ); - - 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; - - 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; - } + iscsi_put_be16( (uint8_t *) &login_response_pkt->tsih, 42 ); - conn->flags |= ISCSI_CONNECT_FLAGS_FULL_FEATURE; + conn->state = ISCSI_CONNECT_STATE_NORMAL_SESSION; + iscsi_connection_update_key_value_pairs( conn, pairs ); break; } @@ -3856,53 +3530,53 @@ static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi * * @param[in] conn Pointer to iSCSI connection to handle. May * NOT be NULL, so take caution. - * @param[in] pdu Pointer to iSCSI client request PDU to handle. + * @param[in] request_pdu Pointer to iSCSI client request PDU to handle. * May be NULL in which case an error is returned. + * @param login_response_pdu * @return 0 on success. A negative value indicates * an error. A positive value a warning. */ -static int iscsi_connection_pdu_data_handle_login_req(iscsi_connection *conn, iscsi_pdu *pdu) +static int iscsi_connection_handle_login_req(iscsi_connection *conn, iscsi_pdu *request_pdu) { int rc; - iscsi_pdu *login_response_pdu = (iscsi_pdu *) conn->login_response_pdu; - if ( login_response_pdu == NULL ) - return ISCSI_CONNECT_PDU_READ_OK; + if ( request_pdu->ds_len > ISCSI_DEFAULT_RECV_DS_LEN || conn->state != ISCSI_CONNECT_STATE_NEW ) + return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); - iscsi_login_req_packet *login_req_pkt = (iscsi_login_req_packet *) pdu->bhs_pkt; - uint cid = iscsi_get_be16(login_req_pkt->cid); + const iscsi_login_req_packet *login_req_pkt = (iscsi_login_req_packet *) request_pdu->bhs_pkt; - 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 ); + request_pdu->cmd_sn = iscsi_get_be32(login_req_pkt->cmd_sn); - 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; + iscsi_pdu CLEANUP_PDU login_response_pdu; + if ( !iscsi_connection_pdu_init( &login_response_pdu, 0, false ) ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE; + rc = iscsi_connection_pdu_login_response_init( &login_response_pdu, request_pdu ); + + if ( rc < 0 ) { + // response_init set an error code in the response pdu, send it right away and bail out + return iscsi_send_login_response_pdu( conn, &login_response_pdu ); } - if ( conn->state == ISCSI_CONNECT_STATE_INVALID ) { - rc = iscsi_connection_handle_login_phase_none( conn, login_response_pdu, &pairs, cid ); - logadd( LOG_DEBUG1, "rc2: %d", rc ); + 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 *) request_pdu->ds_cmd_data, request_pdu->ds_len ); - if ( rc != ISCSI_CONNECT_PDU_READ_OK ) { - iscsi_connection_pdu_login_response( conn, login_response_pdu ); + 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_AUTH_ERR; - return ISCSI_CONNECT_PDU_READ_OK; - } + return iscsi_send_login_response_pdu( conn, &login_response_pdu ); } - rc = iscsi_connecction_handle_login_response( conn, login_response_pdu, &pairs ); + rc = iscsi_connection_handle_login_phase_none( conn, &login_response_pdu, &pairs ); - if ( rc == ISCSI_CONNECT_PDU_READ_OK ) { - conn->state = ISCSI_CONNECT_STATE_RUNNING; + if ( rc != ISCSI_CONNECT_PDU_READ_OK ) { + return iscsi_send_login_response_pdu( conn, &login_response_pdu ); } - iscsi_connection_pdu_login_response( conn, login_response_pdu ); - - return ISCSI_CONNECT_PDU_READ_OK; + iscsi_connecction_handle_login_response( conn, &login_response_pdu, &pairs ); + return iscsi_send_login_response_pdu( conn, &login_response_pdu ); } /** @@ -3915,308 +3589,241 @@ static int iscsi_connection_pdu_data_handle_login_req(iscsi_connection *conn, is * * @param[in] conn Pointer to iSCSI connection to handle. May * NOT be NULL, so take caution. - * @param[in] pdu Pointer to iSCSI client request PDU to handle. + * @param[in] request_pdu Pointer to iSCSI client request PDU to handle. * May be NULL in which case an error is returned. * @return 0 on success. A negative value indicates * an error. A positive value a warning. */ -static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, iscsi_pdu *pdu) +static int iscsi_connection_handle_text_req(iscsi_connection *conn, iscsi_pdu *request_pdu) { - iscsi_text_req_packet *text_req_pkt = (iscsi_text_req_packet *) pdu->bhs_pkt; - iscsi_negotiation_kvp pairs; - int rc = iscsi_parse_login_key_value_pairs( &pairs, (uint8_t *) pdu->ds_cmd_data, pdu->ds_len ); + iscsi_text_req_packet *text_req_pkt = (iscsi_text_req_packet *) request_pdu->bhs_pkt; - if ( rc < 0 ) { - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + if ( request_pdu->ds_len > ISCSI_MAX_DS_SIZE ) + return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); + + if ( (text_req_pkt->flags & (ISCSI_TEXT_REQ_FLAGS_CONTINUE | ISCSI_TEXT_REQ_FLAGS_FINAL)) + == (ISCSI_TEXT_REQ_FLAGS_CONTINUE | ISCSI_TEXT_REQ_FLAGS_FINAL) ) { + // Continue and Final at the same time is invalid + return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); + } + if ( (text_req_pkt->flags & ISCSI_TEXT_REQ_FLAGS_FINAL) == 0 ) { + // Text request spread across multiple PDUs not supported + return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_COMMAND_NOT_SUPPORTED ); + } + if ( text_req_pkt->target_xfer_tag != 0xFFFFFFFFUL ) { + // Initial request must have this set to all 1 + return iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); } - iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 8192, false ); + const uint32_t exp_stat_sn = iscsi_get_be32(text_req_pkt->exp_stat_sn); + if ( exp_stat_sn != conn->stat_sn ) { + conn->stat_sn = exp_stat_sn; + } - if ( response_pdu == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_pdu_data_handle_text_req: Out of memory while allocating iSCSI text response PDU" ); + iscsi_negotiation_kvp pairs; + int rc = iscsi_parse_login_key_value_pairs( &pairs, (uint8_t *) request_pdu->ds_cmd_data, request_pdu->ds_len ); + if ( rc < 0 ) { return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } - iscsi_connection_update_key_value_pairs( conn, &pairs ); + iscsi_pdu CLEANUP_PDU response_pdu; + if ( !iscsi_connection_pdu_init( &response_pdu, MIN( 8192, conn->session->opts.MaxRecvDataSegmentLength ), false ) ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - int payload_len = iscsi_write_login_options_to_pdu( conn, &pairs, response_pdu ); + iscsi_connection_update_key_value_pairs( conn, &pairs ); - if ( payload_len < 0 || payload_len > response_pdu->ds_len ) { - iscsi_connection_pdu_destroy( response_pdu ); + // TODO: Handle SendTargets + int payload_len = iscsi_write_login_options_to_pdu( conn, &pairs, &response_pdu ); + if ( payload_len < 0 || (uint32_t)payload_len > response_pdu.ds_len ) { return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } - iscsi_text_response_packet *text_response_pkt = (iscsi_text_response_packet *) iscsi_connection_pdu_resize( response_pdu, 0, response_pdu->ds_write_pos ); + 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 = (int8_t) ISCSI_TEXT_RESPONSE_FLAGS_FINAL; text_response_pkt->reserved = 0; - 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. - - if ( (text_req_pkt->flags & ISCSI_TEXT_REQ_FLAGS_FINAL) != 0 ) { - text_response_pkt->target_xfer_tag = 0xFFFFFFFFUL; // Minus one does not require endianess conversion - - conn->session->current_text_init_task_tag = 0xFFFFFFFFUL; - } else { - iscsi_put_be32( (uint8_t *) &text_response_pkt->target_xfer_tag, (uint32_t) conn->id + 1UL ); - } + // 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, response_pdu.ds_write_pos ); + 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. + text_response_pkt->target_xfer_tag = 0xFFFFFFFFUL; // Minus one does not require endianess conversion iscsi_put_be32( (uint8_t *) &text_response_pkt->stat_sn, conn->stat_sn++ ); - if ( (text_response_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) - conn->session->max_cmd_sn++; + conn->session->max_cmd_sn++; iscsi_put_be32( (uint8_t *) &text_response_pkt->exp_cmd_sn, conn->session->exp_cmd_sn ); iscsi_put_be32( (uint8_t *) &text_response_pkt->max_cmd_sn, conn->session->max_cmd_sn ); text_response_pkt->reserved2[0] = 0ULL; text_response_pkt->reserved2[1] = 0ULL; - iscsi_connection_pdu_write( conn, response_pdu ); - - return ISCSI_CONNECT_PDU_READ_OK; + return iscsi_connection_pdu_write( conn, &response_pdu ) ? ISCSI_CONNECT_PDU_READ_OK : ISCSI_CONNECT_PDU_READ_ERR_FATAL; } /** - * @brief Handles an incoming iSCSI payload data PDU. + * @brief Handles an incoming iSCSI PDU. * - * This function handles all payload data sent - * by the client.\n * If a response needs to be sent, this will * be done as well. * * @param[in] conn Pointer to iSCSI connection to handle. May * NOT be NULL, so take caution. - * @param[in] pdu Pointer to iSCSI client request PDU to handle. + * @param[in] request_pdu Pointer to iSCSI client request PDU to handle. * May be NULL in which case an error is returned. * @return 0 on success. A negative value indicates * an error. A positive value a warning. */ -static int iscsi_connection_pdu_data_handle(iscsi_connection *conn, iscsi_pdu *pdu) +static int iscsi_connection_pdu_handle(iscsi_connection *conn, iscsi_pdu *request_pdu) { int rc = 0; - const uint8_t opcode = ISCSI_GET_OPCODE(pdu->bhs_pkt->opcode); - - switch ( opcode ) { - case ISCSI_OPCODE_CLIENT_NOP_OUT : { - rc = iscsi_connection_pdu_data_handle_nop_out( conn, pdu ); + const uint8_t opcode = ISCSI_GET_OPCODE(request_pdu->bhs_pkt->opcode); - break; + if ( conn->state == ISCSI_CONNECT_STATE_NEW ) { + // Fresh connection, not logged in yet - we only support LOGIN in this state + if ( opcode == ISCSI_OPCODE_CLIENT_LOGIN_REQ ) { + rc = iscsi_connection_handle_login_req( conn, request_pdu ); + } else { + rc = iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); } - case ISCSI_OPCODE_CLIENT_SCSI_CMD : { - rc = iscsi_connection_pdu_data_handle_scsi_cmd( conn, pdu ); + } else if ( conn->state == ISCSI_CONNECT_STATE_EXITING ) { + // Exiting, nothing to do + rc = ISCSI_CONNECT_PDU_READ_OK; + } else if ( conn->state == ISCSI_CONNECT_STATE_NORMAL_SESSION ) { + // Normal operation + rc = iscsi_connection_handle_cmd_sn( conn, request_pdu ); + if ( rc != 0 ) + return rc; - break; - } - case ISCSI_OPCODE_CLIENT_LOGIN_REQ : { - rc = iscsi_connection_pdu_data_handle_login_req( conn, pdu ); + switch ( opcode ) { + case ISCSI_OPCODE_CLIENT_NOP_OUT : { + rc = iscsi_connection_handle_nop_out( conn, request_pdu ); - break; - } - case ISCSI_OPCODE_CLIENT_TEXT_REQ : { - rc = iscsi_connection_pdu_data_handle_text_req( conn, pdu ); + break; + } + case ISCSI_OPCODE_CLIENT_SCSI_CMD : { + rc = iscsi_connection_handle_scsi_cmd( conn, request_pdu ); - break; - } - case ISCSI_OPCODE_CLIENT_TASK_FUNC_REQ : - case ISCSI_OPCODE_CLIENT_LOGOUT_REQ : - case ISCSI_OPCODE_CLIENT_SNACK_REQ : { - break; - } - default : { - return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); + break; + } + case ISCSI_OPCODE_CLIENT_TEXT_REQ : { + rc = iscsi_connection_handle_text_req( conn, request_pdu ); - break; + break; + } + case ISCSI_OPCODE_CLIENT_LOGOUT_REQ : { + rc = iscsi_connection_handle_logout_req( conn, request_pdu ); + + break; + } + case ISCSI_OPCODE_CLIENT_TASK_FUNC_REQ : { + // TODO: Send OK if a task is requested to be cancelled + break; + } + default : { + rc = iscsi_connection_handle_reject( conn, request_pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); + + break; + } } } if ( rc < 0 ) { logadd( LOG_ERROR, "Fatal error during payload handler (opcode 0x%02x) detected for client %s", (int) opcode, conn->client->hostName ); } + return rc; } /** - * @brief Retrieves and merges splitted iSCSI PDU data read from TCP/IP socket. - * - * This function handles partial reads of data - * 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. - * @retval -1 Fatal error occured during processing the PDU. - * @retval 0 Read operation was successful and next read is ready. - * @retval 1 Read operation was successful and PDU was fully processed. + * @brief Reads and processes incoming iSCSI connection PDUs in a loop. + * + * This function continuously reads Protocol Data Units (PDUs) on an iSCSI + * connection and performs operations based on the type and content of the + * received data. The function processes Basic Header Segment (BHS), Additional + * Header Segment (AHS), and Data Segment (DS) as part of the PDU handling. If + * any errors occur during the process, the function gracefully exits the loop. + * + * @param[in] conn Pointer to the iSCSI connection object. Must not be NULL and + * contains the state and data required for processing the iSCSI connection. + * @param[in] request Pointer to the initial received data for the PDU. This + * serves as the partially received data of the BHS. Must not be NULL. + * @param[in] len Length of the already received portion of the BHS in bytes. */ -static int iscsi_connection_pdu_data_read(iscsi_connection *conn, iscsi_pdu *pdu) +static void iscsi_connection_pdu_read_loop(iscsi_connection *conn, const dnbd3_request_t *request, const int len) { - const uint32_t ds_len = ISCSI_ALIGN( pdu->ds_len, ISCSI_ALIGN_SIZE ); + iscsi_pdu CLEANUP_PDU request_pdu; - 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; + if ( !iscsi_connection_pdu_init( &request_pdu, 0, false ) ) + return; - pdu->recv_pos += len; + // 1) Receive BHS (partially already received, in "request", merge and finish) + memcpy( request_pdu.bhs_pkt, request, len ); + if ( sock_recv( conn->client->sock, ((uint8_t *)request_pdu.bhs_pkt) + len, sizeof(iscsi_bhs_packet) - len ) + != sizeof(iscsi_bhs_packet) - len ) { + logadd( LOG_INFO, "Cannot receive first BHS for client %s", conn->client->hostName ); + return; } - if ( pdu->recv_pos < ds_len ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - - return ISCSI_CONNECT_PDU_READ_OK; -} - -/** - * @brief Retrieves and merges splitted iSCSI PDU data read from TCP/IP socket. - * - * This function handles partial reads of BHS, AHS - * and DS packet data.\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. - * - * @param[in] conn Pointer to iSCSI connection to read TCP/IP data from. - * @retval -1 Fatal error occured during processing the PDU. - * @retval 0 Read operation was successful and next read is ready. - * @retval 1 Read operation was successful and PDU was fully processed. - */ -static int iscsi_connection_pdu_read(iscsi_connection *conn) -{ - int prev_recv_state; - do { - iscsi_pdu *pdu = conn->pdu_processing; - - prev_recv_state = conn->pdu_recv_state; - - 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, 0UL, false ); - - if ( conn->pdu_processing == NULL ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - - conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_HDR; - - break; - } - case ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_HDR : { - 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; - - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } - - pdu->bhs_pos += len; - } - - if ( (conn->flags & ISCSI_CONNECT_FLAGS_LOGGED_OUT) != 0 ) { - conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; - - 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 * 4); - const uint32_t ds_len = iscsi_get_be24(bhs_pkt->ds_len); - - bhs_pkt = iscsi_connection_pdu_resize( pdu, ahs_len, ds_len ); - - if ( bhs_pkt == NULL ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - - int pos = 0; - while ( pos < ahs_len ) { - const int32_t len = iscsi_connection_read( conn, (((uint8_t *) pdu->ahs_pkt) + pos), (ahs_len - pos) ); + // 2) Evaluate BHS regarding length of AHS and DS + iscsi_bhs_packet *bhs_pkt = request_pdu.bhs_pkt; + const uint ahs_len = ((uint) bhs_pkt->total_ahs_len * ISCSI_ALIGN_SIZE); + const uint32_t ds_len = iscsi_get_be24(bhs_pkt->ds_len); - if ( len < 0L ) { - conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; + bhs_pkt = iscsi_connection_pdu_resize( &request_pdu, ahs_len, ds_len ); - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } + if ( bhs_pkt == NULL ) { + logadd( LOG_WARNING, "Cannot resize PDU for client %s", conn->client->hostName ); + break; + } - pos += len; - } + // 3) Receive the optional AHS + if ( ahs_len != 0 && sock_recv( conn->client->sock, request_pdu.ahs_pkt, ahs_len ) != ahs_len ) { + logadd( LOG_DEBUG1, "Could not receive AHS for client %s", conn->client->hostName ); + break; + } - 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; - } + // 4) Receive the optional DS + if ( request_pdu.ds_len != 0U ) { + const uint32_t padded_ds_len = ISCSI_ALIGN( request_pdu.ds_len, ISCSI_ALIGN_SIZE ); + if ( sock_recv( conn->client->sock, request_pdu.ds_cmd_data, padded_ds_len ) != padded_ds_len ) { + logadd( LOG_DEBUG1, "Could not receive DS for client %s", conn->client->hostName ); break; } - case ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_DATA : { - if ( pdu->ds_len != 0U ) { - const int len = iscsi_connection_pdu_data_read( conn, pdu ); - - if ( len < 0 ) { - conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; - - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } else if ( len > 0 ) { - return ISCSI_CONNECT_PDU_READ_OK; - } - } - - int rc; - - conn->pdu_processing = NULL; - if ( (conn->flags & ISCSI_CONNECT_FLAGS_REJECTED) != 0 ) { - rc = 0; - } else { - rc = iscsi_connection_pdu_data_handle( conn, pdu ); - } - - iscsi_connection_pdu_destroy( pdu ); - - if ( rc == 0 ) { - conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_READY; + } - return ISCSI_CONNECT_PDU_READ_PROCESSED; - } - conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR; + // 5) Handle PDU + if ( iscsi_connection_pdu_handle( conn, &request_pdu ) != ISCSI_CONNECT_PDU_READ_OK + || conn->state == ISCSI_CONNECT_STATE_EXITING ) { + break; + } + // In case we needed an extra buffer, reset + if ( request_pdu.big_alloc != NULL ) { + iscsi_connection_pdu_destroy( &request_pdu ); + if ( !iscsi_connection_pdu_init( &request_pdu, 0, false ) ) { + logadd( LOG_WARNING, "Cannot re-initialize PDU for client %s", conn->client->hostName ); break; } - case ISCSI_CONNECT_PDU_RECV_STATE_ERR : { - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + } - break; - } - default : { - logadd( LOG_ERROR, "iscsi_connection_pdu_read: Fatal error reading, unknown packet status." - " Should NEVER happen! Please report this bug to the developer" ); + // Move first part of next iteration last in this loop, as we completed the first, partial + // header before the loop - this saves us from accounting for this within the mainloop - break; - } - } - if ( conn->state == ISCSI_CONNECT_STATE_EXITING || _shutdown ) { - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + // 1) Receive entire BHS + if ( sock_recv( conn->client->sock, request_pdu.bhs_pkt, sizeof(iscsi_bhs_packet) ) != sizeof(iscsi_bhs_packet) ) { + logadd( LOG_INFO, "Cannot receive BHS for client %s", conn->client->hostName ); + break; } - } while ( prev_recv_state != conn->pdu_recv_state ); - - return 0; + } while ( !_shutdown ); } /** @@ -4247,24 +3854,15 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ return; } - conn->pdu_processing = iscsi_connection_pdu_create( conn, 0UL, false ); - - if ( conn->pdu_processing == NULL ) { - iscsi_connection_destroy( conn ); - - return; - } - - memcpy( conn->pdu_processing->bhs_pkt, request, len ); - - conn->pdu_processing->bhs_pos = len; - conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_HDR; - static atomic_int CONN_ID = 0; conn->id = ++CONN_ID; - while ( iscsi_connection_pdu_read( conn ) >= ISCSI_CONNECT_PDU_READ_OK ) { - } + iscsi_connection_pdu_read_loop( conn, request, len ); + + // Wait for the client to receive any pending outgoing PDUs + shutdown( client->sock, SHUT_WR ); + sock_setTimeout( client->sock, 100 ); + while ( recv( client->sock, (void *)request, len, 0 ) > 0 ) {} iscsi_connection_destroy( conn ); } diff --git a/src/server/iscsi.h b/src/server/iscsi.h index 05f22e7..f4fa6b1 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -627,7 +627,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_10 { iscsi_scsi_cdb cdb; /// Flags. - int8_t flags; + uint8_t flags; /// Logical Block Address (LBA). uint32_t lba; @@ -653,7 +653,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_12 { iscsi_scsi_cdb cdb; /// Flags. - int8_t flags; + uint8_t flags; /// Logical Block Address (LBA). uint32_t lba; @@ -679,7 +679,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_16 { iscsi_scsi_cdb cdb; /// Flags. - int8_t flags; + uint8_t flags; /// Logical Block Address (LBA). uint64_t lba; @@ -842,7 +842,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_mode_sense_6 { iscsi_scsi_cdb cdb; /// Flags. - int8_t flags; + uint8_t flags; /// Page code and page control. uint8_t page_code_control; @@ -918,7 +918,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_mode_sense_10 { iscsi_scsi_cdb cdb; /// Flags. - int8_t flags; + uint8_t flags; /// Page code and page control. uint8_t page_code_control; @@ -1266,7 +1266,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_std_inquiry_data_packet { int8_t services_flags; /// Flags. - int8_t flags; + uint8_t flags; /// Vendor identification. uint8_t vendor_id[8]; @@ -1311,7 +1311,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_ext_inquiry_data_packet { uint8_t vendor_spec[20]; /// Flags. - int8_t flags; + uint8_t flags; /// Reserved for future usage (always MUST be 0). uint8_t reserved; @@ -1591,7 +1591,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_design_desc_inquiry_d uint8_t protocol_id_code_set; /// Flags. - int8_t flags; + uint8_t flags; /// Reserved for future usage (always MUST be 0). uint8_t reserved; @@ -1897,7 +1897,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_ext_inquiry_data_pack */ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_block_limits_inquiry_data_packet { /// Flags. - int8_t flags; + uint8_t flags; /// Maximum COMPARE AND WRITE length in logical blocks. uint8_t max_cmp_write_len; @@ -2036,7 +2036,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_block_dev_chars_inqui uint8_t product_type; /// Flags. - int8_t flags; + uint8_t flags; /// Support flags. uint8_t support_flags; @@ -2297,7 +2297,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_service_action_in_16_parameter uint32_t block_len; /// Flags: RC_BASIS, P_TYPE and PROT_EN. - int8_t flags; + uint8_t flags; /// P_I_EXPONENT and logical blocks per physical block exponent. uint8_t exponents; @@ -2380,7 +2380,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_6_parameter_header_ uint8_t medium_type; /// Flags. - int8_t flags; + uint8_t flags; /// Block descriptor length in bytes. uint8_t block_desc_len; @@ -2412,7 +2412,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_10_parameter_header uint8_t medium_type; /// Flags. - int8_t flags; + uint8_t flags; /// Long Logical Block Address (LONGLBA). uint8_t long_lba; @@ -2739,7 +2739,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_read_write_err_reco iscsi_scsi_mode_sense_mode_page_data_packet mode_page; /// Flags. - int8_t flags; + uint8_t flags; /// Read retry count. uint8_t read_retry_cnt; @@ -2806,7 +2806,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_verify_err_recovery iscsi_scsi_mode_sense_mode_page_data_packet mode_page; /// Flags. - int8_t flags; + uint8_t flags; /// Verify retry count. uint8_t verify_retry_cnt; @@ -2869,7 +2869,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_caching_mode_page_d iscsi_scsi_mode_sense_mode_page_data_packet mode_page; /// Flags. - int8_t flags; + uint8_t flags; /// Retention priority. uint8_t retention_pri; @@ -2913,7 +2913,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_control_mode_page_d iscsi_scsi_mode_sense_mode_page_data_packet mode_page; /// Flags. - int8_t flags; + uint8_t flags; /// Queue flags. int8_t queue_flags; @@ -2945,7 +2945,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_control_ext_mode_pa iscsi_scsi_mode_sense_mode_sub_page_data_packet mode_sub_page; /// Flags. - int8_t flags; + uint8_t flags; /// Initial command priority. uint8_t init_cmd_pri; @@ -2971,7 +2971,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_xor_ext_mode_page_d iscsi_scsi_mode_sense_mode_page_data_packet mode_page; /// Flags. - int8_t flags; + uint8_t flags; /// Reserved for future usage (always MUST be 0 for now). uint8_t reserved; @@ -3006,7 +3006,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_power_cond_mode_pag iscsi_scsi_mode_sense_mode_page_data_packet mode_page; /// Flags. - int8_t flags; + uint8_t flags; /// Idle and standby flags. int8_t idle_standby_flags; @@ -3053,7 +3053,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_info_exceptions_con iscsi_scsi_mode_sense_mode_page_data_packet mode_page; /// Flags. - int8_t flags; + uint8_t flags; /// Method Of Reporting Informational Exceptions (MRIE) flags. uint8_t mrie; @@ -3296,7 +3296,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cmd_packet { uint8_t opcode; /// Flags and Task Attributes. - int8_t flags_task; + uint8_t flags_task; /// Reserved for future usage, MUST always be 0. uint16_t reserved; @@ -3570,7 +3570,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_response_packet { uint8_t opcode; /// Flags. - int8_t flags; + uint8_t flags; /// This field contains the iSCSI service response. uint8_t response; @@ -3794,7 +3794,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_data_in_response_packet { uint8_t opcode; /// Incoming data flags. The fields StatSN, Status, and Residual Count only have meaningful content if the S bit is set to 1. - int8_t flags; + uint8_t flags; /// Rserved for future usage, always MUST be 0. uint8_t reserved; @@ -3937,7 +3937,7 @@ typedef struct __attribute__((packed)) iscsi_text_req_packet { uint8_t opcode; /// Text request flags. - int8_t flags; + uint8_t flags; /// Reserved for future usage, always MUST be 0. uint16_t reserved; @@ -4072,7 +4072,7 @@ typedef struct __attribute__((packed)) iscsi_text_response_packet { uint8_t opcode; /// Text response flags. - int8_t flags; + uint8_t flags; /// Reserved for future usage, always MUST be 0. uint16_t reserved; @@ -4359,7 +4359,7 @@ typedef struct __attribute__((packed)) iscsi_login_req_packet { uint8_t opcode; /// Login request flags. - int8_t flags; + uint8_t flags; /** * @brief Version-max indicates the maximum version number supported. @@ -4459,17 +4459,8 @@ typedef struct __attribute__((packed)) iscsi_login_req_packet { /// Reserved for future usage, always MUST be 0. uint64_t reserved2[2]; - - /** - * @brief Data segment - Login Parameters in Text Request Format. - * - * The initiator MUST provide some basic parameters in order - * to enable the target to determine if the initiator may use - * the target's resources and the initial text parameters for the security exchange - */ - iscsi_scsi_ds_cmd_data ds_cmd_data; } iscsi_login_req_packet; - +ASSERT_IS_BHS( iscsi_login_req_packet ); /// Login response Next Stage (NSG) flags: SecurityNegotiation. #define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION 0x0 @@ -4718,7 +4709,7 @@ typedef struct __attribute__((packed)) iscsi_login_response_packet { uint8_t opcode; /// Login response flags. - int8_t flags; + uint8_t flags; /** * @brief This is the highest version number supported by the target. @@ -5025,7 +5016,7 @@ typedef struct __attribute__((packed)) iscsi_logout_response_packet { uint8_t opcode; /// Reserved for future usage (must be always 0x80 for now). - int8_t flags; + uint8_t flags; /// Response. uint8_t response; @@ -5175,7 +5166,7 @@ typedef struct __attribute__((packed)) iscsi_reject_packet { uint8_t opcode; /// Reserved for future usage (must be always 0x80 for now). - int8_t flags; + uint8_t flags; /** * @brief Reject reason. @@ -5284,7 +5275,7 @@ typedef struct __attribute__((packed)) iscsi_nop_out_packet { uint8_t opcode; /// Reserved for future usage (must be always 0x80 for now). - int8_t flags; + uint8_t flags; /// Reserved for future usage, always MUST be 0. uint16_t reserved; @@ -5382,7 +5373,7 @@ typedef struct __attribute__((packed)) iscsi_nop_in_packet { uint8_t opcode; /// Reserved for future usage (must be always 0x80 for now). - int8_t flags; + uint8_t flags; /// Reserved for future usage, always MUST be 0. uint16_t reserved; @@ -6064,9 +6055,6 @@ typedef struct iscsi_session { /// MaxCmdSN. uint32_t max_cmd_sn; - /// Current text Initiator Task Tag (ITT). - uint32_t current_text_init_task_tag; - /// Session options client sent in login request. iscsi_session_options opts; } iscsi_session; @@ -6094,27 +6082,9 @@ typedef struct iscsi_pdu iscsi_pdu; #define ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER_XCHG_NOT_ONCE (-4) -/// iSCSI connection flags: Rejected. -#define ISCSI_CONNECT_FLAGS_REJECTED (1 << 1) - -/// iSCSI connection flags: Logged out. -#define ISCSI_CONNECT_FLAGS_LOGGED_OUT (1 << 2) - /// iSCSI connection flags: Full feature. #define ISCSI_CONNECT_FLAGS_FULL_FEATURE (1 << 3) -/// iSCSI connection flags: CHAP authentication is disabled. -#define ISCSI_CONNECT_FLAGS_CHAP_DISABLE (1 << 4) - -/// iSCSI connection flags: CHAP authentication is required. -#define ISCSI_CONNECT_FLAGS_CHAP_REQUIRE (1 << 5) - -/// iSCSI connection flags: CHAP authentication is mutual. -#define ISCSI_CONNECT_FLAGS_CHAP_MUTUAL (1 << 6) - -/// iSCSI connection flags: Authenticated. -#define ISCSI_CONNECT_FLAGS_AUTH (1 << 7) - /// iSCSI connection flags: Oustanding NOP. #define ISCSI_CONNECT_FLAGS_NOP_OUTSTANDING (1 << 8) @@ -6132,13 +6102,13 @@ typedef struct iscsi_pdu iscsi_pdu; #define ISCSI_CONNECT_PDU_RECV_STATE_ERR 3 -/// iSCSI connection state: Invalid. -#define ISCSI_CONNECT_STATE_INVALID 0 +/// iSCSI connection state: Fresh connection, no login yet. +#define ISCSI_CONNECT_STATE_NEW 0 -/// iSCSI connection state: Running. -#define ISCSI_CONNECT_STATE_RUNNING 1 +/// iSCSI connection state: Running as session type "normal". +#define ISCSI_CONNECT_STATE_NORMAL_SESSION 1 -/// iSCSI connection state: Exiting. +/// iSCSI connection state: Exiting, teardown of connection imminent. #define ISCSI_CONNECT_STATE_EXITING 2 @@ -6161,18 +6131,9 @@ typedef struct iscsi_connection { /// Associated dnbd3 client dnbd3_client_t *client; - /// Current PDU being processed. - iscsi_pdu *pdu_processing; - - /// Login response PDU. - iscsi_pdu *login_response_pdu; - /// Internal connection identifier int id; - /// iSCSI connection receiving state. - int pdu_recv_state; - /// iSCSI connection flags. int flags; @@ -6214,6 +6175,8 @@ typedef struct iscsi_task iscsi_task; /// iSCSI PDU flags: Rejected. #define ISCSI_PDU_FLAGS_REJECTED (1 << 0) +/// iSCSI PDU will contain a small buffer for sending/receiving trivial PDUs with no/very small DS, and small AH +#define ISCSI_INTERNAL_BUFFER_SIZE (2 * ISCSI_BHS_SIZE) /** * @brief This structure is used to partially read PDU data. @@ -6232,9 +6195,6 @@ typedef struct iscsi_pdu { /// iSCSI DataSegment (DS) packet data for fast access and is straight after BHS, AHS and header digest packet in memory. iscsi_scsi_ds_cmd_data *ds_cmd_data; - /// iSCSI task handling this PDU. - iscsi_task *task; - /// Flags. int flags; @@ -6250,11 +6210,14 @@ typedef struct iscsi_pdu { /// DS Buffer write pos when filling buffer for sending. uint32_t ds_write_pos; - /// Read position in AHS + DS buffer (recv). - uint32_t recv_pos; - /// CmdSN. uint32_t cmd_sn; + + /// If we need a larger area than internal_buffer + void *big_alloc; + + /// Used for smaller PDUs to avoid extra malloc/free + char internal_buffer[ISCSI_INTERNAL_BUFFER_SIZE]; } iscsi_pdu; diff --git a/src/shared/sockhelper.c b/src/shared/sockhelper.c index 08d73fc..b3f479a 100644 --- a/src/shared/sockhelper.c +++ b/src/shared/sockhelper.c @@ -440,7 +440,7 @@ ssize_t sock_recv(const int sock, void *buffer, const size_t len) return (ssize_t)done; } -bool sock_sendPadding(const int fd, uint32_t bytes) +bool sock_sendPadding(const int fd, size_t bytes) { static char nullbytes[512] = {0}; |
