diff options
| author | Sebastian Vater | 2025-08-20 11:41:49 +0200 |
|---|---|---|
| committer | Sebastian Vater | 2025-08-20 11:41:49 +0200 |
| commit | 89e4269db10cc24a1ff8af6084cf38dcd732aa39 (patch) | |
| tree | 6995d3c23631833a306c2af7169c1dbb067bba75 | |
| parent | Added iSCSI handling to main server and network handling code and also creati... (diff) | |
| download | dnbd3-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.c | 337 | ||||
| -rw-r--r-- | src/server/iscsi.h | 26 |
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 |
