summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/server/iscsi.c693
-rw-r--r--src/server/iscsi.h767
2 files changed, 1339 insertions, 121 deletions
diff --git a/src/server/iscsi.c b/src/server/iscsi.c
index 73be352..7dd35b1 100644
--- a/src/server/iscsi.c
+++ b/src/server/iscsi.c
@@ -1,4 +1,4 @@
- /*
+/*
* This file is part of the Distributed Network Block Device 3
*
* Copyright(c) 2025 Sebastian Vater <sebastian.vater@rz.uni-freiburg.de>
@@ -27,6 +27,7 @@
#include <inttypes.h>
#include <strings.h>
#include <sys/socket.h>
+#include <sys/types.h>
#include <dnbd3/shared/log.h>
#include <dnbd3/shared/sockhelper.h>
#include <dnbd3/types.h>
@@ -2356,19 +2357,92 @@ static int iscsi_update_bool_key_value_pair(iscsi_hashmap *key_value_pairs, cons
}
/**
- * @brief Enqueues an iSCSI task into it's underlying SCSI management structure.
+ * @brief Allocates and initializes an iSCSI task structure.
*
- * This function enqueues an iSCSI task into
- * the underlying SCSI task management for
- * execution.
+ * This function also initializes the underlying
+ * SCSI task structure with the transfer complete
+ * callback function.\n
+ * If a parent task is specified, SCSI data
+ * is copied over from it.
*
- * @param[in] task Pointer to iSCSI task to be put into
- * the SCSI task management, may NOT be NULL,
- * so be careful.
+ * @param[in] conn Pointer to iSCSI connection to associate
+ * the task with. May NOT be NULL, so take
+ * caution.
+ * @param[in] parent Pointer to parent iSCSI task to copy
+ * over SCSI task data from.
+ * @param[in] callback Callback function to be invoked
+ * after data transfer has been completed and
+ * may be NULL in case no further action is
+ * required.
+ * @return Pointer to iSCSI task structure or NULL
+ * in case of an error (memory exhaustion).
+ */
+iscsi_task *iscsi_task_create(iscsi_connection *conn, iscsi_task *parent, iscsi_scsi_task_xfer_complete_callback callback)
+{
+ iscsi_task *task = (iscsi_task *) malloc( sizeof(struct iscsi_task) );
+
+ if ( task == NULL ) {
+ logadd( LOG_ERROR, "iscsi_task_create: Out of memory while allocating iSCSI task" );
+
+ return NULL;
+ }
+
+ task->parent = parent;
+ task->conn = conn;
+ task->pdu = NULL;
+ task->buf = NULL;
+ task->pos = 0UL;
+ task->len = 0UL;
+ task->flags = 0L;
+ task->lun_id = 0L;
+ task->init_task_tag = 0UL;
+ task->target_xfer_tag = 0UL;
+ task->des_data_xfer_len = 0UL;
+ task->r2t_len = 0UL;
+ task->r2t_sn = 0UL;
+ task->r2t_next_exp_pos = 0UL;
+ task->r2t_data_sn = 0UL;
+ task->r2t_sn_ack = 0UL;
+
+ conn->task_cnt++;
+
+ iscsi_scsi_task_create( &task->scsi_task, callback, iscsi_task_destroy );
+
+ if ( parent != NULL ) {
+ parent->scsi_task.ref++;
+
+ task->target_xfer_tag = parent->target_xfer_tag;
+ task->lun_id = parent->lun_id;
+
+ task->scsi_task.flags = parent->scsi_task.flags;
+ task->scsi_task.xfer_len = parent->scsi_task.xfer_len;
+ task->scsi_task.lun = parent->scsi_task.lun;
+ task->scsi_task.cdb = parent->scsi_task.cdb;
+ task->scsi_task.target_port = parent->scsi_task.target_port;
+ task->scsi_task.init_port = parent->scsi_task.init_port;
+
+ if ( (task->scsi_task.flags & ISCSI_SCSI_TASK_FLAGS_XFER_READ) != 0 )
+ conn->scsi_data_read_cnt++;
+ }
+
+ return task;
+}
+
+/**
+ * @brief Deallocates resources acquired by iscsi_task_create.
+ *
+ * This function also frees the embedded SCSI task.
+ *
+ * @param[in] task Pointer to iSCSI task to deallocate. May be
+ * NULL in which case this function does nothing.
*/
-void iscsi_task_put(iscsi_task *task)
+void iscsi_task_destroy(iscsi_task *task)
{
- iscsi_scsi_task_put( &task->scsi_task );
+ if ( task != NULL ) {
+ iscsi_scsi_task_destroy( &task->scsi_task );
+
+ free( task );
+ }
}
/**
@@ -2415,7 +2489,7 @@ int iscsi_task_find_callback(uint8_t *key, const size_t key_size, uint8_t *value
*
* @param[in] conn Pointer to iSCSI connection to
* search in the active Ready To Transfer tasks
- * hash map.
+ * hash map and may NOT be NULL, so be careful.
* @param[in] target_xfer_tag Target Transfer Tag (TTT)
* to be searched for.
* @return Pointer to found iSCSI task or NULL in
@@ -2432,6 +2506,157 @@ static iscsi_task *iscsi_task_find(iscsi_connection *conn, const uint32_t target
}
/**
+ * @brief Handles iSCSI task read (incoming) data.
+ *
+ * This function handles iSCSI incoming data
+ * read buffer for both processed and
+ * unprocessed tasks.
+ *
+ * @param[in] conn Pointer to iSCSI connection of which the
+ * incoming data should be handled, may NOT be
+ * NULL, so be careful.
+ * @param[in] task Pointer to iSCSI task for handling
+ * the incoming data. NULL is NOT allowed here,
+ * take caution.
+ * @return 0 on successful incoming transfer handling,
+ * a negative error code otherwise.
+ */
+static int iscsi_task_xfer_in(iscsi_connection *conn, iscsi_task *task)
+{
+ // TODO: Implement function.
+
+ return 0L;
+}
+
+/**
+ * @brief Creates, initializes and sends an iSCSI task reponse PDU.
+ *
+ * This function also receives any remaining
+ * incoming data in case the task is reading.
+ *
+ * @param[in] conn Pointer to iSCSI connection to handle the
+ * task resnponse for 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.
+ */
+void iscsi_task_response(iscsi_connection *conn, iscsi_task *task)
+{
+ iscsi_task *primary_task = (task->parent != NULL) ? task->parent : task;
+ iscsi_pdu *pdu = primary_task->pdu;
+ iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) pdu->bhs_pkt;
+ const uint32_t xfer_len = primary_task->scsi_task.xfer_len;
+
+ if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_READ) != 0 ) {
+ const int rc = iscsi_task_xfer_in( conn, task );
+
+ if ( (rc < 0) || (primary_task->pos != xfer_len) )
+ return;
+ }
+
+ iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn );
+
+ if ( response_pdu == NULL ) {
+ logadd( LOG_ERROR, "iscsi_task_response: Out of memory while allocating iSCSI SCSI response PDU" );
+
+ return;
+ }
+
+ iscsi_scsi_response_packet *scsi_response_pkt;
+ uint32_t ds_len;
+
+ if ( task->scsi_task.sense_data_len != 0 ) {
+ ds_len = (task->scsi_task.sense_data_len + offsetof(struct iscsi_scsi_ds_cmd_data, sense_data));
+ scsi_response_pkt = (iscsi_scsi_response_packet *) iscsi_append_ds_packet( response_pdu->bhs_pkt, conn->header_digest, ds_len, conn->data_digest );
+
+ if ( scsi_response_pkt == NULL ) {
+ logadd( LOG_ERROR, "iscsi_task_response: Out of memory while allocating iSCSI SCSI response packet data" );
+
+ iscsi_connection_pdu_destroy( response_pdu );
+
+ return;
+ }
+
+ response_pdu->bhs_pkt = (iscsi_bhs_packet *) scsi_response_pkt;
+
+ if ( conn->header_digest != 0 ) {
+ response_pdu->header_digest = (iscsi_header_digest *) (((iscsi_bhs_packet *) scsi_response_pkt) + 1);
+ response_pdu->header_digest_size = conn->header_digest;
+ }
+
+ response_pdu->ds_cmd_data = (iscsi_scsi_ds_cmd_data *) (((uint8_t *) scsi_response_pkt) + sizeof(struct iscsi_bhs_packet) + conn->header_digest);
+ response_pdu->ds_len = ds_len;
+
+ if ( conn->data_digest != 0 ) {
+ response_pdu->data_digest = (iscsi_data_digest *) (((uint8_t *) response_pdu->ds_cmd_data) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE));
+ response_pdu->data_digest_size = conn->data_digest;
+ }
+
+ 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_be24( (uint8_t *) &scsi_response_pkt->ds_len, ds_len );
+ } else {
+ ds_len = 0uL;
+
+ if ( conn->header_digest != 0 ) {
+ scsi_response_pkt = (iscsi_r2t_packet *) iscsi_append_header_digest_packet( response_pdu->bhs_pkt, conn->header_digest );
+
+ if ( scsi_response_pkt == NULL ) {
+ logadd( LOG_ERROR, "iscsi_task_response: Out of memory while allocating iSCSI SCSI response packet data" );
+
+ iscsi_connection_pdu_destroy( response_pdu );
+
+ return;
+ }
+
+ response_pdu->bhs_pkt = (iscsi_bhs_packet *) scsi_response_pkt;
+ response_pdu->header_digest = (iscsi_header_digest *) (((iscsi_bhs_packet *) scsi_response_pkt) + 1);
+ response_pdu->header_digest_size = conn->header_digest;
+ } else {
+ scsi_response_pkt = (iscsi_scsi_response_packet *) response_pdu->bhs_pkt;
+ }
+ }
+
+ response_pdu->task = task;
+ task->scsi_task.ref++;
+
+ scsi_response_pkt->opcode = ISCSI_OPCODE_SERVER_SCSI_RESPONSE;
+ scsi_response_pkt->flags = -0x80;
+
+ const uint32_t pos = primary_task->scsi_task.pos;
+
+ if ( (xfer_len != 0) && (task->scsi_task.status == ISCSI_SCSI_STATUS_GOOD) ) {
+ if ( pos < xfer_len ) {
+ const uint32_t res_cnt = (xfer_len - pos);
+
+ scsi_response_pkt->flags |= ISCSI_SCSI_RESPONSE_FLAGS_RES_UNDERFLOW;
+ iscsi_put_be32( (uint8_t *) &scsi_response_pkt->res_cnt, res_cnt );
+ } else if ( pos > xfer_len ) {
+ const uint32_t res_cnt = (pos - xfer_len);
+
+ scsi_response_pkt->flags |= ISCSI_SCSI_RESPONSE_FLAGS_RES_OVERFLOW;
+ iscsi_put_be32( (uint8_t *) &scsi_response_pkt->res_cnt, res_cnt );
+ }
+ }
+
+ scsi_response_pkt->status = task->scsi_task.status;
+ iscsi_put_be32( (uint8_t *) &scsi_response_pkt->init_task_tag, task->init_task_tag );
+ iscsi_put_be32( (uint8_t *) &scsi_response_pkt->stat_sn, conn->stat_sn++ );
+
+ if ( (scsi_cmd_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 )
+ conn->session->max_cmd_sn++;
+
+ iscsi_put_be32( (uint8_t *) &scsi_response_pkt->exp_cmd_sn, conn->session->exp_cmd_sn );
+ iscsi_put_be32( (uint8_t *) &scsi_response_pkt->max_cmd_sn, conn->session->max_cmd_sn );
+
+ iscsi_connection_pdu_write( conn, response_pdu, NULL, NULL );
+}
+
+/**
* @brief Creates and initializes an iSCSI portal group.
*
* Specified tag and flags are used for portal group
@@ -2499,6 +2724,7 @@ int iscsi_portal_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *
*
* This function frees the associated hash map containing the
* poptals and the structure itself.
+ *
* @param[in] portal_group Pointer to iSCSI portal group to deallocate.
* May be NULL in which case this function does nothing.
*/
@@ -2645,20 +2871,185 @@ void iscsi_portal_destroy(iscsi_portal *portal)
}
/**
- * @brief Enqueues a SCSI task for execution.
+ * @brief Allocates and initializes a SCSI task.
*
- * This function decreases the reference
- * counter and after execution is done,
- * the associated I/O resources will be
- * freed.
+ * THis function assocates the callback
+ * functions to the SCSI task and sets
+ * the reference count to 1.
*
- * @param[in] scsi_task Pointer to SCSI task structure to
- * enqueue. NULL is NOT allowed here, so
- * take caution.
+ * @param[in] scsi_task Pointer to SCSI task. This
+ * may NOT be NULL, so be careful.
+ * @param[in] xfer_complete_callback Pointer to transfer completed callback
+ * function.
+ * @param[in] destroy_callback Pointer to SCSI task destruction
+ * callback function.
+ */
+void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task, iscsi_scsi_task_xfer_complete_callback xfer_complete_callback, iscsi_scsi_task_destroy_callback destroy_callback)
+{
+ scsi_task->lun = NULL;
+ scsi_task->target_port = NULL;
+ scsi_task->init_port = NULL;
+ scsi_task->cdb = NULL;
+ scsi_task->xfer_complete_callback = xfer_complete_callback;
+ scsi_task->destroy_callback = destroy_callback;
+ scsi_task->sense_data = NULL;
+ scsi_task->buf = NULL;
+ scsi_task->pos = 0UL;
+ scsi_task->len = 0UL;
+ scsi_task->flags = 0L;
+ scsi_task->ref = 1UL;
+ scsi_task->xfer_len = 0UL;
+ scsi_task->sense_data_len = 0U;
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+ scsi_task->task_mgmt_func = ISCSI_TASK_MGMT_FUNC_REQ_FUNC_ABORT_TASK;
+ scsi_task->task_mgmt_response = ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_COMPLETE;
+}
+
+/**
+ * @brief Deallocates all resources acquired iscsi_scsi_task_create.
+ *
+ * This function also calls the task destruction
+ * callback function if the reference count
+ * becomes zero.
+ *
+ * @param[in] scsi_task SCSI task to deallocate. This may
+ * be NULL in which case nothing happens.
+ */
+void iscsi_scsi_task_destroy(iscsi_scsi_task *scsi_task)
+{
+ if ( scsi_task != NULL ) {
+ if ( --scsi_task->ref == 0 )
+ scsi_task->destroy_callback( scsi_task );
+ }
+}
+
+/**
+ * @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.
+ */
+void iscsi_scsi_task_xfer_complete(iscsi_scsi_task *scsi_task)
+{
+ // TODO: Implement function.
+}
+
+/**
+ * @brief Allocates, if necessary and initializes SCSI sense data for check condition status code.
+ *
+ * This function is invoked whenever additional
+ * SCSI sense data for check condition status
+ * code is required for sending to the
+ * initiator.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to allocate
+ * and assign the SCSI check condition status
+ * code sense data for. May NOT be NULL, so
+ * be careful.
+ * @param[in] sense_key Sense Key (SK).
+ * @param[in] asc Additional Sense Code (ASC).
+ * @param[in] ascq Additional Sense Code Qualifier (ASCQ).
+ */
+void iscsi_scsi_task_sense_data_check_cond_build(iscsi_scsi_task *scsi_task, const uint8_t sense_key, const uint8_t asc, const uint8_t ascq)
+{
+ iscsi_scsi_sense_data_check_cond_packet *sense_data = (iscsi_scsi_sense_data_check_cond_packet *) scsi_task->sense_data;
+
+ if ( sense_data == NULL ) {
+ sense_data = malloc( sizeof(struct iscsi_scsi_sense_data_check_cond_packet) );
+
+ if ( sense_data == NULL ) {
+ logadd( LOG_ERROR, "iscsi_scsi_task_sense_data_build: Out of memory allocating iSCSI SCSI conidtion check status code sense data" );
+
+ return;
+ }
+
+ scsi_task->sense_data = sense_data;
+ }
+
+ sense_data->sense_data.response_code = (ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_CURRENT_FMT | ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_VALID);
+ sense_data->sense_data.reserved = 0U;
+ sense_data->sense_data.sense_key_flags = (sense_key & ISCSI_SCSI_SENSE_DATA_SENSE_KEY_MASK);
+ sense_data->sense_data.info = 0UL; // Zero does not require endianess conversion
+ sense_data->sense_data.add_len = (sizeof(struct iscsi_scsi_sense_data_check_cond_packet) - sizeof(struct iscsi_scsi_sense_data_packet));
+
+ sense_data->cmd_spec_info = 0UL; // Zero does not require endianess conversion
+ sense_data->asc = asc;
+ sense_data->ascq = ascq;
+ sense_data->field_rep_unit_code = 0U;
+ sense_data->sense_key_spec_flags = 0U;
+ sense_data->sense_key_spec = 0U; // Zero does not require endianess conversion
+
+ scsi_task->sense_data_len = sizeof(struct iscsi_scsi_sense_data_check_cond_packet);
+}
+
+/**
+ * @brief Sets an iSCSI SCSI task status code with optional additional details.
+ *
+ * Sense Key (SK), Additional Sense Code (ASC)
+ * and Additional Sense Code Qualifier (ASCQ)
+ * are only generated on check condition SCSI
+ * status code.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to set the
+ * SCSI status and additional details for. May
+ * NOT be NULL, so be careful.
+ * @param[in] status SCSI status code to be set.
+ * @param[in] sense_key Sense Key (SK).
+ * @param[in] asc Additional Sense Code (ASC).
+ * @param[in] ascq Additional Sense Code Qualifier (ASCQ).
+ */
+static void iscsi_scsi_task_status_set(iscsi_scsi_task *scsi_task, const uint8_t status, const uint8_t sense_key, const uint8_t asc, const uint8_t ascq)
+{
+ if ( status == ISCSI_SCSI_STATUS_CHECK_COND )
+ iscsi_scsi_task_sense_data_check_cond_build( scsi_task, sense_key, asc, ascq );
+
+ scsi_task->status = status;
+}
+
+/**
+ * @brief Processes a iSCSI SCSI task with no LUN identifier.
+ *
+ * This function only generates a SCSI response
+ * if the SCSI command is INQUIRY, otherwise
+ * a SCSI error will be generated as specified
+ * by the SCSI standard.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to process
+ * the task with no LUN identifier for. May NOT
+ * be NULL, so be careful.
*/
-void iscsi_scsi_task_put(iscsi_scsi_task *scsi_task)
+void iscsi_scsi_task_lun_process_none(iscsi_scsi_task *scsi_task)
{
- // TODO: Implement SCSI function.
+ iscsi_scsi_std_inquiry_data_packet std_inquiry_data_pkt;
+
+ scsi_task->len = scsi_task->xfer_len;
+
+ if ( scsi_task->cdb->opcode == ISCSI_SCSI_OPCODE_INQUIRY ) {
+ uint len = sizeof(struct iscsi_scsi_std_inquiry_data_packet);
+
+ memset( &std_inquiry_data_pkt, 0, len );
+
+ std_inquiry_data_pkt.peripheral_type_id = ((ISCSI_SCSI_DATA_PERIPHERAL_TYPE_UNKNOWN << ISCSI_SCSI_DATA_PERIPHERAL_TYPE_FIRST_BIT) | (ISCSI_SCSI_DATA_PERIPHERAL_ID_NEVER << ISCSI_SCSI_DATA_PERIPHERAL_ID_FIRST_BIT));
+ std_inquiry_data_pkt.add_len = (uint8_t) (len - sizeof(struct iscsi_scsi_data_packet));
+
+ const uint alloc_len = iscsi_get_be16(scsi_task->cdb->alloc_len);
+
+ if ( len > alloc_len )
+ len = alloc_len;
+
+ memcpy( scsi_task->buf, &std_inquiry_data_pkt, len );
+
+ scsi_task->pos = len;
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+ } else {
+ 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->pos = 0UL;
+ }
}
/**
@@ -3554,7 +3945,8 @@ iscsi_session *iscsi_session_create(iscsi_connection *conn, iscsi_target_node *t
* @brief Deallocates all resources acquired by iscsi_session_create.
*
* This function also frees the associated key and value pairs,
- * the attached connections as well as frees the initator port.
+ * the attached connections as well as frees the initiator
+ * port.
*
* @param[in] session Pointer to iSCSI session to be freed.
* May be NULL in which case this function does nothing at all.
@@ -3580,10 +3972,10 @@ void iscsi_session_destroy(iscsi_session *session)
session->connections = NULL;
}
- if ( session->initiator_port != NULL ) {
- iscsi_port_destroy( session->initiator_port );
+ if ( session->init_port != NULL ) {
+ iscsi_port_destroy( session->init_port );
- session->initiator_port = NULL;
+ session->init_port = NULL;
}
free( session ); // TODO: Check if potential reusage of session makes sense.
@@ -3718,8 +4110,22 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock)
conn->r2t_tasks_active = iscsi_hashmap_create( 0UL );
if ( conn->r2t_tasks_active == NULL ) {
- logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI active Ready To Transfer task hash map" );
+ logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI active Ready To Transfer (R2T) task hash map" );
+
+ iscsi_hashmap_destroy( conn->pdu_snack );
+ iscsi_hashmap_iterate( conn->key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( conn->key_value_pairs );
+ free( conn );
+
+ return NULL;
+ }
+
+ conn->r2t_tasks_queue = iscsi_hashmap_create( 0UL );
+ if ( conn->r2t_tasks_queue == NULL ) {
+ logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI enqueued Ready To Transfer (R2T) task hash map" );
+
+ iscsi_hashmap_destroy( conn->r2t_tasks_active );
iscsi_hashmap_destroy( conn->pdu_snack );
iscsi_hashmap_iterate( conn->key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
iscsi_hashmap_destroy( conn->key_value_pairs );
@@ -3729,6 +4135,8 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock)
}
conn->target_send_total_size = 0UL;
+ conn->scsi_data_read_cnt = 0UL;
+ conn->task_cnt = 0UL;
conn->header_digest = 0L;
conn->data_digest = 0L;
conn->id = 0L;
@@ -3796,6 +4204,13 @@ int iscsi_connection_destroy_callback(uint8_t *key, const size_t key_size, uint8
void iscsi_connection_destroy(iscsi_connection *conn)
{
if ( conn != NULL ) {
+ if ( conn->r2t_tasks_queue != NULL ) {
+ iscsi_hashmap_iterate( conn->r2t_tasks_queue, iscsi_hashmap_key_destroy_callback, NULL );
+ iscsi_hashmap_destroy( conn->r2t_tasks_queue );
+
+ conn->r2t_tasks_queue = NULL;
+ }
+
if ( conn->r2t_tasks_active != NULL ) {
iscsi_hashmap_iterate( conn->r2t_tasks_active, iscsi_hashmap_key_destroy_callback, NULL );
iscsi_hashmap_destroy( conn->r2t_tasks_active );
@@ -4632,7 +5047,7 @@ static int iscsi_connection_update_key_value_pairs(iscsi_connection *conn)
* be sent via TCP/IP.
* @param[in] key_value_pairs Pointer to hash map of key and value pairs
* to be used for login response storage.
- * @paran[in] callback Pointer to post processing callback function
+ * @param[in] callback Pointer to post processing callback function
* after sending the TCP/IP packet.
*/
static void iscsi_connection_pdu_login_response(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_hashmap *key_value_pairs, iscsi_connection_xfer_complete_callback callback)
@@ -4735,7 +5150,7 @@ static int iscsi_login_response_init(iscsi_pdu *login_response_pdu, const iscsi_
}
login_response_pdu->bhs_pkt = (iscsi_bhs_packet *) login_response_pkt;
- login_response_pdu->ds_cmd_data = (iscsi_ds_cmd_data *) (((uint8_t *) login_response_pkt) + sizeof(struct iscsi_bhs_packet) + pdu->header_digest_size);
+ login_response_pdu->ds_cmd_data = (iscsi_scsi_ds_cmd_data *) (((uint8_t *) login_response_pkt) + sizeof(struct iscsi_bhs_packet) + pdu->header_digest_size);
login_response_pdu->len = ISCSI_DEFAULT_RECV_DS_LEN;
login_response_pkt->flags |= (int8_t) (login_req_pkt->flags & (ISCSI_LOGIN_REQ_FLAGS_TRANSIT | ISCSI_LOGIN_REQ_FLAGS_CONTINUE | ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_MASK));
@@ -4844,8 +5259,8 @@ static inline uint64_t iscsi_connection_get_isid(const iscsi_isid *isid)
* @param[in] response_pdu Pointer to response PDU to initialize the
* port from, NULL is NOT allowed here, so be careful.
* @param[in] key_value_pairs Pointer to the hash map containing the key
- * and value pair for the initator name. May NOT be NULL,
- * so take caution.
+ * and value pair for the initiator name. May NOT be
+ * NULL, so take caution.
* @param[out] init_port_name Pointer to store the full qualified name
* of the initiator port and may NOT be NULL, so be careful.
* @return 0 in case the port could be initialized
@@ -5066,7 +5481,7 @@ static uint16_t iscsi_session_append(iscsi_connection *conn, const uint8_t *init
{
iscsi_session *session = iscsi_session_get_by_tsih( tsih );
- if ( (session == NULL) || (conn->pg_tag != session->tag) || (strcasecmp( (char *) init_port_name, (char *) iscsi_port_get_name( session->initiator_port ) ) != 0) || (conn->target != session->target) )
+ if ( (session == NULL) || (conn->pg_tag != session->tag) || (strcasecmp( (char *) init_port_name, (char *) iscsi_port_get_name( session->init_port ) ) != 0) || (conn->target != session->target) )
return (ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR << 8U) | ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_NO_SESSION_SPANNING;
if ( iscsi_hashmap_size( session->connections ) >= session->max_conns )
@@ -5179,6 +5594,7 @@ iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn)
pdu->ds_cmd_data = NULL;
pdu->data_digest = NULL;
pdu->task = NULL;
+ pdu->conn = conn;
pdu->xfer_complete_callback = NULL;
pdu->xfer_complete_user_data = NULL;
pdu->flags = 0L;
@@ -5192,7 +5608,7 @@ iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn)
pdu->ds_len = 0UL;
pdu->pos = 0UL;
pdu->len = 0UL;
- pdu->conn = conn;
+ pdu->task_ref_cnt = 0UL;
pdu->cmd_sn = 0UL;
return pdu;
@@ -5234,7 +5650,7 @@ void iscsi_connection_pdu_free(iscsi_connection *conn, iscsi_pdu *pdu)
pdu->xfer_complete_callback = NULL;
if ( pdu->task != NULL )
- iscsi_task_put( pdu->task );
+ iscsi_task_destroy( pdu->task );
iscsi_connection_pdu_destroy( pdu );
@@ -5414,7 +5830,7 @@ static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu
response_pdu->header_digest_size = conn->header_digest;
}
- response_pdu->ds_cmd_data = (iscsi_ds_cmd_data *) (((uint8_t *) reject_pkt) + sizeof(struct iscsi_bhs_packet) + conn->header_digest);
+ response_pdu->ds_cmd_data = (iscsi_scsi_ds_cmd_data *) (((uint8_t *) reject_pkt) + sizeof(struct iscsi_bhs_packet) + conn->header_digest);
response_pdu->ds_len = ds_len;
if ( conn->data_digest != 0 ) {
@@ -5570,7 +5986,7 @@ static int iscsi_connection_pdu_header_handle_nop_out(iscsi_connection *conn, is
if ( (init_task_tag == 0xFFFFFFFFUL) && (nop_out_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 )
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
- return 0L;
+ return ISCSI_CONNECT_PDU_READ_OK;
}
/**
@@ -5590,9 +6006,91 @@ static int iscsi_connection_pdu_header_handle_nop_out(iscsi_connection *conn, is
*/
static int iscsi_connection_pdu_header_handle_scsi_cmd(iscsi_connection *conn, iscsi_pdu *pdu)
{
- // TODO: Implement opcode.
+ if ( conn->session->type != ISCSI_SESSION_TYPE_NORMAL )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
- return 0;
+ 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) != 0) && ((scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_WRITE) != 0) ) // Bidirectional transfer is not supported
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ iscsi_task *task = iscsi_task_create( conn, NULL, iscsi_scsi_task_xfer_complete );
+
+ 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.buf = (uint8_t *) pdu->ds_cmd_data;
+ 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->scsi_task.target_port = conn->target_port;
+ task->scsi_task.init_port = conn->init_port;
+ task->init_task_tag = iscsi_get_be32(scsi_cmd_pkt->init_task_tag);
+ task->pdu = pdu;
+
+ const uint64_t lun = iscsi_get_be64(scsi_cmd_pkt->lun);
+ const int lun_id = iscsi_lun_get_from_iscsi( lun );
+
+ task->scsi_task.lun = iscsi_device_find_lun( conn->device, lun_id );
+
+ if ( task->scsi_task.lun == NULL ) {
+ iscsi_scsi_task_lun_process_none( &task->scsi_task );
+ iscsi_scsi_task_xfer_complete( &task->scsi_task );
+
+ return ISCSI_CONNECT_PDU_READ_OK;
+ }
+
+ if ( ((scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_READ) == 0) && ((scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_WRITE) == 0) && (exp_xfer_len > 0) ) {
+ 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->flags |= ISCSI_SCSI_TASK_FLAGS_XFER_READ;
+
+ if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_WRITE) != 0 ) {
+ task->flags |= ISCSI_SCSI_TASK_FLAGS_XFER_WRITE;
+
+ if ( (conn->session->err_recovery_level > 0) && (iscsi_r2t_find_pdu_bhs( conn, pdu ) != NULL) ) {
+ iscsi_task_response( conn, task );
+ iscsi_task_destroy( task );
+
+ return ISCSI_CONNECT_PDU_READ_OK;
+ }
+
+ if ( pdu->ds_len > (uint) (sizeof(struct iscsi_bhs_packet) + ISCSI_MAX_AHS_SIZE + conn->header_digest + ISCSI_SESSION_DEFAULT_FIRST_BURST_LEN + conn->data_digest) ) {
+ iscsi_task_destroy( task );
+
+ return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR );
+ }
+
+ if ( pdu->ds_len > exp_xfer_len ) {
+ iscsi_task_destroy( task );
+
+ return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR );
+ }
+
+ if ( (((conn->session->flags & ISCSI_SESSION_FLAGS_IMMEDIATE_DATA) == 0) && (pdu->ds_len > 0)) || (pdu->ds_len > conn->session->first_burst_len) ) {
+ iscsi_task_destroy( task );
+
+ return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR );
+ }
+
+ if ( ((scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_FINAL) != 0) && (pdu->ds_len < exp_xfer_len) ) {
+ if ( exp_xfer_len > ISCSI_DEFAULT_MAX_RECV_DS_LEN )
+ exp_xfer_len = ISCSI_DEFAULT_MAX_RECV_DS_LEN;
+
+ pdu->len = exp_xfer_len;
+ }
+ }
+
+ pdu->task = task;
+ pdu->task_ref_cnt++;
+
+ return ISCSI_CONNECT_PDU_READ_OK;
}
/**
@@ -5657,6 +6155,74 @@ static int iscsi_connection_pdu_header_handle_text_req(iscsi_connection *conn, i
}
/**
+ * @brief Finds an iSCSI PDU by Basic Header Segment (BHS) in either the Ready To Transfer (R2T) active and queued task hash map.
+ *
+ * Callback function for each element while iterating
+ * either through the iSCSI active or queued Ready To
+ * Transfer (R2T) task hash map.
+ *
+ * @param[in] key Pointer to zero padded key. NULL is
+ * an invalid pointer here, so be careful.
+ * @param[in] key_size Number of bytes for the key, MUST
+ * be a multiple of 8 bytes which is NOT checked, so
+ * be careful.
+ * @param[in] value Value of the key, NULL creates an
+ * empty key assignment.
+ * @param[in,out] user_data Pointer to a data structure
+ * containing the iSCSI PDU and the Basic Header
+ * Segment (BHS) to be searched for and may NOT
+ * be NULL, so be careful.
+ * @retval -1 The PDU has been found and stored
+ * in the result structure. Therefore, no further
+ * searching is needed.
+ * @retval 0 The PDU has not been found yet.
+ */
+int iscsi_r2t_find_pdu_bhs_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
+{
+ iscsi_r2t_find_bhs *pdu_find_bhs = (iscsi_r2t_find_bhs *) user_data;
+ iscsi_task *task = (iscsi_task *) value;
+
+ if ( memcmp( task->pdu->bhs_pkt, pdu_find_bhs->bhs_pkt, sizeof(struct iscsi_bhs_packet) ) != 0 )
+ return 0L;
+
+ pdu_find_bhs->pdu = task->pdu;
+
+ return -1L;
+}
+
+/**
+ * @brief Searches an iSCSI PDU by Basic Header Segment (BHS) in the Ready To Transfer (R2T) active and queued task hash map.
+ *
+ * This function searches for an iSCSI PDU by
+ * iterating through the iSCSI connection active
+ * and queued Ready To Transfer tasks hash map.
+ *
+ * @param[in] conn Pointer to iSCSI connection to
+ * search in the active and queued Ready To
+ * Transfer tasks hash map. May NOT be NULL, so
+ * be careful.
+ * @param[in] pdu Pointer to iSCSI PDU of which
+ * the Basic Header Segment (BHS) should be
+ * searched for. NULL is NOT allowed here, so
+ * take caution.
+ * @return Pointer to found iSCSI PDU or NULL in
+ * case neither an iSCSI active nor enqueued
+ * Ready To Transfer (R2T) task has a matching
+ * Basic Header Segment (BHS).
+ */
+iscsi_pdu *iscsi_r2t_find_pdu_bhs(iscsi_connection *conn, iscsi_pdu *pdu)
+{
+ iscsi_r2t_find_bhs pdu_find_bhs = {NULL, pdu->bhs_pkt};
+
+ const int rc = iscsi_hashmap_iterate( conn->r2t_tasks_active, iscsi_r2t_find_pdu_bhs_callback, (uint8_t *) &pdu_find_bhs );
+
+ if ( rc == 0 )
+ iscsi_hashmap_iterate( conn->r2t_tasks_queue, iscsi_r2t_find_pdu_bhs_callback, (uint8_t *) &pdu_find_bhs );
+
+ return pdu_find_bhs.pdu;
+}
+
+/**
* @brief Sends an iSCSI Ready To Transfer Sequence Number (R2TSN) packet to the initiator.
*
* This function allocates and initializes a
@@ -5774,20 +6340,29 @@ int iscsi_r2t_remove_pdu_from_snack_list_callback(uint8_t *key, const size_t key
}
/**
- * @brief Searches an iSCSI task by Target Transfer Tag (TTT).
+ * @brief Searches an iSCSI PDU task by Ready To Transfer Sequence Number (R2TSN) and removes it from PDU SNACK hash map.
*
- * This function searches for an iSCSI task by
- * iterating through the iSCSI connection active
- * Ready To Transfer tasks hash map.
+ * This function searches for an iSCSI PDU task
+ * by iterating through the iSCSI connection
+ * Sequence Number Acknowledgement S(NACK)
+ * and matches the Ready To Transfer Sequence
+ * Number (R2TSN).\n
+ * If found, the PDU will be removed from the
+ * PDU SNACK list.
*
* @param[in] conn Pointer to iSCSI connection to
- * search in the active Ready To Transfer tasks
- * hash map.
- * @param[in] target_xfer_tag Target Transfer Tag (TTT)
- * to be searched for.
- * @return Pointer to found iSCSI task or NULL in
- * case no iSCSI task has a matching Target
- * Transfer Tag (TTT).
+ * search in the Sequence Number
+ * Acknowledgement (SNACK) hash map. May NOT be
+ * NULL, so be careful.
+ * @param[in] task Pointer to iSCSI task to search
+ * for in the Sequence Number Acknowledgement
+ * (SNACK) hash map. NULL is not allowed here,
+ * take caution.
+ * @param[in] r2t_sn Ready To Transfer Sequence Number
+ * (R2TSN) to be searched for.
+ * @return Pointer to found iSCSI PDU or NULL in
+ * case no iSCSI PDU has a matching Ready To Transfer
+ * Sequence Number (R2TSN).
*/
static iscsi_pdu *iscsi_r2t_remove_pdu_from_snack_list(iscsi_connection *conn, iscsi_task *task, const uint32_t r2t_sn)
{
@@ -5943,7 +6518,7 @@ static int iscsi_connection_pdu_header_handle_scsi_data_out(iscsi_connection *co
// TODO: Implement dif ctx stuff.
if ( task->buf != NULL ) {
- pdu->ds_cmd_data = (iscsi_ds_cmd_data *) (task->buf + task->len);
+ pdu->ds_cmd_data = (iscsi_scsi_ds_cmd_data *) (task->buf + task->len);
pdu->ds_len = ISCSI_DEFAULT_MAX_RECV_DS_LEN;
} else {
// TODO: Implement dif ctx stuff.
@@ -6203,7 +6778,7 @@ static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscs
response_pdu->header_digest_size = conn->header_digest;
}
- response_pdu->ds_cmd_data = (iscsi_ds_cmd_data *) (((uint8_t *) nop_in_pkt) + sizeof(struct iscsi_bhs_packet) + conn->header_digest);
+ response_pdu->ds_cmd_data = (iscsi_scsi_ds_cmd_data *) (((uint8_t *) nop_in_pkt) + sizeof(struct iscsi_bhs_packet) + conn->header_digest);
response_pdu->ds_len = ds_len;
if ( conn->data_digest != 0 ) {
@@ -6470,11 +7045,11 @@ static int iscsi_connection_login_set_info(iscsi_connection *conn, iscsi_pdu *lo
return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE;
}
- conn->session->initiator_port = init_port;
- conn->stat_sn = iscsi_get_be32(login_response_pkt->stat_sn);
- conn->session->isid = isid;
+ conn->session->init_port = init_port;
+ conn->stat_sn = iscsi_get_be32(login_response_pkt->stat_sn);
+ conn->session->isid = isid;
- const int rc = iscsi_port_transport_id_set( conn->session->initiator_port, conn->init_name, isid );
+ const int rc = iscsi_port_transport_id_set( conn->session->init_port, conn->init_name, isid );
if ( rc < 0 ) {
iscsi_session_destroy( conn->session );
@@ -6490,7 +7065,7 @@ static int iscsi_connection_login_set_info(iscsi_connection *conn, iscsi_pdu *lo
conn->session->max_cmd_sn = login_response_pdu->cmd_sn + conn->session->queue_depth - 1;
}
- conn->init_port = conn->session->initiator_port;
+ conn->init_port = conn->session->init_port;
return ISCSI_CONNECT_PDU_READ_OK;
}
@@ -7031,7 +7606,7 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc
response_pdu->header_digest_size = conn->header_digest;
}
- response_pdu->ds_cmd_data = (iscsi_ds_cmd_data *) (((uint8_t *) text_response_pkt) + sizeof(struct iscsi_bhs_packet) + conn->header_digest);
+ response_pdu->ds_cmd_data = (iscsi_scsi_ds_cmd_data *) (((uint8_t *) text_response_pkt) + sizeof(struct iscsi_bhs_packet) + conn->header_digest);
response_pdu->ds_len = ds_len;
if ( conn->data_digest != 0 ) {
@@ -7260,10 +7835,10 @@ int iscsi_connection_pdu_data_read(iscsi_connection *conn, iscsi_pdu *pdu)
if ( buf == NULL )
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
- pdu->bhs_pkt = (iscsi_bhs_packet *) buf;
- pdu->ahs_pkt = (iscsi_ahs_packet *) (((iscsi_bhs_packet *) pdu->bhs_pkt) + 1);
+ pdu->bhs_pkt = (iscsi_bhs_packet *) buf;
+ pdu->ahs_pkt = (iscsi_ahs_packet *) (((iscsi_bhs_packet *) pdu->bhs_pkt) + 1);
pdu->header_digest = (iscsi_header_digest *) (((uint8_t *) pdu->bhs_pkt) + sizeof(struct iscsi_bhs_packet) + pdu->ahs_len);
- pdu->ds_cmd_data = (iscsi_ds_cmd_data *) (((uint8_t *) pdu->bhs_pkt) + sizeof(struct iscsi_bhs_packet) + pdu->ahs_len + conn->header_digest);
+ pdu->ds_cmd_data = (iscsi_scsi_ds_cmd_data *) (((uint8_t *) pdu->bhs_pkt) + sizeof(struct iscsi_bhs_packet) + pdu->ahs_len + conn->header_digest);
if ( conn->data_digest != 0 )
pdu->data_digest = (iscsi_data_digest *) (((uint8_t *) pdu->bhs_pkt) + sizeof(struct iscsi_bhs_packet) + pdu->ahs_len + conn->header_digest + ds_len);
diff --git a/src/server/iscsi.h b/src/server/iscsi.h
index 67c1655..4af7bb8 100644
--- a/src/server/iscsi.h
+++ b/src/server/iscsi.h
@@ -33,6 +33,12 @@
#define DNBD3_ISCSI_H_
#include <inttypes.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <dnbd3/types.h>
+
+#include "globals.h"
+#include "image.h"
#if defined(__BIG_ENDIAN__) || (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN) || (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#define iscsi_get_be16(x) (x)
@@ -463,16 +469,6 @@ typedef struct __attribute__((packed)) iscsi_ahs_packet {
uint8_t data[0];
} iscsi_ahs_packet;
-/**
- * @brief iSCSI CDB packet data structure.
- *
- * There are 16 bytes in the CDB field to accommodate the commonly used
- * CDBs. Whenever the CDB is larger than 16 bytes, an Extended CDB AHS
- * MUST be used to contain the CDB spillover.
- */
-typedef struct __attribute__((packed)) iscsi_cdb {
- uint8_t data[16];
-} iscsi_cdb;
/**
* @brief iSCSI Extended CDB AHS packet data structure.
@@ -633,8 +629,37 @@ typedef struct __attribute__((packed)) iscsi_data_digest {
uint32_t crc32c;
} iscsi_data_digest;
+
+/**
+ * @brief iSCSI CDB packet data structure.
+ *
+ * There are 16 bytes in the CDB field to accommodate the commonly used
+ * CDBs. Whenever the CDB is larger than 16 bytes, an Extended CDB AHS
+ * MUST be used to contain the CDB spillover.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb {
+ /// SCSI opcode.
+ uint8_t opcode;
+
+ /// Logical Unit Number (LUN) and EVPD.
+ uint8_t lun_evpd;
+
+ /// Page code.
+ uint8_t page_code;
+
+ /// Allocation length in bytes.
+ uint16_t alloc_len;
+
+ /// Control.
+ uint8_t control;
+
+ /// Additional data.
+ uint8_t data[10];
+} iscsi_scsi_cdb;
+
+
/**
- * @brief iSCSI DataSegment Command packet structure.
+ * @brief iSCSI SCSI DataSegment Command packet structure.
*
* iSCSI targets MUST support and enable Autosense. If Status is CHECK
* CONDITION (0x02), then the data segment MUST contain sense data for
@@ -644,7 +669,7 @@ typedef struct __attribute__((packed)) iscsi_data_digest {
* response-related information (e.g., for a target failure, it may
* contain a vendor-specific detailed description of the failure).
*/
-typedef struct __attribute__((packed)) iscsi_ds_cmd_data {
+typedef struct __attribute__((packed)) iscsi_scsi_ds_cmd_data {
/// SenseLength: This field indicates the length of Sense Data.
uint16_t len;
@@ -653,7 +678,384 @@ typedef struct __attribute__((packed)) iscsi_ds_cmd_data {
/// Response Data.
uint8_t res_data[0];
-} iscsi_ds_cmd_data;
+} iscsi_scsi_ds_cmd_data;
+
+
+/// ISCSI SCSI Data peripheral type: Direct access device.
+#define ISCSI_SCSI_DATA_PERIPHERAL_TYPE_DIRECT 0x00
+
+/// ISCSI SCSI Data peripheral type: Sequential access device.
+#define ISCSI_SCSI_DATA_PERIPHERAL_TYPE_SEQ 0x01
+
+/// ISCSI SCSI Data peripheral type: Printer device.
+#define ISCSI_SCSI_DATA_PERIPHERAL_TYPE_PRINTER 0x02
+
+/// ISCSI SCSI Data peripheral type: Processor device.
+#define ISCSI_SCSI_DATA_PERIPHERAL_TYPE_PROCESSOR 0x03
+
+/// ISCSI SCSI Data peripheral type: Write once device.
+#define ISCSI_SCSI_DATA_PERIPHERAL_TYPE_WORM 0x04
+
+/// ISCSI SCSI Data peripheral type: Read only direct access (e.g. CD-ROM) device.
+#define ISCSI_SCSI_DATA_PERIPHERAL_TYPE_RO_DIRECT 0x05
+
+/// ISCSI SCSI Data peripheral type: Scanner device.
+#define ISCSI_SCSI_DATA_PERIPHERAL_TYPE_SCANNER 0x06
+
+/// ISCSI SCSI Data peripheral type: Optical memory device.
+#define ISCSI_SCSI_DATA_PERIPHERAL_TYPE_OPTICAL 0x07
+
+/// ISCSI SCSI Data peripheral type: Medium changer device.
+#define ISCSI_SCSI_DATA_PERIPHERAL_TYPE_CHANGER 0x08
+
+/// ISCSI SCSI Data peripheral type: Communications device.
+#define ISCSI_SCSI_DATA_PERIPHERAL_TYPE_COMM 0x09
+
+/// ISCSI SCSI Data peripheral type: Unknown or no device.
+#define ISCSI_SCSI_DATA_PERIPHERAL_TYPE_UNKNOWN 0x1F
+
+/// ISCSI SCSI Data peripheral type: First bit of the five bits.
+#define ISCSI_SCSI_DATA_PERIPHERAL_TYPE_FIRST_BIT 0
+
+/// ISCSI SCSI Data peripheral type: Second bit of the five bits.
+#define ISCSI_SCSI_DATA_PERIPHERAL_TYPE_SECOND_BIT ((ISCSI_SCSI_DATA_PERIPHERAL_TYPE_FIRST_BIT) + 1)
+
+/// ISCSI SCSI Data peripheral type: Third bit of the five bits.
+#define ISCSI_SCSI_DATA_PERIPHERAL_TYPE_THIRD_BIT ((ISCSI_SCSI_DATA_PERIPHERAL_TYPE_SECOND_BIT) + 1)
+
+/// ISCSI SCSI Data peripheral type: Fourth bit of the five bits.
+#define ISCSI_SCSI_DATA_PERIPHERAL_TYPE_FOURTH_BIT ((ISCSI_SCSI_DATA_PERIPHERAL_TYPE_THIRD_BIT) + 1)
+
+/// ISCSI SCSI Data peripheral type: Fourth bit of the five bits.
+#define ISCSI_SCSI_DATA_PERIPHERAL_TYPE_FIFTH_BIT ((ISCSI_SCSI_DATA_PERIPHERAL_TYPE_FOURTH_BIT) + 1)
+
+/// ISCSI SCSI Data peripheral type: Bit mask.
+#define ISCSI_SCSI_DATA_PERIPHERAL_TYPE_MASK ((1 << (ISCSI_SCSI_DATA_PERIPHERAL_TYPE_FIRST_BIT)) | (1 << (ISCSI_SCSI_DATA_PERIPHERAL_TYPE_SECOND_BIT)) | (1 << (ISCSI_SCSI_DATA_PERIPHERAL_TYPE_THIRD_BIT)) | (1 << (ISCSI_SCSI_DATA_PERIPHERAL_TYPE_FOURTH_BIT)) | (1 << (ISCSI_SCSI_DATA_PERIPHERAL_TYPE_FIFTH_BIT)))
+
+/// ISCSI SCSI Data peripheral type: Extracts the Peripheral device type bits.
+#define ISCSI_SCSI_DATA_GET_PERIPHERAL_TYPE(x) (((x) & ISCSI_SCSI_DATA_PERIPHERAL_TYPE_MASK) >> ISCSI_SCSI_DATA_PERIPHERAL_TYPE_FIRST_BIT)
+
+/// ISCSI SCSI Data peripheral identifier: The specified peripheral device type is currently connected to this logical unit, or connection state could not be determined.
+#define ISCSI_SCSI_DATA_PERIPHERAL_ID_POSSIBLE 0x0
+
+/// ISCSI SCSI Data peripheral identifier: The target is capable of supporting the specified peripheral device type on this logical unit, but not connected.
+#define ISCSI_SCSI_DATA_PERIPHERAL_ID_SUPPORTED 0x1
+
+/// ISCSI SCSI Data peripheral identifier: The target is not capable of supporting a physical device on this logical unit.
+#define ISCSI_SCSI_DATA_PERIPHERAL_ID_NEVER 0x3
+
+/// ISCSI SCSI Data peripheral identifier: Vendor specific.
+#define ISCSI_SCSI_DATA_PERIPHERAL_ID_VENDOR_UNIQ 0x4
+
+/// ISCSI SCSI Data peripheral identifier: First bit of the three bits.
+#define ISCSI_SCSI_DATA_PERIPHERAL_ID_FIRST_BIT 5
+
+/// ISCSI SCSI Data peripheral identifier: Second bit of the fhree bits.
+#define ISCSI_SCSI_DATA_PERIPHERAL_ID_SECOND_BIT ((ISCSI_SCSI_DATA_PERIPHERAL_ID_FIRST_BIT) + 1)
+
+/// ISCSI SCSI Data peripheral identifier: Third bit of the three bits.
+#define ISCSI_SCSI_DATA_PERIPHERAL_ID_THIRD_BIT ((ISCSI_SCSI_DATA_PERIPHERAL_ID_SECOND_BIT) + 1)
+
+/// ISCSI SCSI Data peripheral identifier: Bit mask.
+#define ISCSI_SCSI_DATA_PERIPHERAL_ID_MASK ((1 << (ISCSI_SCSI_DATA_PERIPHERAL_ID_FIRST_BIT)) | (1 << (ISCSI_SCSI_DATA_PERIPHERAL_ID_SECOND_BIT)) | (1 << (ISCSI_SCSI_DATA_PERIPHERAL_ID_THIRD_BIT)))
+
+/// ISCSI SCSI Data peripheral identifier: Extracts the Peripheral device identifier bits.
+#define ISCSI_SCSI_DATA_GET_PERIPHERAL_ID(x) (((x) & ISCSI_SCSI_DATA_PERIPHERAL_ID_MASK) >> ISCSI_SCSI_DATA_PERIPHERAL_ID_FIRST_BIT)
+
+
+/// ISCSI SCSI Data peripheral type modifier: Bit mask.
+#define ISCSI_SCSI_DATA_PERIPHERAL_TYPE_MOD_MASK 0x7F
+
+/// ISCSI SCSI Data peripheral type modifier: Removable media.
+#define ISCSI_SCSI_DATA_PERIPHERAL_TYPE_MOD_FLAGS_REMOVABLE_MEDIA (1 << 7L)
+
+
+/// ISCSI SCSI Data ANSI version: First bit of the three bits.
+#define ISCSI_SCSI_DATA_VERSION_ANSI_FIRST_BIT 0
+
+/// ISCSI SCSI Data ANSI version: Second bit of the fhree bits.
+#define ISCSI_SCSI_DATA_VERSION_ANSI_SECOND_BIT ((ISCSI_SCSI_DATA_VERSION_ANSI_FIRST_BIT) + 1)
+
+/// ISCSI SCSI Data ANSI version: Third bit of the three bits.
+#define ISCSI_SCSI_DATA_VERSION_ANSI_THIRD_BIT ((ISCSI_SCSI_DATA_VERSION_ANSI_SECOND_BIT) + 1)
+
+/// ISCSI SCSI Data ANSI version: Bit mask.
+#define ISCSI_SCSI_DATA_VERSION_ANSI_MASK ((1 << (ISCSI_SCSI_DATA_VERSION_ANSI_FIRST_BIT)) | (1 << (ISCSI_SCSI_DATA_VERSION_ANSI_SECOND_BIT)) | (1 << (ISCSI_SCSI_DATA_VERSION_ANSI_THIRD_BIT)))
+
+/// ISCSI SCSI Data ANSI version: Extracts the Peripheral device identifier bits.
+#define ISCSI_SCSI_DATA_GET_VERSION_ANSI(x) (((x) & ISCSI_SCSI_DATA_VERSION_ANSI_MASK) >> ISCSI_SCSI_DATA_VERSION_ANSI_FIRST_BIT)
+
+/// ISCSI SCSI Data ECMA version: First bit of the three bits.
+#define ISCSI_SCSI_DATA_VERSION_ECMA_FIRST_BIT 3
+
+/// ISCSI SCSI Data ECMA version: Second bit of the fhree bits.
+#define ISCSI_SCSI_DATA_VERSION_ECMA_SECOND_BIT ((ISCSI_SCSI_DATA_VERSION_ECMA_FIRST_BIT) + 1)
+
+/// ISCSI SCSI Data ECMA version: Third bit of the three bits.
+#define ISCSI_SCSI_DATA_VERSION_ECMA_THIRD_BIT ((ISCSI_SCSI_DATA_VERSION_ECMA_SECOND_BIT) + 1)
+
+/// ISCSI SCSI Data ECMA version: Bit mask.
+#define ISCSI_SCSI_DATA_VERSION_ECMA_MASK ((1 << (ISCSI_SCSI_DATA_VERSION_ECMA_FIRST_BIT)) | (1 << (ISCSI_SCSI_DATA_VERSION_ECMA_SECOND_BIT)) | (1 << (ISCSI_SCSI_DATA_VERSION_ECMA_THIRD_BIT)))
+
+/// ISCSI SCSI Data ECMA version: Extracts the Peripheral device identifier bits.
+#define ISCSI_SCSI_DATA_GET_VERSION_ECMA(x) (((x) & ISCSI_SCSI_DATA_VERSION_ECMA_MASK) >> ISCSI_SCSI_DATA_VERSION_ECMA_FIRST_BIT)
+
+/// ISCSI SCSI Data ISO version: First bit of the two bits.
+#define ISCSI_SCSI_DATA_VERSION_ISO_FIRST_BIT 6
+
+/// ISCSI SCSI Data ISO version: Second bit of the two bits.
+#define ISCSI_SCSI_DATA_VERSION_ISO_SECOND_BIT ((ISCSI_SCSI_DATA_VERSION_ISO_FIRST_BIT) + 1)
+
+/// ISCSI SCSI Data ISO version: Bit mask.
+#define ISCSI_SCSI_DATA_VERSION_ISO_MASK ((1 << (ISCSI_SCSI_DATA_VERSION_ISO_FIRST_BIT)) | (1 << (ISCSI_SCSI_DATA_VERSION_ISO_SECOND_BIT)))
+
+/// ISCSI SCSI Data ISO version: Extracts the Peripheral device identifier bits.
+#define ISCSI_SCSI_DATA_GET_VERSION_ISO(x) (((x) & ISCSI_SCSI_DATA_VERSION_ISO_MASK) >> ISCSI_SCSI_DATA_VERSION_ISO_FIRST_BIT)
+
+
+/// ISCSI SCSI Data response data format flags: This structure complies with SCSI-1 specifications.
+#define ISCSI_SCSI_DATA_RESPONSE_DATA_FMT_FLAGS_LEVEL_0 0x00
+
+/// ISCSI SCSI Data response data format flags: This structure complies with CCS pseudo specifications.
+#define ISCSI_SCSI_DATA_RESPONSE_DATA_FMT_FLAGS_CCS 0x01
+
+/// ISCSI SCSI Data response data format flags: This structure complies with SCSI-2/3 specifications.
+#define ISCSI_SCSI_DATA_RESPONSE_DATA_FMT_FLAGS_SCSI_2 0x02
+
+/// ISCSI SCSI Data response data format flags: First bit of the four bits.
+#define ISCSI_SCSI_DATA_RESPONSE_DATA_FMT_FLAGS_FIRST_BIT 0
+
+/// ISCSI SCSI Data response data format flags: Second bit of the four bits.
+#define ISCSI_SCSI_DATA_RESPONSE_DATA_FMT_FLAGS_SECOND_BIT ((ISCSI_SCSI_DATA_RESPONSE_DATA_FMT_FLAGS_FIRST_BIT) + 1)
+
+/// ISCSI SCSI Data response data format flags: Third bit of the four bits.
+#define ISCSI_SCSI_DATA_RESPONSE_DATA_FMT_FLAGS_THIRD_BIT ((ISCSI_SCSI_DATA_RESPONSE_DATA_FMT_FLAGS_SECOND_BIT) + 1)
+
+/// ISCSI SCSI Data response data format flags: Fourth bit of the four bits.
+#define ISCSI_SCSI_DATA_RESPONSE_DATA_FMT_FLAGS_FOURTH_BIT ((ISCSI_SCSI_DATA_RESPONSE_DATA_FMT_FLAGS_THIRD_BIT) + 1)
+
+/// ISCSI SCSI Data response data format flags: Bit mask.
+#define ISCSI_SCSI_DATA_RESPONSE_DATA_FMT_FLAGS_MASK ((1 << (ISCSI_SCSI_DATA_RESPONSE_DATA_FMT_FLAGS_FIRST_BIT)) | (1 << (ISCSI_SCSI_DATA_RESPONSE_DATA_FMT_FLAGS_SECOND_BIT)) | (1 << (ISCSI_SCSI_DATA_RESPONSE_DATA_FMT_FLAGS_THIRD_BIT)) | (1 << (ISCSI_SCSI_DATA_RESPONSE_DATA_FMT_FLAGS_FOURTH_BIT)))
+
+/// ISCSI SCSI Data response data format flags: Extracts the Peripheral device type bits.
+#define ISCSI_SCSI_DATA_GET_RESPONSE_DATA_FMT_FLAGS(x) (((x) & ISCSI_SCSI_DATA_RESPONSE_DATA_FMT_FLAGS_MASK) >> ISCSI_SCSI_DATA_RESPONSE_DATA_FMT_FLAGS_FIRST_BIT)
+
+/// ISCSI SCSI Data response data format flags: TERMINATE I/O PROCESS message device support.
+#define ISCSI_SCSI_DATA_RESPONSE_DATA_FMT_FLAGS_TERMINATE_IO_PROC_MSG (1 << 6L)
+
+/// ISCSI SCSI Data response data format flags: Asynchronous Event Notification device support.
+#define ISCSI_SCSI_DATA_RESPONSE_DATA_FMT_FLAGS_ASYNC_EVENT_NOTIFY (1 << 7L)
+
+
+typedef struct __attribute__((packed)) iscsi_scsi_data_packet {
+ /// Peripheral device type and qualifier.
+ uint8_t peripheral_type_id;
+
+ /// Peripheral device type modifier and removable media flag.
+ int8_t peripheral_type_mod_flags;
+
+ /// ANSI-Approved, ECMA and ISO version.
+ uint8_t version;
+
+ /// Response data format, AENC and TrmIOP flags.
+ int8_t response_data_fmt_flags;
+
+ /// Additional length in bytes.
+ uint8_t add_len;
+} iscsi_scsi_data_packet;
+
+
+/// ISCSI SCSI Standard Inquiry Data flags: Device responds with soft reset instead of hard reset to reset condition.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_FLAGS_SOFT_RESET (1 << 0L)
+
+/// ISCSI SCSI Standard Inquiry Data flags: Device supports tagged command queueing.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_FLAGS_COMMAND_QUEUE (1 << 1L)
+
+/// ISCSI SCSI Standard Inquiry Data flags: Reserved for future usage.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_FLAGS_RESERVED (1 << 2L)
+
+/// ISCSI SCSI Standard Inquiry Data flags: Device supports linked commands for this logical unit.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_FLAGS_LINKED_CMDS (1 << 3L)
+
+/// ISCSI SCSI Standard Inquiry Data flags: Device supports synchronous data transfers.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_FLAGS_SYNC (1 << 4L)
+
+/// ISCSI SCSI Standard Inquiry Data flags: Device supports 16-bit wide data transfers.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_FLAGS_WIDE_16_BIT (1 << 5L)
+
+/// ISCSI SCSI Standard Inquiry Data flags: Device supports 32-bit wide data transfers.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_FLAGS_WIDE_32_BIT (1 << 6L)
+
+/// ISCSI SCSI Standard Inquiry Data flags: Device supports relative addressing mode of this logical unit.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_FLAGS_REL_ADDR (1 << 7L)
+
+
+typedef struct __attribute__((packed)) iscsi_scsi_std_inquiry_data_packet {
+ /// iSCSI SCSI data packet.
+ iscsi_scsi_data_packet scsi_data;
+
+ /// Peripheral device type and qualifier.
+ uint8_t peripheral_type_id;
+
+ /// Peripheral device type modifier and removable media flag.
+ int8_t peripheral_type_mod_flags;
+
+ /// ANSI-Approved, ECMA and ISO version.
+ uint8_t version;
+
+ /// Response data format, AENC and TrmIOP flags.
+ int8_t response_data_fmt_flags;
+
+ /// Additional length in bytes.
+ uint8_t add_len;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint16_t reserved;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Vendor identification.
+ uint8_t vendor_id[8];
+
+ /// Product identification.
+ uint8_t product_id[16];
+
+ /// Product revision level.
+ uint8_t product_rev_level[4];
+} iscsi_scsi_std_inquiry_data_packet;
+
+/**
+ * @brief iSCSI SCSI Sense Event data packet.
+ *
+ * For a SCSI event, this data accompanies the report in the data
+ * segment and identifies the condition.
+ *
+ * For an iSCSI event, additional vendor-unique data MAY accompany the
+ * Async event. Initiators MAY ignore the data when not understood,
+ * while processing the rest of the PDU.
+ *
+ * If the DataSegmentLength is not 0, the format of the DataSegment is
+ * as follows:
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_sense_event_data_packet {
+ /**
+ * @brief SenseLength.
+ *
+ * This is the length of Sense Data. When the Sense Data field is empty
+ * (e.g., the event is not a SCSI event), SenseLength is 0.
+ */
+ uint16_t sense_len;
+
+ /// Sense Data.
+ uint16_t sense_data[0];
+
+ /// iSCSI Event Data.
+ uint16_t event_data[0];
+} iscsi_scsi_sense_event_data_packet;
+
+
+/// iSCSI SCSI sense data response code: Current format.
+#define ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_CURRENT_FMT 0x70
+
+/// iSCSI SCSI sense data response code: Current format.
+#define ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_DEFERRED_FMT 0x71
+
+/// iSCSI SCSI sense data response code: Valid.
+#define ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_VALID (1 << 7L)
+
+
+/// iSCSI SCSI sense data sense key: First bit of the four bits.
+#define ISCSI_SCSI_SENSE_DATA_SENSE_KEY_FIRST_BIT 0
+
+/// iSCSI SCSI sense data sense key: Second bit of the four bits.
+#define ISCSI_SCSI_SENSE_DATA_SENSE_KEY_SECOND_BIT ((ISCSI_SCSI_SENSE_DATA_SENSE_KEY_FIRST_BIT) + 1)
+
+/// iSCSI SCSI sense data sense key: Third bit of the four bits.
+#define ISCSI_SCSI_SENSE_DATA_SENSE_KEY_THIRD_BIT ((ISCSI_SCSI_SENSE_DATA_SENSE_KEY_SECOND_BIT) + 1)
+
+/// iSCSI SCSI sense data sense key: Fourth bit of the four bits.
+#define ISCSI_SCSI_SENSE_DATA_SENSE_KEY_FOURTH_BIT ((ISCSI_SCSI_SENSE_DATA_SENSE_KEY_THIRD_BIT) + 1)
+
+/// iSCSI SCSI sense data sense key: Bit mask.
+#define ISCSI_SCSI_SENSE_DATA_SENSE_KEY_MASK ((1 << (ISCSI_SCSI_SENSE_DATA_SENSE_KEY_FIRST_BIT)) | (1 << (ISCSI_SCSI_SENSE_DATA_SENSE_KEY_SECOND_BIT)) | (1 << (ISCSI_SCSI_SENSE_DATA_SENSE_KEY_THIRD_BIT)) | (1 << (ISCSI_SCSI_SENSE_DATA_SENSE_KEY_FOURTH_BIT)))
+
+/// iSCSI SCSI sense data sense key: Extracts the Sense Key (SK) bits.
+#define ISCSI_SCSI_SENSE_DATA_GET_SENSE_KEY(x) (((x) & ISCSI_SCSI_SENSE_DATA_SENSE_KEY_MASK) >> ISCSI_SCSI_SENSE_DATA_SENSE_KEY_FIRST_BIT)
+
+// iSCSI SCSI sense data sense key flags: ILI.
+#define ISCSI_SCSI_SENSE_DATA_SENSE_KEY_FLAGS_ILI (1 << 5L)
+
+// iSCSI SCSI sense data sense key flags: EOM.
+#define ISCSI_SCSI_SENSE_DATA_SENSE_KEY_FLAGS_EOM (1 << 6L)
+
+// iSCSI SCSI sense data sense key flags: FILEMARK.
+#define ISCSI_SCSI_SENSE_DATA_SENSE_KEY_FLAGS_FILEMARK (1 << 7L)
+
+
+/**
+ * @brief iSCSI SCSI basic sense data packet data.
+ *
+ * This is the basic SCSI sense data shared by
+ * all SCSI sense data.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_sense_data_packet {
+ /// Response code.
+ int8_t response_code;
+
+ /// Reserved for future usage (always MUST be 0).
+ uint8_t reserved;
+
+ /// Sense key and flags.
+ int8_t sense_key_flags;
+
+ /// Information.
+ uint32_t info;
+
+ /// Additional sense length in bytes.
+ uint8_t add_len;
+} iscsi_scsi_sense_data_packet;
+
+/// iSCSI SCSI maximum sense data length.
+#define ISCSI_SCSI_MAX_SENSE_DATA_LEN (sizeof(struct iscsi_scsi_sense_data_packet) + 255UL)
+
+
+// iSCSI SCSI sense data check condition sense key specific bit mask.
+#define ISCSI_SCSI_SENSE_DATA_CHECK_COND_SENSE_KEY_SPEC_MASK 0x3F
+
+// iSCSI SCSI sense data check condition sense key specific flags: SKSV.
+#define ISCSI_SCSI_SENSE_DATA_CHECK_COND_SENSE_KEY_SPEC_FLAGS_SKSV (1 << 7L)
+
+
+/**
+ * @brief iSCSI SCSI sense data check condition packet data.
+ *
+ * This is the additional SCSI sense data used by
+ * the check condition status code.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_sense_data_check_cond_packet {
+ /// Basic SCSI sense data packet.
+ iscsi_scsi_sense_data_packet sense_data;
+
+ /// Information.
+ uint32_t cmd_spec_info;
+
+ /// Additional Sense Code (ASC).
+ uint8_t asc;
+
+ /// Additional Sense Code Qualifier (ASCQ).
+ uint8_t ascq;
+
+ /// Field replaceable unit code.
+ uint32_t field_rep_unit_code;
+
+ /// Sense key specific.
+ uint8_t sense_key_spec_flags;
+
+ /// Sense key specific.
+ uint16_t sense_key_spec;
+} iscsi_scsi_sense_data_check_cond_packet;
+
/// SCSI command opcode (embedded in iSCSI protocol): TEST UNIT READY.
#define ISCSI_SCSI_OPCODE_TESTUNITREADY 0x00
@@ -802,6 +1204,10 @@ typedef struct __attribute__((packed)) iscsi_ds_cmd_data {
*/
#define ISCSI_SCSI_CMD_FLAGS_TASK_NO_UNSOLICITED_DATA (1 << 7)
+
+/// SCSI SCSI command flags: Final.
+#define ISCSI_SCSI_CMD_FLAGS_FINAL (1 << 7)
+
/**
* @brief iSCSI SCSI command flags: Expected input data.
*
@@ -925,7 +1331,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cmd_packet {
* CDBs. Whenever the CDB is larger than 16 bytes, an Extended CDB AHS
* MUST be used to contain the CDB spillover.
*/
- iscsi_cdb scsi_cdb;
+ iscsi_scsi_cdb scsi_cdb;
/// Optional AHS packet data.
iscsi_ahs_packet ahs;
@@ -934,7 +1340,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cmd_packet {
iscsi_header_digest hdr_digest;
/// Optional data segment, command data.
- iscsi_ds_cmd_data ds_cmd_data;
+ iscsi_scsi_ds_cmd_data ds_cmd_data;
/// Optional data digest.
iscsi_data_digest data_digest;
@@ -1260,7 +1666,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_response_packet {
iscsi_header_digest hdr_digest;
/// Optional data segment, command data.
- iscsi_ds_cmd_data ds_cmd_data;
+ iscsi_scsi_ds_cmd_data ds_cmd_data;
/// Optional data digest.
iscsi_data_digest data_digest;
@@ -1621,7 +2027,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_data_out_req_packet {
iscsi_header_digest hdr_digest;
/// Data segment.
- iscsi_ds_cmd_data ds_cmd_data;
+ iscsi_scsi_ds_cmd_data ds_cmd_data;
/// Optional data digest.
iscsi_data_digest data_digest;
@@ -1833,7 +2239,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_data_in_response_packet {
iscsi_header_digest hdr_digest;
/// Data segment.
- iscsi_ds_cmd_data ds_cmd_data;
+ iscsi_scsi_ds_cmd_data ds_cmd_data;
/// Optional data digest.
iscsi_data_digest data_digest;
@@ -2148,7 +2554,7 @@ typedef struct __attribute__((packed)) iscsi_async_msg_packet {
iscsi_header_digest hdr_digest;
/// Data segment.
- iscsi_ds_cmd_data ds_cmd_data;
+ iscsi_scsi_ds_cmd_data ds_cmd_data;
/// Optional data digest.
iscsi_data_digest data_digest;
@@ -2156,36 +2562,6 @@ typedef struct __attribute__((packed)) iscsi_async_msg_packet {
/**
- * @brief iSCSI Sense Event data packet.
- *
- * For a SCSI event, this data accompanies the report in the data
- * segment and identifies the condition.
- *
- * For an iSCSI event, additional vendor-unique data MAY accompany the
- * Async event. Initiators MAY ignore the data when not understood,
- * while processing the rest of the PDU.
- *
- * If the DataSegmentLength is not 0, the format of the DataSegment is
- * as follows:
- */
-typedef struct __attribute__((packed)) iscsi_sense_event_data_packet {
- /**
- * @brief SenseLength.
- *
- * This is the length of Sense Data. When the Sense Data field is empty
- * (e.g., the event is not a SCSI event), SenseLength is 0.
- */
- uint16_t sense_len;
-
- /// Sense Data.
- uint16_t sense_data[0];
-
- /// iSCSI Event Data.
- uint16_t event_data[0];
-} iscsi_sense_event_data_packet;
-
-
-/**
* @brief Text Request flags: Continue.
*
* (C) When set to 1, this bit indicates that the text (set of key=value
@@ -2316,7 +2692,7 @@ typedef struct __attribute__((packed)) iscsi_text_req_packet {
* Text operations are usually meant for parameter setting/negotiations
* but can also be used to perform some long-lasting operations.
*/
- iscsi_ds_cmd_data ds_cmd_data;
+ iscsi_scsi_ds_cmd_data ds_cmd_data;
/// Optional data digest.
iscsi_data_digest data_digest;
@@ -2438,7 +2814,7 @@ typedef struct __attribute__((packed)) iscsi_text_response_packet {
* key=value pairs of its own as part of a sequence and not only in
* response to the initiator.
*/
- iscsi_ds_cmd_data ds_cmd_data;
+ iscsi_scsi_ds_cmd_data ds_cmd_data;
/// Optional data digest.
iscsi_data_digest data_digest;
@@ -4461,7 +4837,7 @@ typedef struct __attribute__((packed)) iscsi_login_req_packet {
* 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_ds_cmd_data ds_cmd_data;
+ iscsi_scsi_ds_cmd_data ds_cmd_data;
} iscsi_login_req_packet;
@@ -4803,7 +5179,7 @@ typedef struct __attribute__((packed)) iscsi_login_response_packet {
* All the rules specified for Text Responses also hold for Login
* Responses.
*/
- iscsi_ds_cmd_data ds_cmd_data;
+ iscsi_scsi_ds_cmd_data ds_cmd_data;
} iscsi_login_response_packet;
@@ -5529,7 +5905,7 @@ typedef struct __attribute__((packed)) iscsi_nop_out_packet {
* ping data is indicated by the DataSegmentLength. 0 is a valid value
* for the DataSegmentLength and indicates the absence of ping data.
*/
- iscsi_ds_cmd_data ds_ping_data;
+ iscsi_scsi_ds_cmd_data ds_ping_data;
/// Optional data digest.
iscsi_data_digest data_digest;
@@ -5618,7 +5994,7 @@ typedef struct __attribute__((packed)) iscsi_nop_in_packet {
iscsi_header_digest hdr_digest;
/// DataSegment - Return Ping Data.
- iscsi_ds_cmd_data ds_ping_data;
+ iscsi_scsi_ds_cmd_data ds_ping_data;
/// Optional data digest.
iscsi_data_digest data_digest;
@@ -5968,6 +6344,186 @@ iscsi_portal *iscsi_portal_create(const uint8_t *host, const uint8_t *port); //
void iscsi_portal_destroy(iscsi_portal *portal);
+/// iSCSI SCSI task flags: Read.
+#define ISCSI_SCSI_TASK_FLAGS_XFER_READ (1 << 0L)
+
+/// iSCSI SCSI task flags: Write.
+#define ISCSI_SCSI_TASK_FLAGS_XFER_WRITE (1 << 1L)
+
+
+/// iSCSI SCSI status code: Good.
+#define ISCSI_SCSI_STATUS_GOOD 0x00
+
+/// iSCSI SCSI status code: Check condition.
+#define ISCSI_SCSI_STATUS_CHECK_COND 0x02
+
+/// iSCSI SCSI status code: Condition met.
+#define ISCSI_SCSI_STATUS_COND_MET 0x04
+
+/// iSCSI SCSI status code: Busy.
+#define ISCSI_SCSI_STATUS_BUSY 0x08
+
+/// iSCSI SCSI status code: Intermediate.
+#define ISCSI_SCSI_STATUS_INTERMEDIATE 0x10
+
+/// iSCSI SCSI status code: Intermediate condition met.
+#define ISCSI_SCSI_STATUS_INTERMEDIATE_COND_MET 0x14
+
+/// iSCSI SCSI status code: Reservation conflict.
+#define ISCSI_SCSI_STATUS_RESERVATION_CONFLICT 0x18
+
+/// iSCSI SCSI status code: Obselete.
+#define ISCSI_SCSI_STATUS_OBSELETE 0x22
+
+/// iSCSI SCSI status code: Task set full.
+#define ISCSI_SCSI_STATUS_TASK_SET_FULL 0x28
+
+/// iSCSI SCSI status code: ACA active.
+#define ISCSI_SCSI_STATUS_ACA_ACTIVE 0x30
+
+/// iSCSI SCSI status code: Task aborted.
+#define ISCSI_SCSI_STATUS_TASK_ABORTED 0x40
+
+
+/// iSCSI SCSI sense key: No sense.
+#define ISCSI_SCSI_SENSE_KEY_NO_SENSE 0x00
+
+/// iSCSI SCSI sense key: Recovered error.
+#define ISCSI_SCSI_SENSE_KEY_RECOVERED_ERR 0x01
+
+/// iSCSI SCSI sense key: Not ready.
+#define ISCSI_SCSI_SENSE_KEY_NOT_READY 0x02
+
+/// iSCSI SCSI sense key: Medium error.
+#define ISCSI_SCSI_SENSE_KEY_MEDIUM_ERR 0x03
+
+/// iSCSI SCSI sense key: Hardware error.
+#define ISCSI_SCSI_SENSE_KEY_HARDWARE_ERR 0x04
+
+/// iSCSI SCSI sense key: Illegal request.
+#define ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ 0x05
+
+/// iSCSI SCSI sense key: Unit attention.
+#define ISCSI_SCSI_SENSE_KEY_UNIT_ATTENTION 0x06
+
+/// iSCSI SCSI sense key: Data protect.
+#define ISCSI_SCSI_SENSE_KEY_DATA_PROTECT 0x07
+
+/// iSCSI SCSI sense key: Blank check.
+#define ISCSI_SCSI_SENSE_KEY_BLANK_CHECK 0x08
+
+/// iSCSI SCSI sense key: Vendor specific.
+#define ISCSI_SCSI_SENSE_KEY_VENDOR_SPECIFIC 0x09
+
+/// iSCSI SCSI sense key: Copy aborted.
+#define ISCSI_SCSI_SENSE_KEY_COPY_ABORTED 0x0A
+
+/// iSCSI SCSI sense key: Aborted command.
+#define ISCSI_SCSI_SENSE_KEY_ABORTED_COMMAND 0x0B
+
+/// iSCSI SCSI sense key: Volume overflow.
+#define ISCSI_SCSI_SENSE_KEY_VOLUME_OVERFLOW 0x0D
+
+/// iSCSI SCSI sense key: Miscompare.
+#define ISCSI_SCSI_SENSE_KEY_MISCOMPARE 0x0E
+
+
+/// iSCSI SCSI Additional Sense Code (ASC): No additional sense.
+#define ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE 0x00
+
+/// iSCSI SCSI Additional Sense Code (ASC): Peripheral device write fault.
+#define ISCSI_SCSI_ASC_PERIPHERAL_DEVICE_WRITE_FAULT 0x03
+
+/// iSCSI SCSI Additional Sense Code (ASC): Logical unit not ready.
+#define ISCSI_SCSI_ASC_LOGICAL_UNIT_NOT_READY 0x04
+
+/// iSCSI SCSI Additional Sense Code (ASC): Warning.
+#define ISCSI_SCSI_ASC_WARNING 0x0B
+
+/// iSCSI SCSI Additional Sense Code (ASC): Block guard check failed.
+#define ISCSI_SCSI_ASC_LOGICAL_BLOCK_GUARD_CHECK_FAIL 0x10
+
+/// iSCSI SCSI Additional Sense Code (ASC): Block application tag checdk failed.
+#define ISCSI_SCSI_ASC_LOGICAL_BLOCK_APP_TAG_CHECK_FAIL 0x10
+
+/// iSCSI SCSI Additional Sense Code (ASC): Block reference tag check failed.
+#define ISCSI_SCSI_ASC_LOGICAL_BLOCK_REF_TAG_CHECK_FAIL 0x10
+
+/// iSCSI SCSI Additional Sense Code (ASC): Unrecovered read error.
+#define ISCSI_SCSI_ASC_UNRECOVERED_READ_ERR 0x11
+
+/// iSCSI SCSI Additional Sense Code (ASC): Miscompare during verify operation.
+#define ISCSI_SCSI_ASC_MISCOMPARE_DURING_VERIFY_OPERATION 0x1D
+
+/// iSCSI SCSI Additional Sense Code (ASC): Invalid command operation code.
+#define ISCSI_SCSI_ASC_INVALID_COMMAND_OPERATION_CODE 0x20
+
+/// iSCSI SCSI Additional Sense Code (ASC): Access denied.
+#define ISCSI_SCSI_ASC_ACCESS_DENIED 0x20
+
+/// iSCSI SCSI Additional Sense Code (ASC): Logical block address out of range.
+#define ISCSI_SCSI_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x21
+
+/// iSCSI SCSI Additional Sense Code (ASC): Invalid field in CDB.
+#define ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB 0x24
+
+/// iSCSI SCSI Additional Sense Code (ASC): Logical unit not supported.
+#define ISCSI_SCSI_ASC_LU_NOT_SUPPORTED 0x25
+
+/// iSCSI SCSI Additional Sense Code (ASC): Write protected.
+#define ISCSI_SCSI_ASC_WRITE_PROTECTED 0x27
+
+/// iSCSI SCSI Additional Sense Code (ASC): Data has changed.
+#define ISCSI_SCSI_ASC_CAPACITY_DATA_HAS_CHANGED 0x2A
+
+/// iSCSI SCSI Additional Sense Code (ASC): Format command failed.
+#define ISCSI_SCSI_ASC_FORMAT_COMMAND_FAIL 0x31
+
+/// iSCSI SCSI Additional Sense Code (ASC): Saving parameters not supported.
+#define ISCSI_SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED 0x39
+
+/// iSCSI SCSI Additional Sense Code (ASC): Internal target failure.
+#define ISCSI_SCSI_ASC_INTERNAL_TARGET_FAIL 0x44
+
+
+/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Cause not reportable.
+#define ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE 0x00
+
+/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Becoming ready.
+#define ISCSI_SCSI_ASCQ_BECOMING_READY 0x01
+
+/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Format command failed.
+#define ISCSI_SCSI_ASCQ_FORMAT_COMMAND_FAIL 0x01
+
+/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Block guard check failed.
+#define ISCSI_SCSI_ASCQ_LOGICAL_BLOCK_GUARD_CHECK_FAIL 0x01
+
+/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Block application tag check failed.
+#define ISCSI_SCSI_ASCQ_LOGICAL_BLOCK_APP_TAG_CHECK_FAIL 0x02
+
+/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): No access rights.
+#define ISCSI_SCSI_ASCQ_NO_ACCESS_RIGHTS 0x02
+
+/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Block reference tag check failed.
+#define ISCSI_SCSI_ASCQ_LOGICAL_BLOCK_REF_TAG_CHECK_FAIL 0x03
+
+/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Power loss expected.
+#define ISCSI_SCSI_ASCQ_POWER_LOSS_EXPECTED 0x08
+
+/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Invalid logical unit identifier.
+#define ISCSI_SCSI_ASCQ_INVALID_LU_IDENTIFIER 0x09
+
+/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Capacity data has changed.
+#define ISCSI_SCSI_ASCQ_CAPACITY_DATA_HAS_CHANGED 0x09
+
+
+/// Callback function when SCSI transfer is completed.
+typedef void (*iscsi_scsi_task_xfer_complete_callback)(struct iscsi_scsi_task *scsi_task);
+
+/// Callback function for SCSI task destruction.
+typedef void (*iscsi_scsi_task_destroy_callback)(struct iscsi_scsi_task *scsi_task);
+
+
/**
* @brief iSCSI SCSI Task.
*
@@ -5975,24 +6531,69 @@ void iscsi_portal_destroy(iscsi_portal *portal);
* layer task management.
*/
typedef struct iscsi_scsi_task {
+ /// SCSI LUN associated with this task.
+ struct iscsi_lun *lun;
+
/// Target iSCSI port.
struct iscsi_port *target_port;
/// Initiator iSCSI port.
struct iscsi_port *init_port;
+ /// SCSI Command Descriptor Block (CDB).
+ iscsi_scsi_cdb *cdb;
+
+ /// SCSI sense data.
+ iscsi_scsi_sense_data_packet *sense_data;
+
+ /// Transfer complete callback function.
+ iscsi_scsi_task_xfer_complete_callback xfer_complete_callback;
+
+ /// Task destruction callback function.
+ iscsi_scsi_task_destroy_callback destroy_callback;
+
+ /// Output buffer.
+ uint8_t *buf;
+
+ /// Position of buffer in bytes.
+ uint pos;
+
+ /// Length of buffer in bytes.
+ uint len;
+
+ /// Flags.
+ int flags;
+
/// Reference counter.
uint32_t ref;
/// Transfer length in bytes.
uint32_t xfer_len;
+
+ /// Sense data length.
+ uint8_t sense_data_len;
+
+ /// iSCSI SCSI status code.
+ uint8_t status;
+
+ /// Task management function.
+ uint8_t task_mgmt_func;
+
+ /// Task management response code.
+ uint8_t task_mgmt_response;
} iscsi_scsi_task;
/// ISCSI port flags: In use.
#define ISCSI_PORT_FLAGS_IN_USE (1 << 0L)
-void iscsi_scsi_task_put(iscsi_scsi_task *scsi_task); // Enqueues a SCSI task for execution
+void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task, iscsi_scsi_task_xfer_complete_callback xfer_complete_callback, iscsi_scsi_task_destroy_callback destroy_callback); // Allocates and initializes a SCSI task
+void iscsi_scsi_task_destroy(iscsi_scsi_task *scsi_task); // Deallocates all resources acquired iscsi_scsi_task_create
+
+void iscsi_scsi_task_xfer_complete(iscsi_scsi_task *scsi_task); // Callback function when an iSCSI SCSI task completed the data transfer
+
+void iscsi_scsi_task_sense_data_build(iscsi_scsi_task *scsi_task, const uint8_t sense_key, const uint8_t asc, const uint8_t ascq); // Allocates, if necessary and initializes SCSI sense data
+void iscsi_scsi_task_lun_process_none(iscsi_scsi_task *scsi_task); // Processes a iSCSI SCSI task with no LUN identifier
/**
@@ -6055,6 +6656,23 @@ typedef struct iscsi_lun {
/**
+ * @brief iSCSI Basic Header Segment (BHS) search in active and queued Ready To Transfer (R2T) tasks.
+ *
+ * This structure is used by iterating through
+ * all active and enqueued Ready To Transfer
+ * (R2T) tasks for finding an iSCSI PDU
+ * Basic Header Segment (BHS).
+ */
+typedef struct iscsi_r2t_find_bhs {
+ /// Found iSCSI PDU is stored here, should be initialized to NULL.
+ struct iscsi_pdu *pdu;
+
+ /// iSCSI Basic Header Segment (BHS) to be searched for.
+ iscsi_bhs_packet *bhs_pkt;
+} iscsi_r2t_find_bhs;
+
+
+/**
* @brief iSCSI PDU search and removal by Ready To Transfer Sequence Number (R2TSN).
*
* This structure is used by iterating through
@@ -6280,7 +6898,7 @@ typedef struct iscsi_session {
iscsi_hashmap *connections;
/// Initiator port.
- iscsi_port *initiator_port;
+ iscsi_port *init_port;
/// Login key / value pairs negotiated with this session.
iscsi_hashmap *key_value_pairs;
@@ -6471,12 +7089,21 @@ typedef struct iscsi_connection {
/// Hash map containing SNACK PDU's associated with this connection.
iscsi_hashmap *pdu_snack;
- /// Active Ready To Transfer Tasks.
+ /// Active Ready To Transfer (R2T) tasks.
iscsi_hashmap *r2t_tasks_active;
+ /// Queued Ready To Transfer (R2T) tasks.
+ iscsi_hashmap *r2t_tasks_queue;
+
/// iSCSI SendTargets total number of bytes completed.
uint target_send_total_size;
+ /// iSCSI SCSI data read count.
+ uint scsi_data_read_cnt;
+
+ /// iSCSI tasks pending count.
+ uint task_cnt;
+
/// iSCSI connection contains a header digest (CRC32), always MUST be 0 or 4 for now.
int header_digest;
@@ -6564,7 +7191,7 @@ typedef struct iscsi_pdu {
iscsi_header_digest *header_digest;
/// iSCSI DataSegment (DS) packet data for fast access and is straight after BHS, AHS and header digest packet in memory.
- iscsi_ds_cmd_data *ds_cmd_data;
+ iscsi_scsi_ds_cmd_data *ds_cmd_data;
/// Data digest (CRC32C) packet data for fast access and is straight after BHS, AHS, header digest and DataSegment packet in memory.
iscsi_data_digest *data_digest;
@@ -6572,6 +7199,9 @@ typedef struct iscsi_pdu {
/// iSCSI task handling this PDU.
struct iscsi_task *task;
+ /// Associated iSCSI connection.
+ iscsi_connection *conn;
+
/// Transfer complete callback function.
iscsi_connection_xfer_complete_callback xfer_complete_callback;
@@ -6611,8 +7241,8 @@ typedef struct iscsi_pdu {
/// Allocated DataSegment buffer length.
uint len;
- /// Associated iSCSI connection.
- iscsi_connection *conn;
+ /// Tasks referenced by this PDU counter.
+ uint task_ref_cnt;
/// CmdSN.
uint32_t cmd_sn;
@@ -6636,6 +7266,9 @@ typedef struct iscsi_task {
/// Underlying SCSI task structure.
iscsi_scsi_task scsi_task;
+ /// Parent iSCSI task.
+ struct iscsi_task *parent;
+
/// Associated iSCSI connection.
iscsi_connection *conn;
@@ -6645,6 +7278,9 @@ typedef struct iscsi_task {
/// Buffer to send to initiator.
uint8_t *buf;
+ /// Buffer position in bytes.
+ uint pos;
+
/// Buffer length in bytes.
uint len;
@@ -6696,11 +7332,14 @@ typedef struct iscsi_task_find_tag {
} iscsi_task_find_tag;
-void iscsi_task_put(iscsi_task *task); // Enqueues an iSCSI task into it's underlying SCSI management structure
+iscsi_task *iscsi_task_create(iscsi_connection *conn, iscsi_task *parent, iscsi_scsi_task_xfer_complete_callback callback); // Allocates and initializes an iSCSI task structure
+void iscsi_task_destroy(iscsi_task *task); // Deallocates resources acquired by iscsi_task_create
int iscsi_task_find_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Finds an iSCSI task by Target Transfer Tag (TTT)
int iscsi_device_find_lun_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Finds an iSCSI LUN by LUN identifier
+void iscsi_task_response(iscsi_connection *conn, iscsi_task *task); //
+
iscsi_lun *iscsi_lun_create(const uint id); // Allocates and initializes an iSCSI LUN structure for linkage with a DNBD3 image
void iscsi_lun_destroy(iscsi_lun *lun); // Deallocates all resources acquired by iscsi_lun_create
@@ -6748,6 +7387,10 @@ void iscsi_connection_pdu_free(iscsi_connection *conn, iscsi_pdu *pdu); // Frees
void iscsi_connection_pdu_ack_remove(iscsi_connection *conn, const uint32_t exp_stat_sn); // Removes an acknowledged PDU from SNACK PDU hash map by ExpStatSN
+int iscsi_r2t_find_pdu_bhs_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Finds an iSCSI PDU by Basic Header Segment (BHS) in either the Ready To Transfer (R2T) active and queued task hash map
+iscsi_pdu *iscsi_r2t_find_pdu_bhs(iscsi_connection *conn, iscsi_pdu *pdu); // Searches an iSCSI PDU by Basic Header Segment (BHS) in the Ready To Transfer (R2T) active and queued task hash map
+int iscsi_r2t_remove_pdu_from_snack_list_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Finds and removes an iSCSI PDU by Ready To Transfer Sequence Number (R2TSN)
+
int iscsi_connection_read_data(iscsi_connection *conn, int len, void *buf);
int iscsi_connection_read_iov_data(iscsi_connection *conn, struct iovec *iov, int iov_count);
void iscsi_connection_pdu_write(iscsi_connection *conn, iscsi_pdu *pdu, iscsi_connection_xfer_complete_callback callback, uint8_t *user_data);