diff options
| author | Simon Rettberg | 2025-10-29 18:05:00 +0100 |
|---|---|---|
| committer | Simon Rettberg | 2025-12-09 15:33:20 +0100 |
| commit | 062df52ab9f3464ce79ca91d54fa3d2ad209a31b (patch) | |
| tree | 2bb2c53a0e29a9691a77199fa631a007e4d96fa2 /src/server | |
| parent | [SERVER] iscsi: Honor global _shutdown (diff) | |
| download | dnbd3-062df52ab9f3464ce79ca91d54fa3d2ad209a31b.tar.gz dnbd3-062df52ab9f3464ce79ca91d54fa3d2ad209a31b.tar.xz dnbd3-062df52ab9f3464ce79ca91d54fa3d2ad209a31b.zip | |
[SERVER] iscsi: Refactor receive function and PDU handling
- Fold header/data handling into one function
This uncovered a few redundant checks
and makes it easier to reason about control flow
- Make all iscsi_pdu stack-allocated
This greatly reduces the number of malloc and free calls
during normal operation, lowers the risk of memory management
bugs, and potentially increases performance in high concurrency
scenarios.
Diffstat (limited to 'src/server')
| -rw-r--r-- | src/server/iscsi.c | 1540 | ||||
| -rw-r--r-- | src/server/iscsi.h | 129 |
2 files changed, 615 insertions, 1054 deletions
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; |
