From 254dea5ab33170e1c7d7dde19c73b82da35f4e0f Mon Sep 17 00:00:00 2001 From: Sebastian Vater Date: Tue, 19 Aug 2025 16:53:14 +0200 Subject: Added iSCSI handling to main server and network handling code and also creation of basic iSCSI portal and initial iSCSI connection. Finally, implemented iSCSI logout response. --- src/server/iscsi.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/server/iscsi.h | 7 ++- src/server/net.c | 2 + src/server/server.c | 8 +++ 4 files changed, 178 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/server/iscsi.c b/src/server/iscsi.c index bfda85b..7546f00 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -27,7 +27,12 @@ #include #include #include +#include +#include #include + +#include "globals.h" +#include "helper.h" #include "iscsi.h" /** @@ -43,6 +48,9 @@ * @see https://www.rfc-editor.org/rfc/rfc7143 */ +/// iSCSI global vector. MUST be initialized with iscsi_create before any iSCSI functions are used. +iscsi_globals *iscsi_globvec = NULL; + /// iSCSI connection negotation key and value pair lookup table. static const iscsi_key_value_pair_lut_entry iscsi_connection_key_value_pair_lut[] = { { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_HEADER_DIGEST, (uint8_t *) "None", (uint8_t *) "CRC32C\0None\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST, 0L }, @@ -4717,7 +4725,7 @@ static int iscsi_connection_pdu_header_handle_nop_out(iscsi_connection *conn, is const uint32_t init_task_tag = iscsi_get_be32(nop_out_pkt->init_task_tag); const uint32_t target_xfer_tag = iscsi_get_be32(nop_out_pkt->target_xfer_tag); - if ( (target_xfer_tag != 0xFFFFFFFFUL) && (target_xfer_tag != conn->id) ) + if ( (target_xfer_tag != 0xFFFFFFFFUL) && (target_xfer_tag != (uint32_t) conn->id) ) return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_INVALID_PDU_FIELD ); // TODO: Check if this is the correct error code. if ( (init_task_tag == 0xFFFFFFFFUL) && (nop_out_pkt->opcode & 0x40) == 0 ) @@ -4831,9 +4839,70 @@ static int iscsi_connection_pdu_header_handle_scsi_data_out(iscsi_connection *co */ static int iscsi_connection_pdu_header_handle_logout_req(iscsi_connection *conn, iscsi_pdu *pdu) { - // TODO: Implement opcode. + iscsi_logout_req_packet *logout_req_pkt = (iscsi_logout_req_packet *) pdu->bhs_pkt; - return 0; + if ( (conn->session != NULL) && (conn->session->type == ISCSI_SESSION_TYPE_DISCOVERY) && (logout_req_pkt->reason_code != ISCSI_LOGOUT_REQ_REASON_CODE_CLOSE_SESSION) ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + + iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn ); + + if ( response_pdu == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_pdu_header_handle_logout_req: Out of memory while allocating iSCSI logout response PDU" ); + + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + } + + iscsi_logout_response_packet *logout_response_pkt = (iscsi_logout_response_packet *) response_pdu->bhs_pkt; + + if ( conn->header_digest != 0 ) { + logout_response_pkt = (iscsi_logout_response_packet *) iscsi_append_header_digest_packet( response_pdu->bhs_pkt, conn->header_digest ); + + if ( logout_response_pkt == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_pdu_header_handle_logout_req: Out of memory while allocating iSCSI logout packet data" ); + + iscsi_connection_pdu_destroy( response_pdu ); + + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + } + + response_pdu->bhs_pkt = (iscsi_bhs_packet *) logout_response_pkt; + response_pdu->header_digest = (iscsi_header_digest *) (((iscsi_bhs_packet *) logout_response_pkt) + 1); + response_pdu->header_digest_size = conn->header_digest; + } + + logout_response_pkt->opcode = ISCSI_SERVER_LOGOUT_RES; + logout_response_pkt->flags = -0x80; + + const uint16_t cid = iscsi_get_be16(logout_req_pkt->cid); + + if ( cid == conn->cid ) { + conn->flags |= ISCSI_CONNECT_FLAGS_LOGGED_OUT; + + logout_response_pkt->response = ISCSI_LOGOUT_RESPONSE_CLOSED_SUCCESSFULLY; + } else { + logout_response_pkt->response = ISCSI_LOGOUT_RESPONSE_CID_NOT_FOUND; + } + + logout_response_pkt->init_task_tag = logout_req_pkt->init_task_tag; // Copying over doesn't change endianess. + iscsi_put_be32( (uint8_t *) &logout_response_pkt->stat_sn, conn->stat_sn++ ); + + if ( conn->session != NULL ) { + if ( iscsi_hashmap_size( conn->session->connections ) == 1 ) + conn->session->max_cmd_sn++; + + iscsi_put_be32( (uint8_t *) &logout_response_pkt->exp_cmd_sn, conn->session->exp_cmd_sn ); + iscsi_put_be32( (uint8_t *) &logout_response_pkt->max_cmd_sn, conn->session->max_cmd_sn ); + } else { + iscsi_put_be32( (uint8_t *) &logout_response_pkt->exp_cmd_sn, pdu->cmd_sn ); + iscsi_put_be32( (uint8_t *) &logout_response_pkt->max_cmd_sn, pdu->cmd_sn ); + } + + logout_response_pkt->time_wait = 0U; + logout_response_pkt->time_retain = 0U; + + iscsi_connection_pdu_write( conn, response_pdu, NULL, NULL ); + + return ISCSI_CONNECT_PDU_READ_OK; } /** @@ -6027,14 +6096,14 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn) #define ISCSI_PDU_HANDLE_COUNT 16 /** - * @brief Handle incoming PDU data, read up to 16 fragments at once. + * @brief Handles incoming PDU data, read up to 16 fragments at once. * * Until iSCSI processing has been stopped or a * complete iSCSI packet has been read, this * function will read, parse and process * incoming iSCSI protocol data. * - * @param[in] iSCSI connection to handle. + * @param[in] conn iSCSI connection to handle. * @return Number of proccessed fragments or return * code of iscsi_connection_pdu_read in case of a * fatal error. @@ -6044,7 +6113,7 @@ int iscsi_connection_pdu_handle(iscsi_connection *conn) uint i; for ( i = 0; i < ISCSI_PDU_HANDLE_COUNT; i++ ) { - int rc = iscsi_connection_pdu_read(conn); + int rc = iscsi_connection_pdu_read( conn ); if ( rc == 0 ) break; @@ -6057,3 +6126,91 @@ int iscsi_connection_pdu_handle(iscsi_connection *conn) return i; } + +/** + * @brief Handles an iSCSI connection until connection is closed. + * + * This function creates an iSCSI portal group + * and iSCSI portal with connection data + * delivered from the DNBD3 client and + * request data. + * + * @param[in] client Pointer to DNBD3 client structure, + * may NOT be NULL, so be careful. + * @param[in] request Pointer to DNBD3 request packet data. + * NULL is not allowed here, take caution. + * @param[in] len Length of already read DNBD3 request data. + */ +void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *request, const int len) +{ + _Static_assert( sizeof(dnbd3_request_t) <= sizeof(struct iscsi_bhs_packet) ); + sock_setTimeout( client->sock, 1000L * 3600L ); // TODO: Remove after finishing iSCSI implementation + + host_to_string( &client->host, client->hostName, HOSTNAMELEN ); + const uint8_t *port = memchr( client->hostName, ':', HOSTNAMELEN ); + + if ( port != NULL ) + port++; + + iscsi_portal *portal = iscsi_portal_create( (uint8_t *) client->hostName, port ); + + if ( portal == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI portal" ); + + return; + } + + iscsi_connection *conn = iscsi_connection_create( portal, client->sock ); + + if ( conn == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI connection" ); + + iscsi_portal_destroy( portal ); + + return; + } + + const int id = iscsi_hashmap_size( iscsi_globvec->connections ) + 1; + + conn->id = id; + + uint8_t *hash_key = iscsi_hashmap_key_create( (uint8_t *) &id, sizeof(id) ); + + if ( hash_key == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI connection" ); + + iscsi_connection_destroy( conn ); + iscsi_portal_destroy( portal ); + + return; + } + + conn->pdu_processing = iscsi_connection_pdu_create( conn ); + + if ( conn->pdu_processing == NULL ) { + iscsi_hashmap_key_destroy( hash_key ); + iscsi_connection_destroy( conn ); + iscsi_portal_destroy( portal ); + + return; + } + + memcpy( conn->pdu_processing->bhs_pkt, request, len ); + + conn->pdu_processing->bhs_read_len = len; + conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_HDR; + + int rc = iscsi_hashmap_put( iscsi_globvec->connections, hash_key, sizeof(id), (uint8_t *) conn ); + + if ( rc < 0 ) { + iscsi_connection_pdu_destroy( conn->pdu_processing ); + iscsi_hashmap_key_destroy( hash_key ); + iscsi_connection_destroy( conn ); + iscsi_portal_destroy( portal ); + + return; + } + + while ( iscsi_connection_pdu_handle( conn ) >= ISCSI_CONNECT_PDU_READ_OK ) { + } +} diff --git a/src/server/iscsi.h b/src/server/iscsi.h index 5990d69..c802d05 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -5880,8 +5880,8 @@ typedef struct iscsi_globals { } iscsi_globals; -/// iSCSI global vector. MUST be initialized with iscsi_create before any iSCSI functions are used. -iscsi_globals *iscsi_globvec = NULL; +/// Reference to iSCSI global vector. MUST be initialized with iscsi_create before any iSCSI functions are used. +extern iscsi_globals *iscsi_globvec; int iscsi_create(); // Allocates and initializes the iSCSI global vector structure @@ -6552,4 +6552,7 @@ 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); +int iscsi_connection_pdu_handle(iscsi_connection *conn); // Handles incoming PDU data, read up to 16 fragments at once +void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *request, const int len); // Handles an iSCSI connection until connection is closed + #endif /* DNBD3_ISCSI_H_ */ diff --git a/src/server/net.c b/src/server/net.c index 629c491..af31f4a 100644 --- a/src/server/net.c +++ b/src/server/net.c @@ -20,6 +20,7 @@ #include "helper.h" #include "image.h" +#include "iscsi.h" #include "uplink.h" #include "locks.h" #include "rpc.h" @@ -178,6 +179,7 @@ void* net_handleNewConnection(void *clientPtr) // Close enough... rpc_sendStatsJson( client->sock, &client->host, &request, ret ); } else { + iscsi_connection_handle( client, &request, ret ); logadd( LOG_DEBUG1, "Magic in client handshake incorrect" ); } goto fail_preadd; diff --git a/src/server/server.c b/src/server/server.c index d086930..bfda12d 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -27,6 +27,7 @@ #include "net.h" #include "altservers.h" #include "integrity.h" +#include "iscsi.h" #include "threadpool.h" #include "rpc.h" #include "fuse.h" @@ -176,6 +177,9 @@ _Noreturn static void dnbd3_cleanup() threadpool_waitEmpty(); + // Destroy iSCSI global vector + iscsi_destroy(); + // Clean up images retries = 5; while ( !image_tryFreeAll() && --retries > 0 ) { @@ -365,6 +369,10 @@ int main(int argc, char *argv[]) altservers_init(); integrity_init(); net_init(); + + if ( iscsi_create() < 0 ) + return EXIT_FAILURE; + uplink_globalsInit(); rpc_init(); if ( mountDir != NULL && !dfuse_init( "-oallow_other", mountDir ) ) { -- cgit v1.2.3-55-g7522