diff options
| author | Sebastian Vater | 2025-08-29 17:00:46 +0200 |
|---|---|---|
| committer | Sebastian Vater | 2025-08-29 17:00:46 +0200 |
| commit | dffd89d99e66f0d776a8570cd9943654f71844d3 (patch) | |
| tree | 76b4bd73f1aa2784a2953b9af9b77b6e17cf8cfa | |
| parent | Added iSCSI opcode immediate flag to header file to make code more readable a... (diff) | |
| download | dnbd3-dffd89d99e66f0d776a8570cd9943654f71844d3.tar.gz dnbd3-dffd89d99e66f0d776a8570cd9943654f71844d3.tar.xz dnbd3-dffd89d99e66f0d776a8570cd9943654f71844d3.zip | |
Implemented lots of iSCSI SCSI INQUIRY opcode related stuff, also did some code refactoring and finally added most of SCSI command opcode header handling.
| -rw-r--r-- | src/server/iscsi.c | 693 | ||||
| -rw-r--r-- | src/server/iscsi.h | 767 |
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); |
