summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Vater2025-08-20 11:41:49 +0200
committerSebastian Vater2025-08-20 11:41:49 +0200
commit89e4269db10cc24a1ff8af6084cf38dcd732aa39 (patch)
tree6995d3c23631833a306c2af7169c1dbb067bba75
parentAdded iSCSI handling to main server and network handling code and also creati... (diff)
downloaddnbd3-89e4269db10cc24a1ff8af6084cf38dcd732aa39.tar.gz
dnbd3-89e4269db10cc24a1ff8af6084cf38dcd732aa39.tar.xz
dnbd3-89e4269db10cc24a1ff8af6084cf38dcd732aa39.zip
Mostly implemented iSCSI text response handling. Finally, fixed some smaller bugs.
-rw-r--r--src/server/iscsi.c337
-rw-r--r--src/server/iscsi.h26
2 files changed, 312 insertions, 51 deletions
diff --git a/src/server/iscsi.c b/src/server/iscsi.c
index 7546f00..43bb228 100644
--- a/src/server/iscsi.c
+++ b/src/server/iscsi.c
@@ -1682,7 +1682,7 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint
if ( (ISCSI_VERSION_MIN < login_req_pkt->version_min) || (ISCSI_VERSION_MAX > login_req_pkt->version_max) )
return ISCSI_VALIDATE_PACKET_RESULT_ERROR_UNSUPPORTED_VERSION;
- if ( (ISCSI_LOGIN_REQ_FLAGS_GET_CURRENT_STAGE(login_req_pkt->flags) == ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_RESERVED) || ((ISCSI_LOGIN_REQ_FLAGS_GET_NEXT_STAGE(login_req_pkt->flags) == ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_RESERVED) && ((login_req_pkt->flags & ISCSI_LOGIN_REQ_FLAGS_TRANSIT) != 0)) || ((login_req_pkt->flags < 0) && ((login_req_pkt->flags & ISCSI_LOGIN_REQ_FLAGS_CONTINUE) != 0)) || (login_req_pkt->flags & ~(ISCSI_LOGIN_REQ_FLAGS_CONTINUE | ISCSI_LOGIN_REQ_FLAGS_TRANSIT) != 0) || (login_req_pkt->reserved != 0) || (login_req_pkt->reserved2[0] != 0) || (login_req_pkt->reserved2[1] != 0) )
+ if ( (ISCSI_LOGIN_REQ_FLAGS_GET_CURRENT_STAGE(login_req_pkt->flags) == ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_RESERVED) || ((ISCSI_LOGIN_REQ_FLAGS_GET_NEXT_STAGE(login_req_pkt->flags) == ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_RESERVED) && ((login_req_pkt->flags & ISCSI_LOGIN_REQ_FLAGS_TRANSIT) != 0)) || ((login_req_pkt->flags < 0) && ((login_req_pkt->flags & ISCSI_LOGIN_REQ_FLAGS_CONTINUE) != 0)) || ((login_req_pkt->flags & ~(ISCSI_LOGIN_REQ_FLAGS_CONTINUE | ISCSI_LOGIN_REQ_FLAGS_TRANSIT)) != 0) || (login_req_pkt->reserved != 0) || (login_req_pkt->reserved2[0] != 0) || (login_req_pkt->reserved2[1] != 0) )
return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Current Stage (CSG) is set to reserved and Next Stage (NSG) is reserved and T bit is set, if C bit is set, T MUST be cleared and reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
return iscsi_validate_key_value_pairs( ((const uint8_t *) login_req_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len );
@@ -1695,7 +1695,7 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint
const iscsi_text_req_packet *text_req_pkt = (const iscsi_text_req_packet *) packet_data;
- if ( ((text_req_pkt->flags < 0) && ((text_req_pkt->flags & ISCSI_TEXT_REQ_FLAGS_CONTINUE) != 0)) || (text_req_pkt->flags & ~(ISCSI_TEXT_REQ_FLAGS_CONTINUE | ISCSI_TEXT_REQ_FLAGS_FINAL) != 0) || (text_req_pkt->reserved != 0) || (text_req_pkt->reserved2[0] != 0) || (text_req_pkt->reserved2[1] != 0) )
+ if ( ((text_req_pkt->flags < 0) && ((text_req_pkt->flags & ISCSI_TEXT_REQ_FLAGS_CONTINUE) != 0)) || ((text_req_pkt->flags & ~(ISCSI_TEXT_REQ_FLAGS_CONTINUE | ISCSI_TEXT_REQ_FLAGS_FINAL)) != 0) || (text_req_pkt->reserved != 0) || (text_req_pkt->reserved2[0] != 0) || (text_req_pkt->reserved2[1] != 0) )
return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // If C bit is set, F MUST be cleared and reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
return iscsi_validate_key_value_pairs( ((const uint8_t *) text_req_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len );
@@ -1782,7 +1782,7 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint
if ( (ISCSI_VERSION_MIN < login_response_pkt->version_max) || (ISCSI_VERSION_MAX > login_response_pkt->version_max) || (ISCSI_VERSION_MIN < login_response_pkt->version_active) || (ISCSI_VERSION_MAX > login_response_pkt->version_active) )
return ISCSI_VALIDATE_PACKET_RESULT_ERROR_UNSUPPORTED_VERSION;
- if ( (ISCSI_LOGIN_RESPONSE_FLAGS_GET_CURRENT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_RESERVED) || ((ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_RESERVED) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0)) || ((login_response_pkt->flags < 0) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE) != 0)) || (login_response_pkt->flags & ~(ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE | ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0) || (login_response_pkt->reserved != 0) || (login_response_pkt->reserved2 != 0) || (login_response_pkt->reserved3 != 0) )
+ if ( (ISCSI_LOGIN_RESPONSE_FLAGS_GET_CURRENT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_RESERVED) || ((ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_RESERVED) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0)) || ((login_response_pkt->flags < 0) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE) != 0)) || ((login_response_pkt->flags & ~(ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE | ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT)) != 0) || (login_response_pkt->reserved != 0) || (login_response_pkt->reserved2 != 0) || (login_response_pkt->reserved3 != 0) )
return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Current Stage (CSG) is set to reserved and Next Stage (NSG) is reserved and T bit is set, if C bit is set, T MUST be cleared and reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
return iscsi_validate_key_value_pairs( ((const uint8_t *) login_response_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len );
@@ -1795,7 +1795,7 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint
const iscsi_text_response_packet *text_response_pkt = (const iscsi_text_response_packet *) packet_data;
- if ( ((text_response_pkt->flags < 0) && ((text_response_pkt->flags & ISCSI_TEXT_RESPONSE_FLAGS_CONTINUE) != 0)) || (text_response_pkt->flags & ~(ISCSI_TEXT_RESPONSE_FLAGS_CONTINUE | ISCSI_TEXT_RESPONSE_FLAGS_FINAL) != 0) || (text_response_pkt->reserved != 0) || (text_response_pkt->reserved2[0] != 0) || (text_response_pkt->reserved2[1] != 0) )
+ if ( ((text_response_pkt->flags < 0) && ((text_response_pkt->flags & ISCSI_TEXT_RESPONSE_FLAGS_CONTINUE) != 0)) || ((text_response_pkt->flags & ~(ISCSI_TEXT_RESPONSE_FLAGS_CONTINUE | ISCSI_TEXT_RESPONSE_FLAGS_FINAL)) != 0) || (text_response_pkt->reserved != 0) || (text_response_pkt->reserved2[0] != 0) || (text_response_pkt->reserved2[1] != 0) )
return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // If C bit is set, F MUST be cleared and reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
return iscsi_validate_key_value_pairs( ((const uint8_t *) text_response_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len );
@@ -2722,6 +2722,33 @@ iscsi_port *iscsi_device_find_port_by_portal_group_tag(const iscsi_device *devic
}
/**
+ * @brief Sends a buffer from a source iSCSI IQN to target iSCSI IQNs.
+ *
+ * This function sends a buffer starting from a
+ * specified position to one, multiple or all
+ * target IQNs.\n
+ * This function also checks the input and output
+ * IQN for conforming to iSCSI specifications.
+ *
+ * @param[in] conn Pointer to iSCSI connection to write the buffer.
+ * @param[in] dst_iqn Pointer to string containing the target IQNs,
+ * NULL is not allowed here, take caution.
+ * @param[in] src_iqn Pointer to string containing the source IQN.
+ * May NOT be NULL, so be careful.
+ * @param[in] buf Pointer to output buffer.
+ * @param[in] pos Position in buffer in bytes to start sending.
+ * @param[in] len Length of buffer in bytes.
+ * @return The new position of the written data or a
+ * negative error code otherwise.
+ */
+int iscsi_target_node_send(iscsi_connection *conn, const uint8_t *dst_iqn, const uint8_t *src_iqn, uint8_t *buf, uint pos, const uint len)
+{
+ // TODO: Implement function.
+
+ return -1L;
+}
+
+/**
* @brief Finds an iSCSI target node by case insensitive name search.
*
* Callback function for each element while iterating
@@ -3040,7 +3067,7 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock)
conn->key_value_pairs = iscsi_hashmap_create( 32UL );
if ( conn->key_value_pairs == NULL ) {
- logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI text key / value pair hash map" );
+ logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI login text key / value pair hash map" );
free( conn );
@@ -3057,39 +3084,53 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock)
return NULL;
}
- conn->partial_pairs = NULL;
- conn->device = NULL;
- conn->init_port = NULL;
- conn->init_name = NULL;
- conn->init_adr = NULL;
- conn->target = NULL;
- conn->target_port = NULL;
- conn->target_name_short = NULL;
- conn->portal_host = NULL;
- conn->portal_port = NULL;
- conn->header_digest = 0L;
- conn->data_digest = 0L;
- conn->pdu_processing = NULL;
- conn->login_response_pdu = NULL;
- conn->id = 0L;
- conn->sock = sock;
- conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_READY;
- conn->flags = 0L;
- conn->state = ISCSI_CONNECT_STATE_INVALID;
- conn->login_phase = ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION;
- conn->max_recv_ds_len = ISCSI_DEFAULT_RECV_DS_LEN;
- conn->pg_tag = portal->group->tag;
- conn->isid.a = 0;
- conn->isid.b = 0;
- conn->isid.c = 0;
- conn->isid.d = 0;
- conn->tsih = 0U;
- conn->cid = 0U;
- conn->init_task_tag = 0UL;
- conn->auth_chap.phase = ISCSI_AUTH_CHAP_PHASE_NONE;
- conn->chap_group = 0L;
- conn->stat_sn = 0UL;
- conn->exp_stat_sn = 0UL;
+ conn->partial_pairs = NULL;
+ conn->text_key_value_pairs = iscsi_hashmap_create( 32UL );
+
+ if ( conn->text_key_value_pairs == NULL ) {
+ logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI text key / value pair hash map" );
+
+ 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->text_partial_pairs = NULL;
+ conn->device = NULL;
+ conn->init_port = NULL;
+ conn->init_name = NULL;
+ conn->init_adr = NULL;
+ conn->target = NULL;
+ conn->target_port = NULL;
+ conn->target_name_short = NULL;
+ conn->portal_host = NULL;
+ conn->portal_port = NULL;
+ conn->pdu_processing = NULL;
+ conn->login_response_pdu = NULL;
+ conn->target_send_total_size = 0UL;
+ conn->header_digest = 0L;
+ conn->data_digest = 0L;
+ conn->id = 0L;
+ conn->sock = sock;
+ conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_READY;
+ conn->flags = 0L;
+ conn->state = ISCSI_CONNECT_STATE_INVALID;
+ conn->login_phase = ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION;
+ conn->max_recv_ds_len = ISCSI_DEFAULT_RECV_DS_LEN;
+ conn->pg_tag = portal->group->tag;
+ conn->isid.a = 0;
+ conn->isid.b = 0;
+ conn->isid.c = 0;
+ conn->isid.d = 0;
+ conn->tsih = 0U;
+ conn->cid = 0U;
+ conn->init_task_tag = 0UL;
+ conn->auth_chap.phase = ISCSI_AUTH_CHAP_PHASE_NONE;
+ conn->chap_group = 0L;
+ conn->stat_sn = 0UL;
+ conn->exp_stat_sn = 0UL;
return conn;
}
@@ -3166,13 +3207,25 @@ void iscsi_connection_destroy(iscsi_connection *conn)
conn->init_name = NULL;
}
+ if ( conn->text_partial_pairs != NULL ) {
+ free ( conn->text_partial_pairs );
+
+ conn->text_partial_pairs = NULL;
+ }
+
+ if ( conn->text_key_value_pairs != NULL ) {
+ iscsi_hashmap_iterate( conn->text_key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( conn->text_key_value_pairs );
+
+ conn->text_key_value_pairs = NULL;
+ }
+
if ( conn->partial_pairs != NULL ) {
free ( conn->partial_pairs );
conn->partial_pairs = NULL;
}
-
if ( conn->key_value_pairs != NULL ) {
iscsi_hashmap_iterate( conn->key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
iscsi_hashmap_destroy( conn->key_value_pairs );
@@ -4795,9 +4848,26 @@ static int iscsi_connection_pdu_header_handle_task_func_req(iscsi_connection *co
*/
static int iscsi_connection_pdu_header_handle_text_req(iscsi_connection *conn, iscsi_pdu *pdu)
{
- // TODO: Implement opcode.
+ 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) )
+ return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR );
- return 0;
+ iscsi_text_req_packet *text_req_pkt = (iscsi_text_req_packet *) pdu->bhs_pkt;
+
+ const uint32_t init_task_tag = iscsi_get_be32(text_req_pkt->init_task_tag);
+ const uint32_t exp_stat_sn = iscsi_get_be32(text_req_pkt->exp_stat_sn);
+
+ if ( exp_stat_sn != conn->stat_sn )
+ conn->stat_sn = exp_stat_sn;
+
+ if ( (text_req_pkt->opcode & (0x40 | -0x80)) == (0x40 | -0x80) )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ if ( conn->session->current_text_init_task_tag == 0xFFFFFFFFUL )
+ conn->session->current_text_init_task_tag = init_task_tag;
+ else
+ return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR );
+
+ return ISCSI_CONNECT_PDU_READ_OK;
}
/**
@@ -5792,6 +5862,23 @@ static int iscsi_connection_pdu_data_handle_login_req(iscsi_connection *conn, is
}
/**
+ * @brief Callback function after text response has been sent.
+ *
+ * This function is invoked after the text
+ * 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_text_complete(uint8_t *user_data)
+{
+ iscsi_connection *conn = (iscsi_connection *) user_data;
+
+ iscsi_connection_update_key_value_pairs( conn );
+}
+
+/**
* @brief Handles an incoming iSCSI payload data text request PDU.
*
* This function handles text request payload
@@ -5808,9 +5895,173 @@ static int iscsi_connection_pdu_data_handle_login_req(iscsi_connection *conn, is
*/
static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, iscsi_pdu *pdu)
{
- // TODO: Implement opcode.
+ iscsi_hashmap *key_value_pairs = iscsi_hashmap_create( 32UL );
- return 0L;
+ if ( key_value_pairs == NULL )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ iscsi_text_req_packet *text_req_pkt = (iscsi_text_req_packet *) pdu->bhs_pkt;
+ int rc = iscsi_parse_key_value_pairs( key_value_pairs, (uint8_t *) pdu->ds_cmd_data, pdu->ds_len, ((text_req_pkt->flags & ISCSI_TEXT_REQ_FLAGS_CONTINUE) != 0), &conn->text_partial_pairs );
+
+ if ( rc < 0 ) {
+ iscsi_hashmap_iterate( key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( key_value_pairs );
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+
+ if ( (pdu->ds_len == 0) && (iscsi_hashmap_size( key_value_pairs ) == 0) ) {
+ iscsi_hashmap *tmp_hashmap = key_value_pairs;
+ key_value_pairs = conn->text_key_value_pairs;
+ conn->text_key_value_pairs = tmp_hashmap;
+ }
+
+ iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn );
+
+ if ( response_pdu == NULL ) {
+ logadd( LOG_ERROR, "iscsi_connection_pdu_data_handle_text_req: Out of memory while allocating iSCSI text response PDU" );
+
+ iscsi_hashmap_iterate( key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( key_value_pairs );
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+
+ const uint32_t ds_len = conn->max_recv_ds_len;
+ iscsi_text_response_packet *text_response_pkt = (iscsi_text_response_packet *) iscsi_append_ds_packet( response_pdu->bhs_pkt, conn->header_digest, ds_len, conn->data_digest );
+
+ if ( text_response_pkt == NULL ) {
+ logadd( LOG_ERROR, "iscsi_connection_pdu_data_handle_text_req: Out of memory while allocating iSCSI text packet data" );
+
+ iscsi_hashmap_iterate( key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( key_value_pairs );
+ iscsi_connection_pdu_destroy( response_pdu );
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+
+ response_pdu->bhs_pkt = (iscsi_bhs_packet *) text_response_pkt;
+
+ if ( conn->header_digest != 0 ) {
+ response_pdu->header_digest = (iscsi_header_digest *) (((iscsi_bhs_packet *) text_response_pkt) + 1);
+ 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_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;
+ }
+
+ response_pdu->pos = iscsi_negotiate_key_value_pairs( conn, key_value_pairs, (uint8_t *) response_pdu->ds_cmd_data, response_pdu->pos, response_pdu->ds_len );
+
+ if ( (int) response_pdu->pos < 0 ) {
+ iscsi_hashmap_iterate( key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( key_value_pairs );
+ iscsi_connection_pdu_destroy( response_pdu );
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+
+ text_response_pkt->opcode = ISCSI_SERVER_TEXT_RES;
+
+ if ( (text_req_pkt->flags & ISCSI_TEXT_REQ_FLAGS_CONTINUE) != 0 )
+ text_response_pkt->flags |= (int8_t) ISCSI_TEXT_RESPONSE_FLAGS_CONTINUE;
+
+ if ( (text_req_pkt->flags & ISCSI_TEXT_REQ_FLAGS_FINAL) != 0 )
+ text_response_pkt->flags |= (int8_t) ISCSI_TEXT_RESPONSE_FLAGS_FINAL;
+
+ uint8_t *send_targets_val;
+ rc = iscsi_get_key_value_pair( key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_SEND_TARGETS, &send_targets_val );
+
+ if ( rc < 0 ) {
+ uint8_t *type_val;
+ rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_SESSION_TYPE, &type_val );
+
+ if ( (rc >= 0) && (type_val != NULL) && (strcasecmp( (char *) type_val, "Discovery" ) == 0) ) {
+ iscsi_hashmap_iterate( key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( key_value_pairs );
+ iscsi_connection_pdu_destroy( response_pdu );
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+ } else {
+ uint8_t *type_val;
+ rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_SESSION_TYPE, &type_val );
+
+ if ( (rc >= 0) && (type_val != NULL) && (strcasecmp( (char *) type_val, "Discovery" ) == 0) ) {
+ if ( send_targets_val[0] == '\0' )
+ send_targets_val = (uint8_t *) "ALL";
+
+ response_pdu->pos = iscsi_target_node_send( conn, send_targets_val, conn->init_name, (uint8_t *) response_pdu->ds_cmd_data, response_pdu->pos, response_pdu->ds_len );
+ } else {
+ if ( send_targets_val[0] == '\0' )
+ send_targets_val = conn->target_port->name;
+
+ if ( strcasecmp( (char *) send_targets_val, "ALL" ) == 0 ) {
+ iscsi_key_value_pair *key_value_pair;
+ rc = iscsi_hashmap_get( iscsi_globvec->session_key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_SEND_TARGETS, strlen( ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_SEND_TARGETS ) + 1, (uint8_t **) &key_value_pair);
+
+ if ( rc < 0 ) {
+ iscsi_hashmap_iterate( key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( key_value_pairs );
+ iscsi_connection_pdu_destroy( response_pdu );
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+
+ response_pdu->pos = iscsi_append_key_value_pair_packet( key_value_pair, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_SEND_TARGETS, (uint8_t *) "Reject", (uint8_t *) response_pdu->ds_cmd_data, response_pdu->pos, response_pdu->ds_len );
+ } else {
+ response_pdu->pos = iscsi_target_node_send( conn, send_targets_val, conn->init_name, (uint8_t *) response_pdu->ds_cmd_data, response_pdu->pos, response_pdu->ds_len );
+ }
+ }
+
+ if ( conn->target_send_total_size == 0 ) {
+ text_response_pkt->flags |= (int8_t) ISCSI_TEXT_RESPONSE_FLAGS_CONTINUE;
+ text_response_pkt->flags &= (int8_t) ~ISCSI_TEXT_RESPONSE_FLAGS_FINAL;
+ }
+ }
+
+ if ( (int) response_pdu->pos < 0 ) {
+ iscsi_hashmap_iterate( key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( key_value_pairs );
+ iscsi_connection_pdu_destroy( response_pdu );
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+
+ if ( conn->target_send_total_size == 0 ) {
+ iscsi_hashmap_iterate( key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( key_value_pairs );
+ } else {
+ conn->text_key_value_pairs = key_value_pairs;
+ }
+
+ iscsi_put_be24( (uint8_t *) &text_response_pkt->ds_len, (uint32_t) response_pdu->pos );
+ text_response_pkt->lun = text_req_pkt->lun; // Copying over doesn't change endianess.
+ text_response_pkt->init_task_tag = text_req_pkt->init_task_tag; // Copying over doesn't change endianess.
+
+ if ( (text_req_pkt->flags & ISCSI_TEXT_REQ_FLAGS_FINAL) != 0 ) {
+ text_response_pkt->target_xfer_tag = 0xFFFFFFFFUL;
+
+ conn->session->current_text_init_task_tag = 0xFFFFFFFFUL;
+ } else {
+ iscsi_put_be32( (uint8_t *) &text_response_pkt->target_xfer_tag, (uint32_t) conn->id + 1UL );
+ }
+
+ iscsi_put_be32( (uint8_t *) &text_response_pkt->stat_sn, conn->stat_sn++ );
+
+ if ( (text_response_pkt->opcode & 0x40) == 0 )
+ conn->session->max_cmd_sn++;
+
+ iscsi_put_be32( (uint8_t *) &text_response_pkt->exp_cmd_sn, conn->session->exp_cmd_sn );
+ iscsi_put_be32( (uint8_t *) &text_response_pkt->max_cmd_sn, conn->session->max_cmd_sn );
+
+ iscsi_connection_pdu_write( conn, response_pdu, iscsi_connection_pdu_text_complete, (uint8_t *) conn );
+
+ return ISCSI_CONNECT_PDU_READ_OK;
}
/**
diff --git a/src/server/iscsi.h b/src/server/iscsi.h
index c802d05..9f7b355 100644
--- a/src/server/iscsi.h
+++ b/src/server/iscsi.h
@@ -6348,12 +6348,18 @@ typedef struct iscsi_connection {
/// iSCSI session associated with this connection.
iscsi_session *session;
- /// Hash map containing text key / value pairs associated to this connection.
+ /// Hash map containing login text key / value pairs associated to this connection.
iscsi_hashmap *key_value_pairs;
- /// Temporarily storage for partially received parameter.
+ /// Temporarily storage for partially received login parameter.
uint8_t *partial_pairs;
+ /// Hash map containing text key / value pairs associated to this connection.
+ iscsi_hashmap *text_key_value_pairs;
+
+ /// Temporarily storage for partially received text parameter.
+ uint8_t *text_partial_pairs;
+
/// iSCSI device.
iscsi_device *device;
@@ -6381,18 +6387,21 @@ typedef struct iscsi_connection {
/// iSCSI portal host port.
uint8_t *portal_port;
- /// iSCSI connection contains a header digest (CRC32), always MUST be 0 or 4 for now.
- int header_digest;
-
- /// iSCSI connection contains a data digest (CRC32), always MUST be 0 or 4 for now.
- int data_digest;
-
/// Current PDU being processed.
struct iscsi_pdu *pdu_processing;
/// Login response PDU.
struct iscsi_pdu *login_response_pdu;
+ /// iSCSI SendTargets total number of bytes completed.
+ uint target_send_total_size;
+
+ /// iSCSI connection contains a header digest (CRC32), always MUST be 0 or 4 for now.
+ int header_digest;
+
+ /// iSCSI connection contains a data digest (CRC32), always MUST be 0 or 4 for now.
+ int data_digest;
+
/// Internal connection identifier (key of iSCSI global vector hash map).
int id;
@@ -6517,6 +6526,7 @@ typedef struct iscsi_pdu {
} iscsi_pdu;
iscsi_port *iscsi_device_find_port_by_portal_group_tag(const iscsi_device *device, const uint64_t id); // Gets an iSCSI device being in use by portal group identifier
+int iscsi_target_node_send(iscsi_connection *conn, const uint8_t *dst_iqn, const uint8_t *src_iqn, uint8_t *buf, uint pos, const uint len); // Sends a buffer from a source iSCSI IQN to target iSCSI IQNs
int iscsi_target_node_find_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Finds an iSCSI target node by case insensitive name search
iscsi_target_node *iscsi_target_node_find(uint8_t *target_name); // Searches an iSCSI target node by name using case insensitive search