summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSebastian Vater2025-08-08 17:08:02 +0200
committerSebastian Vater2025-08-08 17:08:02 +0200
commit9a11e4e82451bf997cc8e62b635fef07270269d5 (patch)
tree5b9708a80ea9b044bd38d36a94f0a2aa4b973133 /src
parentImplemented main loop of handling incoming iSCSI header packet data. Also add... (diff)
downloaddnbd3-9a11e4e82451bf997cc8e62b635fef07270269d5.tar.gz
dnbd3-9a11e4e82451bf997cc8e62b635fef07270269d5.tar.xz
dnbd3-9a11e4e82451bf997cc8e62b635fef07270269d5.zip
Implemented more login stuff and basic data payload handling. Also fixed some bugs and simplified key and value retrieval. Finally, basic sending response packets implemented.
Diffstat (limited to 'src')
-rw-r--r--src/server/iscsi.c743
-rw-r--r--src/server/iscsi.h32
2 files changed, 718 insertions, 57 deletions
diff --git a/src/server/iscsi.c b/src/server/iscsi.c
index 4d1ab90..8d7b0e5 100644
--- a/src/server/iscsi.c
+++ b/src/server/iscsi.c
@@ -1858,83 +1858,170 @@ iscsi_key_value_pair_packet *iscsi_create_key_value_pairs_packet(const iscsi_has
}
/**
- * @brief Allocates and adds an integer value to a key / value hash map pair.
+ * @brief Extracts a string from a key and value pair.
+ *
+ * This function calculates the length of the key
+ * for the hash map function and returns the value
+ * as string.
+ *
+ * @param[in] key_value_pairs The hash map containing the key and value pairs to be extracted.
+ * @param[in] key The key to retrieve the string value from.
+ * @param[out] out_value The string value of the key is stored here.
+ * @retval -1 An error occured during value retrieval.
+ * 'out value' is unchanged.
+ * @retval 0 The value of the key has been successfully
+ * stored in the 'out_value'.
+ */
+static int iscsi_get_key_value_pair(const iscsi_hashmap *key_value_pairs, const uint8_t *key, uint8_t **out_value)
+{
+ const uint32_t key_len = strlen( key ) + 1;
+
+ return iscsi_hashmap_get( key_value_pairs, key, key_len, out_value );
+}
+
+/**
+ * @brief Allocates and adds a string value to a key / value hash map pair.
*
* This function allocates memory for a string key
- * and its integer representation as string value.
+ * and its string value.
*
* @param[in] key_value_pairs Pointer to the hash map which should
- * contain the added integer key and value pair. NULL is NOT
+ * contain the added string key and value pair. NULL is NOT
* allowed here, so be careful.
* @param[in] key String containing the key name as string. May
* NOT be NULL, so take caution.
- * @param[in] value Integer containing the value to be stored.
+ * @param[in] value String containing the value to be stored.
* @return 0 on successful operation, or a negative value on
* error (memory exhaustion).
*/
-static int iscsi_add_int_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int value)
+static int iscsi_add_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const uint8_t *value)
{
const uint key_len = strlen( key ) + 1;
uint8_t *hash_key = iscsi_hashmap_key_create( key, key_len );
if ( hash_key == NULL ) {
- logadd( LOG_ERROR, "iscsi_add_int_key_value_pair: Out of memory allocating key." );
+ logadd( LOG_ERROR, "iscsi_add_key_value_pair: Out of memory allocating key." );
return -1L;
}
- uint8_t *hash_val = iscsi_sprintf_alloc( "%d", value );
+ const uint val_len = strlen( value ) + 1;
+ uint8_t *hash_val = (uint8_t *) malloc( val_len );
if ( hash_val == NULL ) {
- logadd( LOG_ERROR, "iscsi_add_int_key_value_pair: Out of memory allocating integer value." );
+ logadd( LOG_ERROR, "iscsi_add_key_value_pair: Out of memory allocating string value." );
return -1L;
}
+ memcpy( hash_val, value, val_len );
+
return iscsi_hashmap_put( key_value_pairs, hash_key, key_len, hash_val );
}
/**
- * @brief Allocates and adds an boolean value to a key / value hash map pair.
+ * @brief Extracts an integer value from a key and value pair.
+ *
+ * This function converts a string representation of a
+ * key and value pair to an integer value.
+ *
+ * @param[in] key_value_pairs The hash map containing the key and value pairs to be extracted.
+ * @param[in] key The key to retrieve the integer value from.
+ * @param[out] out_value The integer value of the key is stored here
+ * or 0 in case of an error during string to integer conversion.
+ * @retval -1 An error occured during value retrieval.
+ * 'out value' is unchanged.
+ * @retval 0 The value of the key has been successfully
+ * stored in the 'out_value'.
+ */
+static int iscsi_get_int_key_value_pair(const iscsi_hashmap *key_value_pairs, const uint8_t *key, int32_t *out_value)
+{
+ uint8_t *str_val;
+ int rc = iscsi_get_key_value_pair( key_value_pairs, key, &str_val );
+
+ if ( rc == 0 )
+ *out_value = atol( str_val );
+
+ return rc;
+}
+
+/**
+ * @brief Allocates and adds an integer value to a key / value hash map pair.
*
* This function allocates memory for a string key
- * and its integer value.\n
- * The string representation for true is: Yes\n
- * The string representation for false is: No
+ * and its integer representation as string value.
*
* @param[in] key_value_pairs Pointer to the hash map which should
- * contain the added boolean key and value pair. NULL is NOT
+ * contain the added integer key and value pair. NULL is NOT
* allowed here, so be careful.
* @param[in] key String containing the key name as string. May
* NOT be NULL, so take caution.
- * @param[in] value Boolean containing the value to be stored
- * as string.
+ * @param[in] value Integer containing the value to be stored.
* @return 0 on successful operation, or a negative value on
* error (memory exhaustion).
*/
-static int iscsi_add_bool_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int value)
+static int iscsi_add_int_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int value)
{
- const uint key_len = strlen( key ) + 1;
- uint8_t *hash_key = iscsi_hashmap_key_create( key, key_len );
+ const uint8_t *hash_val = iscsi_sprintf_alloc( "%ld", value );
- if ( hash_key == NULL ) {
- logadd( LOG_ERROR, "iscsi_add_bool_key_value_pair: Out of memory allocating key" );
+ if ( hash_val == NULL ) {
+ logadd( LOG_ERROR, "iscsi_add_int_key_value_pair: Out of memory allocating integer value." );
return -1L;
}
- uint8_t *bool_val = (value ? "Yes" : "No");
- uint8_t *hash_val = (uint8_t *) malloc( strlen( bool_val ) + 1 );
+ return iscsi_add_key_value_pair( key_value_pairs, key, hash_val );
+}
- if ( hash_val == NULL ) {
- logadd( LOG_ERROR, "iscsi_add_bool_key_value_pair: Out of memory allocating boolean string value" );
+/**
+ * @brief Extracts a boolean value from a key and value pair.
+ *
+ * This function converts a string representation of a
+ * key and value pair to a boolean value.
+ *
+ * @param[in] key_value_pairs The hash map containing the key and value pairs to be extracted.
+ * @param[in] key The key to retrieve the boolean value from.
+ * @param[out] out_value The boolean value of the key is stored here.
+ * 'Yes' represents true and any other string results in false.
+ * @retval -1 An error occured during value retrieval.
+ * 'out value' is unchanged.
+ * @retval 0 The value of the key has been successfully
+ * stored in the 'out_value'.
+ */
+static int iscsi_get_bool_key_value_pair(const iscsi_hashmap *key_value_pairs, const uint8_t *key, int32_t *out_value)
+{
+ uint8_t *value;
+ int rc = iscsi_get_key_value_pair( key_value_pairs, key, &value );
- return -1L;
- }
+ if ( rc == 0 )
+ *out_value = (strcasecmp( value, "Yes" ) == 0) ? true : false;
- strcpy( hash_val, bool_val );
+ return rc;
+}
- return iscsi_hashmap_put( key_value_pairs, hash_key, key_len, hash_val );
+/**
+ * @brief Allocates and adds an boolean value to a key / value hash map pair.
+ *
+ * This function allocates memory for a string key
+ * and its integer value.\n
+ * The string representation for true is: Yes\n
+ * The string representation for false is: No
+ *
+ * @param[in] key_value_pairs Pointer to the hash map which should
+ * contain the added boolean key and value pair. NULL is NOT
+ * allowed here, so be careful.
+ * @param[in] key String containing the key name as string. May
+ * NOT be NULL, so take caution.
+ * @param[in] value Boolean containing the value to be stored
+ * as string.
+ * @return 0 on successful operation, or a negative value on
+ * error (memory exhaustion).
+ */
+static int iscsi_add_bool_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int value)
+{
+ const uint8_t *hash_val = ((value != 0) ? "Yes" : "No");
+
+ return iscsi_add_key_value_pair( key_value_pairs, key, hash_val );
}
/**
@@ -2117,7 +2204,7 @@ iscsi_portal *iscsi_portal_create(const uint8_t *host, const uint8_t *port)
memcpy( portal->port, port, port_len );
- portal->sock = 0L;
+ portal->sock = -1L;
return portal;
}
@@ -2404,6 +2491,7 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock)
}
conn->partial_pairs = NULL;
+ conn->init_name = NULL;
conn->header_digest = 0L;
conn->data_digest = 0L;
conn->pdu_processing = NULL;
@@ -2469,6 +2557,12 @@ 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->init_name != NULL ) {
+ free ( conn->init_name );
+
+ conn->init_name = NULL;
+ }
+
if ( conn->partial_pairs != NULL ) {
free ( conn->partial_pairs );
@@ -2488,14 +2582,25 @@ void iscsi_connection_destroy(iscsi_connection *conn)
}
/**
+ * @brief Schedules an iSCSI connection.
+ *
+ * @param[in] conn Pointer to ISCSI connection to be
+ * scheduled. May NOT be NULL, so be careful.
+ */
+void iscsi_connection_schedule(iscsi_connection *conn)
+{
+ // TODO: Implement function.
+}
+
+/**
* @brief Reads data for the specified iSCSI connection from its TCP socket.
*
* The TCP socket is marked as non-blocking, so this function may not read
* all data requested.
*
- * Returns SPDK_ISCSI_CONNECTION_FATAL if the recv() operation indicates a fatal
- * error with the TCP connection (including if the TCP connection was closed
- * unexpectedly.
+ * Returns ISCSI_CONNECT_PDU_READ_ERR_FATAL if the operation
+ * indicates a fatal error with the TCP connection (including
+ * if the TCP connection was closed unexpectedly).
*
* Otherwise returns the number of bytes successfully read.
*/
@@ -2510,6 +2615,190 @@ int iscsi_connection_read(const iscsi_connection *conn, uint8_t *buf, const uint
}
/**
+ * @brief Writes data for the specified iSCSI connection to its TCP socket.
+ *
+ * The TCP socket is marked as non-blocking, so this function may not read
+ * all data requested.
+ *
+ * Returns ISCSI_CONNECT_PDU_READ_ERR_FATAL if the operation
+ * indicates a fatal error with the TCP connection (including
+ * if the TCP connection was closed unexpectedly).
+ *
+ * Otherwise returns the number of bytes successfully written.
+ */
+int iscsi_connection_write(const iscsi_connection *conn, uint8_t *buf, const uint len)
+{
+ if ( len == 0 )
+ return 0;
+
+ const int rc = send( conn->sock, buf, len, 0L );
+
+ return (rc > 0) ? rc : ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+}
+
+/**
+ * @brief Copies retrieved key and value pairs into SCSI connection and session structures.
+ *
+ * This function converts string representations of
+ * integer and boolean key and value pairs.
+ *
+ * @param[in] conn iSCSI connection which holds the
+ * copies of the key and value pairs.
+ * @retval -1 An error occured during the copy process,
+ * e.g. memory is exhausted.
+ * @retval 0 All key and value pairs were copied successfully.
+ */
+int iscsi_connection_copy_key_value_pairs(iscsi_connection *conn)
+{
+ int32_t int_val;
+
+ int rc = iscsi_get_int_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_RECV_DS_LEN, &int_val);
+
+ if ( rc != 0 )
+ return rc;
+
+ if ( (int_val <= 0L) || (int_val > ISCSI_DEFAULT_MAX_RECV_DS_LEN) )
+ int_val = ISCSI_DEFAULT_MAX_RECV_DS_LEN;
+
+ conn->max_recv_ds_len = int_val;
+
+ uint8_t *value;
+
+ rc = iscsi_get_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_HEADER_DIGEST, &value);
+
+ if ( rc != 0 )
+ return rc;
+
+ conn->header_digest = (strcasecmp( value, "CRC32C" ) == 0) ? ISCSI_DIGEST_SIZE : 0L;
+
+ rc = iscsi_get_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST, &value);
+
+ if ( rc != 0 )
+ return rc;
+
+ conn->data_digest = (strcasecmp( value, "CRC32C" ) == 0) ? ISCSI_DIGEST_SIZE : 0L;
+
+ rc = iscsi_get_int_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS, &int_val);
+
+ if ( rc != 0 )
+ return rc;
+
+ conn->session->max_conns = int_val;
+
+ rc = iscsi_get_int_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_OUTSTANDING_R2T, &int_val);
+
+ if ( rc != 0 )
+ return rc;
+
+ conn->session->max_outstanding_r2t = int_val;
+
+ rc = iscsi_get_int_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_FIRST_BURST_LEN, &int_val);
+
+ if ( rc != 0 )
+ return rc;
+
+ conn->session->first_burst_len = int_val;
+
+ rc = iscsi_get_int_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_BURST_LEN, &int_val);
+
+ if ( rc != 0 )
+ return rc;
+
+ conn->session->max_burst_len = int_val;
+
+ rc = iscsi_get_bool_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_INITIAL_R2T, &int_val);
+
+ if ( rc != 0 )
+ return rc;
+
+ conn->session->init_r2t = int_val;
+
+ rc = iscsi_get_bool_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IMMEDIATE_DATA, &int_val);
+
+ if ( rc != 0 )
+ return rc;
+
+ conn->session->immediate_data = int_val;
+
+ return 0L;
+}
+
+/**
+ * @brief Checks buffer sizes of an iSCSI connection with it's associated session for consistency.
+ *
+ * This function ensures that, for example, first
+ * burst length does not exceed maximum burst
+ * length and that the buffers don't exceed their
+ * minimum and maximum allowed values.
+ *
+ * @param[in] conn Pointer to iSCSI connection which holds the
+ * values to be checked for consistency. May NOT be NULL,
+ * so take caution.
+ * @retval -1 At least one value check failed the consistency check.
+ * @retval 0 All consistency checks have passed successfully.
+ */
+static int iscsi_connection_check_key_value_pairs(iscsi_connection *conn)
+{
+ if ( (conn->session->first_burst_len > conn->session->max_burst_len) || (conn->session->first_burst_len < 512) || (conn->session->max_burst_len < 512) || (conn->session->max_burst_len > ISCSI_SESSION_DEFAULT_MAX_BURST_LEN) || (conn->max_recv_ds_len < 512) || (conn->max_recv_ds_len > ISCSI_SESSION_DEFAULT_MAX_BURST_LEN) )
+ return -1;
+
+ return 0;
+}
+
+/**
+ * @brief Updates iSCSI connection and session values after being retrieved from the client.
+ *
+ * This function copies the key and value pairs into the
+ * internal connection and session structure and checks
+ * them for consistency.\n
+ * The TCP receive buffer will be adjusted to the new
+ * updated value but is never lower than 4KiB and never
+ * higher than 8KiB plus header overhead and a factor of
+ * 4 for receiving four packets at once.
+ *
+ * @param[in] conn Pointer to ISCSI connection which should
+ * be updated.
+ * @retval -1 An error occured, e.g. socket is already closed.
+ * @retval 0 All values have been updated successfully and
+ * the socket is still alive.
+ */
+static int iscsi_connection_update_key_value_pairs(iscsi_connection *conn)
+{
+ uint32_t recv_buf_size;
+
+ int rc = iscsi_connection_copy_key_value_pairs( conn );
+
+ if ( rc < 0 ) {
+ if ( conn->state < ISCSI_CONNECT_STATE_EXITING )
+ conn->state = ISCSI_CONNECT_STATE_EXITING;
+
+ return rc;
+ }
+
+ rc = iscsi_connection_check_key_value_pairs( conn );
+
+ if ( (rc < 0) && (conn->state < ISCSI_CONNECT_STATE_EXITING) )
+ conn->state = ISCSI_CONNECT_STATE_EXITING;
+
+ if ( conn->sock < 0 )
+ return -1L;
+
+ uint recv_buf_len = conn->session->first_burst_len;
+
+ if ( recv_buf_len < 4096 )
+ recv_buf_len = 4096;
+ else if ( recv_buf_len > 8192 )
+ recv_buf_len > 8192;
+
+ recv_buf_len += (sizeof(struct iscsi_bhs_packet) + 1020UL + conn->header_digest + conn->data_digest); // BHS + maximum AHS size + header and data digest overhead
+ recv_buf_len <<= 2UL; // Receive up to four streams at once.
+
+ setsockopt( conn->sock, SOL_SOCKET, SO_RCVBUF, &recv_buf_len, sizeof(recv_buf_len)); // Not being able to set the buffer is NOT fatal, so ignore error.
+
+ return rc;
+}
+
+/**
* @brief Prepares an iSCSI login response PDU and sends it via TCP/IP.
*
* This function constructs the login response PDU
@@ -2561,9 +2850,36 @@ static void iscsi_connection_pdu_login_response(iscsi_connection *conn, iscsi_pd
* @param[in] user_data Pointer to iSCSI connection which
* was used for sending the response.
*/
-void iscsi_connection_pdu_login_err_complete(uint8_t *user_data)
+static void iscsi_connection_pdu_login_err_complete(uint8_t *user_data)
{
- // TODO: Implement login response error or complete.
+ iscsi_connection *conn = (iscsi_connection *) user_data;
+
+ if ( (conn->flags & ISCSI_CONNECT_FLAGS_FULL_FEATURE) != 0 )
+ iscsi_connection_update_key_value_pairs( conn );
+}
+
+/**
+ * @brief Callback function after login response has been sent.
+ *
+ * This function is invoked after the login
+ * response has been sent to the client via
+ * TCP/IP.
+ *
+ * @param[in] user_data Pointer to iSCSI connection which
+ * was used for sending the response.
+ */
+static void iscsi_connection_pdu_login_ok_complete(uint8_t *user_data)
+{
+ iscsi_connection *conn = (iscsi_connection *) user_data;
+
+ if ( conn->state >= ISCSI_CONNECT_STATE_EXITING )
+ return;
+
+ if ( (conn->flags & ISCSI_CONNECT_FLAGS_FULL_FEATURE) != 0 ) {
+ iscsi_connection_update_key_value_pairs( conn );
+
+ iscsi_connection_schedule( conn );
+ }
}
/**
@@ -2608,10 +2924,10 @@ static iscsi_login_response_packet *iscsi_login_response_create(iscsi_pdu *pdu,
login_response_pkt->isid.d = login_req_pkt->isid.d; // Copying over doesn't change endianess.
login_response_pkt->tsih = login_req_pkt->tsih; // Copying over doesn't change endianess.'
login_response_pkt->init_task_tag = login_req_pkt->init_task_tag; // Copying over doesn't change endianess.
- pdu->cmd_sn = login_req_pkt->cmd_sn;
+ pdu->cmd_sn = iscsi_get_be32(login_req_pkt->cmd_sn);
if ( login_response_pkt->tsih != 0 )
- login_response_pkt->stat_sn = iscsi_get_be32(login_req_pkt->exp_stat_sn);
+ login_response_pkt->stat_sn = login_req_pkt->exp_stat_sn; // Copying over doesn't change endianess.'
if ( ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSMIT) != 0) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE) != 0) ) {
login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR;
@@ -2666,6 +2982,84 @@ int iscsi_connection_save_incoming_key_value_pairs(iscsi_connection *conn, iscsi
}
/**
+ * @brief Extracts the Initiator Session ID (ISID) from packet data into a 64-bit unsigned integer.
+ *
+ * The ISID is constructed by OR'ing and shifting the
+ * four parts a, b, c and d into their proper places
+ * with d being in the LSB area.\n
+ * Since the ISID is only 48 bit wide, the 16
+ * MSB bits are always cleared.
+ *
+ * @param[in] isid Pointer to the ISID part of packet data.
+ * May NOT be NULL, so be careful.
+ * @return The 64-bit unsigned integer value representing
+ * the Initiator Session ID (ISID).
+ */
+static inline uint64_t iscsi_connection_get_isid(const iscsi_isid *isid)
+{
+ return ((uint64_t) isid->a << 40ULL) | ((uint64_t) iscsi_get_be16(isid->b) << 24ULL) | ((uint64_t) isid->c << 16ULL) | (uint64_t) iscsi_get_be16(isid->d);
+}
+
+/**
+ * @brief Initializes the login response port names.
+ *
+ * This function extracts the initiator name from the
+ * key and value pair and stores the result in
+ * the iSCSI connection, as well as a full qualified
+ * initiator port name.
+ *
+ * @param[in] conn Pointer to iSCSI connection where to
+ * store the initiator name.
+ * @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.
+ * @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
+ * successfully, a negative error code otherwise
+ * in which case the status class and detail are
+ * set as well.
+ */
+static int iscsi_connection_login_init_port(iscsi_connection *conn, iscsi_pdu *response_pdu, const iscsi_hashmap *key_value_pairs, uint8_t **init_port_name)
+{
+ iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) response_pdu->bhs_pkt;
+ uint8_t *init_name;
+ int rc = iscsi_get_key_value_pair( key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_INITIATOR_NAME, &init_name );
+
+ if ( rc != 0 ) {
+ login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR;
+ login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISSING_PARAMETER;
+
+ return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE;
+ }
+
+ conn->init_name = iscsi_sprintf_alloc( "%s", init_name );
+
+ if ( conn->init_name == NULL ) {
+ login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SERVER_ERR;
+ login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_OUT_OF_RESOURCES;
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+
+ *init_port_name = iscsi_sprintf_alloc( "%s,i,0x%12.12" PRIx64, init_name, iscsi_connection_get_isid( &login_response_pkt->isid ) );
+
+ if ( *init_port_name == NULL ) {
+ login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SERVER_ERR;
+ login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_OUT_OF_RESOURCES;
+
+ free( conn->init_name );
+ conn->init_name = NULL;
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+
+ return 0L;
+}
+
+/**
* @brief Creates a rejecting login response packet.
*
* The login response structure has status detail
@@ -2789,7 +3183,21 @@ void iscsi_connection_pdu_destroy(iscsi_pdu *pdu)
*/
void iscsi_connection_pdu_write(iscsi_connection *conn, iscsi_pdu *pdu, iscsi_connection_xfer_complete_callback callback, uint8_t *user_data)
{
- // TODO: Implement PDU write.
+ if ( conn->state >= ISCSI_CONNECT_STATE_EXITING )
+ return;
+
+ if ( ISCSI_GET_OPCODE(pdu->bhs_pkt->opcode) != ISCSI_CLIENT_LOGIN_REQ ) {
+ if ( conn->header_digest != 0 )
+ iscsi_calc_header_digest( pdu->bhs_pkt );
+
+ if ( (conn->data_digest != 0) && (pdu->ds_len != 0) )
+ iscsi_calc_data_digest( pdu->bhs_pkt, conn->header_digest );
+ }
+
+ const uint len = sizeof(struct iscsi_bhs_packet) + pdu->ahs_len + conn->header_digest + iscsi_align(pdu->ds_len, ISCSI_ALIGN_SIZE) + conn->data_digest;
+
+ // TODO: Do the writing in a queue.
+ iscsi_connection_write( conn, (uint8_t *) pdu->bhs_pkt, len );
if ( callback != NULL )
callback( user_data );
@@ -3025,10 +3433,10 @@ static int iscsi_connection_pdu_header_handle_text_req(iscsi_connection *conn, i
}
/**
- * @brief Handles an incoming iSCSI header logout request PDU.
+ * @brief Handles an incoming iSCSI header SCSI data out PDU.
*
- * This function handles logout request header
- * data sent by the client.\n
+ * This function handles header SCSI data out
+ * sent by the client.\n
* If a response needs to be sent, this will
* be done as well.
*
@@ -3039,7 +3447,7 @@ static int iscsi_connection_pdu_header_handle_text_req(iscsi_connection *conn, i
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
*/
-static int iscsi_connection_pdu_header_handle_logout_req(iscsi_connection *conn, iscsi_pdu *pdu)
+static int iscsi_connection_pdu_header_handle_scsi_data_out(iscsi_connection *conn, iscsi_pdu *pdu)
{
// TODO: Implement opcode.
@@ -3047,10 +3455,10 @@ static int iscsi_connection_pdu_header_handle_logout_req(iscsi_connection *conn,
}
/**
- * @brief Handles an incoming iSCSI header SCSI data out PDU.
+ * @brief Handles an incoming iSCSI header logout request PDU.
*
- * This function handles header SCSI data out
- * sent by the client.\n
+ * This function handles logout request header
+ * data sent by the client.\n
* If a response needs to be sent, this will
* be done as well.
*
@@ -3061,7 +3469,7 @@ static int iscsi_connection_pdu_header_handle_logout_req(iscsi_connection *conn,
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
*/
-static int iscsi_connection_pdu_header_handle_scsi_data_out(iscsi_connection *conn, iscsi_pdu *pdu)
+static int iscsi_connection_pdu_header_handle_logout_req(iscsi_connection *conn, iscsi_pdu *pdu)
{
// TODO: Implement opcode.
@@ -3156,13 +3564,13 @@ static int iscsi_connection_pdu_header_handle(iscsi_connection *conn, iscsi_pdu
break;
}
- case ISCSI_CLIENT_LOGOUT_REQ : {
- rc = iscsi_connection_pdu_header_handle_logout_req( conn, pdu );
+ case ISCSI_CLIENT_SCSI_DATA_OUT : {
+ rc = iscsi_connection_pdu_header_handle_scsi_data_out( conn, pdu );
break;
}
- case ISCSI_CLIENT_SCSI_DATA_OUT : {
- rc = iscsi_connection_pdu_header_handle_scsi_data_out( conn, pdu );
+ case ISCSI_CLIENT_LOGOUT_REQ : {
+ rc = iscsi_connection_pdu_header_handle_logout_req( conn, pdu );
break;
}
@@ -3182,6 +3590,199 @@ static int iscsi_connection_pdu_header_handle(iscsi_connection *conn, iscsi_pdu
}
/**
+ * @brief Handles an incoming iSCSI payload data NOP-Out request PDU.
+ *
+ * This function handles NOP-Out request payload
+ * data sent by the client.\n
+ * If a response needs to be sent, this will
+ * be done as well.
+ *
+ * @param[in] conn iSCSI connection to handle. May
+ * NOT be NULL, so take caution.
+ * @param[in] pdu iSCSI client request PDU to handle.
+ * May be NULL in which case an error is returned.
+ * @return 0 on success. A negative value indicates
+ * an error. A positive value a warning.
+ */
+static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscsi_pdu *pdu)
+{
+ // TODO: Implement opcode.
+
+ return 0L;
+}
+
+/**
+ * @brief Handles an incoming iSCSI payload data SCSI command request PDU.
+ *
+ * This function handles SCSI command request payload
+ * data sent by the client.\n
+ * If a response needs to be sent, this will
+ * be done as well.
+ *
+ * @param[in] conn iSCSI connection to handle. May
+ * NOT be NULL, so take caution.
+ * @param[in] pdu iSCSI client request PDU to handle.
+ * May be NULL in which case an error is returned.
+ * @return 0 on success. A negative value indicates
+ * an error. A positive value a warning.
+ */
+static int iscsi_connection_pdu_data_handle_scsi_cmd(iscsi_connection *conn, iscsi_pdu *pdu)
+{
+ // TODO: Implement opcode.
+
+ return 0L;
+}
+
+/**
+ * @brief Handles iSCSI connection login phase none.
+ *
+ * This function negotiates the login phase
+ * without a session.
+ *
+ * @param[in] conn Pointer to iSCSI connection,
+ * may NOT be NULL, so be careful.
+ * @param[in] login_response_pdu Pointer to login response PDU.
+ * NULL is not allowed here, so take caution.
+ * @param[in] key_value_pairs Pointer to key and value pairs.
+ * which may NOT be NULL, so take caution.
+ * @param[in] cid Connection ID (CID).
+ * @return 0 on success, a negative error code otherwise.
+ */
+static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_hashmap *key_value_pairs, uint cid)
+{
+ // TODO: Implement function.
+
+ return 0L;
+}
+
+/**
+ * @brief Handles iSCSI connection login response.
+ *
+ * This function negotiates the login parameters
+ * and determines the authentication method.
+ *
+ * @param[in] conn Pointer to iSCSI connection,
+ * may NOT be NULL, so be careful.
+ * @param[in] login_response_pdu Pointer to login response PDU.
+ * NULL is not allowed here, so take caution.
+ * @param[in] key_value_pairs Pointer to key and value pairs.
+ * which may NOT be NULL, so take caution.
+ * @return 0 on success, a negative error code otherwise.
+ */
+static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_hashmap *key_value_pairs)
+{
+ // TODO: Implement function.
+
+ return 0L;
+}
+
+/**
+ * @brief Handles an incoming iSCSI payload data login request PDU.
+ *
+ * This function handles login request payload
+ * data sent by the client.\n
+ * If a response needs to be sent, this will
+ * be done as well.
+ *
+ * @param[in] conn iSCSI connection to handle. May
+ * NOT be NULL, so take caution.
+ * @param[in] pdu iSCSI client request PDU to handle.
+ * May be NULL in which case an error is returned.
+ * @return 0 on success. A negative value indicates
+ * an error. A positive value a warning.
+ */
+static int iscsi_connection_pdu_data_handle_login_req(iscsi_connection *conn, iscsi_pdu *pdu)
+{
+ iscsi_pdu *login_response_pdu = (iscsi_pdu *) conn->login_response_pdu;
+
+ if ( login_response_pdu == NULL )
+ return 0L;
+
+ iscsi_hashmap *key_value_pairs = iscsi_hashmap_create( 32UL );
+
+ if ( key_value_pairs == NULL )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ iscsi_login_req_packet *login_req_pkt = (iscsi_login_req_packet *) pdu->bhs_pkt;
+ uint cid = iscsi_get_be16(login_req_pkt->cid);
+ int rc = iscsi_connection_save_incoming_key_value_pairs( conn, key_value_pairs, pdu, login_response_pdu );
+
+ if ( rc < 0 ) {
+ iscsi_connection_pdu_login_response( conn, login_response_pdu, NULL, iscsi_connection_pdu_login_err_complete );
+
+ return 0L;
+ }
+
+ if ( conn->state == ISCSI_CONNECT_STATE_INVALID ) {
+ rc = iscsi_connection_handle_login_phase_none( conn, login_response_pdu, key_value_pairs, cid );
+
+ if ( (rc == ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE) || (rc == ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER) ) {
+ iscsi_connection_pdu_login_response( conn, login_response_pdu, key_value_pairs, iscsi_connection_pdu_login_err_complete );
+
+ return 0L;
+ }
+ }
+
+ rc = iscsi_connecction_handle_login_response( conn, login_response_pdu, key_value_pairs );
+
+ if ( rc == ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE ) {
+ iscsi_connection_pdu_login_response( conn, login_response_pdu, key_value_pairs, iscsi_connection_pdu_login_err_complete );
+
+ return 0L;
+ }
+
+ conn->state = ISCSI_CONNECT_STATE_RUNNING;
+
+ iscsi_connection_pdu_login_response( conn, login_response_pdu, key_value_pairs, iscsi_connection_pdu_login_ok_complete );
+
+ return 0L;
+}
+
+/**
+ * @brief Handles an incoming iSCSI payload data text request PDU.
+ *
+ * This function handles text request payload
+ * data sent by the client.\n
+ * If a response needs to be sent, this will
+ * be done as well.
+ *
+ * @param[in] conn iSCSI connection to handle. May
+ * NOT be NULL, so take caution.
+ * @param[in] pdu iSCSI client request PDU to handle.
+ * May be NULL in which case an error is returned.
+ * @return 0 on success. A negative value indicates
+ * an error. A positive value a warning.
+ */
+static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, iscsi_pdu *pdu)
+{
+ // TODO: Implement opcode.
+
+ return 0L;
+}
+
+/**
+ * @brief Handles an incoming iSCSI payload data SCSI data out request PDU.
+ *
+ * This function handles SCSI data out request
+ * payload data sent by the client.\n
+ * If a response needs to be sent, this will
+ * be done as well.
+ *
+ * @param[in] conn iSCSI connection to handle. May
+ * NOT be NULL, so take caution.
+ * @param[in] pdu iSCSI client request PDU to handle.
+ * May be NULL in which case an error is returned.
+ * @return 0 on success. A negative value indicates
+ * an error. A positive value a warning.
+ */
+static int iscsi_connection_pdu_data_handle_scsi_data_out(iscsi_connection *conn, iscsi_pdu *pdu)
+{
+ // TODO: Implement opcode.
+
+ return 0L;
+}
+
+/**
* @brief Handles an incoming iSCSI payload data PDU.
*
* This function handles all payload data sent
@@ -3198,9 +3799,49 @@ static int iscsi_connection_pdu_header_handle(iscsi_connection *conn, iscsi_pdu
*/
static int iscsi_connection_pdu_data_handle(iscsi_connection *conn, iscsi_pdu *pdu)
{
- // TODO: Implement PDU payload data handler.
+ int rc = 0;
- return 0L;
+ const uint8_t opcode = ISCSI_GET_OPCODE(pdu->bhs_pkt->opcode);
+
+ switch ( opcode ) {
+ case ISCSI_CLIENT_NOP_OUT : {
+ rc = iscsi_connection_pdu_data_handle_nop_out( conn, pdu );
+
+ break;
+ }
+ case ISCSI_CLIENT_SCSI_CMD : {
+ rc = iscsi_connection_pdu_data_handle_scsi_cmd( conn, pdu );
+
+ break;
+ }
+ case ISCSI_CLIENT_LOGIN_REQ : {
+ rc = iscsi_connection_pdu_data_handle_login_req( conn, pdu );
+
+ break;
+ }
+ case ISCSI_CLIENT_TEXT_REQ : {
+ rc = iscsi_connection_pdu_data_handle_text_req( conn, pdu );
+
+ break;
+ }
+ case ISCSI_CLIENT_SCSI_DATA_OUT : {
+ rc = iscsi_connection_pdu_data_handle_scsi_data_out( conn, pdu );
+
+ break;
+ }
+ case ISCSI_CLIENT_TASK_FUNC_REQ :
+ case ISCSI_CLIENT_LOGOUT_REQ :
+ case ISCSI_CLIENT_SNACK_REQ : {
+ break;
+ }
+ default : {
+ return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR );
+
+ break;
+ }
+ }
+
+ return rc;
}
/**
diff --git a/src/server/iscsi.h b/src/server/iscsi.h
index fddf04f..c167987 100644
--- a/src/server/iscsi.h
+++ b/src/server/iscsi.h
@@ -254,7 +254,8 @@ int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, u
/// iSCSI Basic Header Segment size.
#define ISCSI_BHS_SIZE 48UL
-/// Default receive DataSegment (DS) size in bytes.
+
+/// iSCSI Default receive DataSegment (DS) size in bytes.
#define ISCSI_DEFAULT_RECV_DS_LEN 8192UL
/// iSCSI default maximum DataSegment receive length in bytes
@@ -5868,6 +5869,13 @@ void iscsi_port_destroy(iscsi_port *port); // Deallocates all resource acquired
#define ISCSI_DEVICE_FLAGS_REMOVED (1 << 1)
+/**
+ * @brief iSCSI device.
+ *
+ * This structure managesw the SCSI
+ * devices and associates the
+ * disk image files with them.
+ */
typedef struct iscsi_device {
/// Name of device.
uint8_t *name;
@@ -6069,16 +6077,19 @@ typedef struct iscsi_session {
/// iSCSI connection read packet data return code from iscsi_connection_pdu_read function: Packet parsed successfully.
-#define ISCSI_CONNECT_PDU_READ_OK 0L
+#define ISCSI_CONNECT_PDU_READ_OK 0L
/// iSCSI connection read packet data return code from iscsi_connection_pdu_read function: Packet processed successfully.
-#define ISCSI_CONNECT_PDU_READ_PROCESSED 1L
+#define ISCSI_CONNECT_PDU_READ_PROCESSED 1L
/// iSCSI connection read packet data return code from iscsi_connection_pdu_read function: Fatail error during packet parsing.
-#define ISCSI_CONNECT_PDU_READ_ERR_FATAL -1L
+#define ISCSI_CONNECT_PDU_READ_ERR_FATAL -1L
/// iSCSI connection read packet data return code from iscsi_connection_pdu_read function: Login error response.
-#define ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE -2L
+#define ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE -2L
+
+/// iSCSI connection read packet data return code from iscsi_connection_pdu_read function: Login parameter error.
+#define ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER -3L
/// iSCSI connection flags: Stopped.
@@ -6138,6 +6149,9 @@ typedef struct iscsi_connection {
/// Temporarily storage for partially received parameter.
uint8_t *partial_pairs;
+ /// Initiator name.
+ uint8_t *init_name;
+
/// iSCSI connection contains a header digest (CRC32), always MUST be 0 or 4 for now.
int header_digest;
@@ -6191,6 +6205,12 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock);
int iscsi_connection_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // iSCSI connection destructor callback for hash map
void iscsi_connection_destroy(iscsi_connection *conn); // Deallocates all resources acquired by iscsi_connection_create
+void iscsi_connection_schedule(iscsi_connection *conn); // Schedules an iSCSI connection
+
+int iscsi_connection_read(const iscsi_connection *conn, uint8_t *buf, const uint len); // Reads data for the specified iSCSI connection from its TCP socket
+int iscsi_connection_write(const iscsi_connection *conn, uint8_t *buf, const uint len); // Writes data for the specified iSCSI connection to its TCP socket
+
+int iscsi_connection_copy_key_value_pairs(iscsi_connection *conn); // Copies retrieved key and value pairs into SCSI connection and session structures
/// iSCSI PDU flags: Rejected.
#define ISCSI_PDU_FLAGS_REJECTED (1 << 0)
@@ -6262,7 +6282,7 @@ iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn); // Creates an iS
void iscsi_connection_pdu_destroy(iscsi_pdu *pdu); // Destroys an iSCSI PDU structure used by connections
int iscsi_connection_read_data(struct spdk_iscsi_conn *conn, int len, void *buf);
-int iscsi_connection_readv_data(struct spdk_iscsi_conn *conn, struct iovec *iov, int iovcnt);
+int iscsi_connection_read_iov_data(struct spdk_iscsi_conn *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);
#endif /* DNBD3_ISCSI_H_ */