From 8e555d9a232286f19b4aeb934732dcd305de005b Mon Sep 17 00:00:00 2001 From: Sebastian Vater Date: Mon, 20 Oct 2025 13:51:47 +0200 Subject: Implemented iSCSI discovery and related portal group and portals handling. Also fixed some bugs and iSCSI emulation layer now handles any block size. Finally, more multipath related stuff. --- README.md | 66 ++- pkg/config/iscsi.conf | 66 ++- pkg/config/server.conf | 34 +- src/server/iscsi.c | 1437 +++++++++++++++++++++++++++++++++--------------- src/server/iscsi.h | 226 +++++--- 5 files changed, 1272 insertions(+), 557 deletions(-) diff --git a/README.md b/README.md index 15b0d78..f4e8efc 100644 --- a/README.md +++ b/README.md @@ -490,11 +490,29 @@ The _iscsi.conf_ file is the main configuration file for the iSCSI implementatio ; scsi-device- sections will be processed in order, always inherit from the [iscsi] and [scsi] ; sections and are matched case sensitive +[iscsi-portal-group-1] +; Portal group access level (warning) specifying invalid access levels will default to private +; access!). Default is Private +; Private All target nodes in this portal group will not be sent in discovery requests +; Public All target nodes in this portal group are visible in discovery requests +Access=Private + +[iscsi-portal-group-2] +; Portal group access level (warning) specifying invalid access levels will default to private +; access!). Default is Private +; Private All target nodes in this portal group will not be sent in discovery requests +; Public All target nodes in this portal group are visible in discovery requests +Access=Public + [iscsi] -; Target name check level (warning) specifying invalid check levels will default to full +; Default portal group tag (warning) specifying invalid portal group tags will default to first +; portal group!). Default is 1 +PortalGroupTag=1 + +; Target name check level (warning) specifying invalid check levels will default to relaxed ; validation!). Default is Relaxed ; Full Target name is checked for full standaard compliance (recommended) -; Relaxed All invalid characters according to standard will be allowed if not an IQN, NAA or EUI. +; Relaxed All invalid characters according to standard will be allowed if not an IQN, NAA or EUI ; None No checking at all (be careful) TargetNameCheck=Relaxed @@ -525,14 +543,14 @@ MaxRecvDataSegmentLength=65536 ; MediaChanger Media changer device DeviceType=Direct -; Physical block size of emulated device, rounded up to nearest power of two). Default is 512 for -; supporting ancient systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed -; physical block sizes range from 256 to 32768 bytes (32 KiB). Default is 512 bytes +; Physical block size of emulated device). Default is 512 for supporting ancient systems. +; Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed physical block sizes range +; from 1 to 2147483647 bytes (2 GiB). Default is 512 bytes PhysicalBlockSize=512 -; Logical block size of emulated device, rounded up to nearest power of two). Default is 512 for -; supporting ancient systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed -; logical block sizes range from 256 to 32768 bytes (32 KiB). Default is 512 bytes +; Logical block size of emulated device). Default is 512 for supporting ancient systems. +; Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed logical block sizes range +; from 1 to 2147483647 bytes (2 GiB). Default is 512 bytes LogicalBlockSize=512 ; Device is emulated as removable. Should be enabled for CD-ROM, DVD and BluRay and disabled for @@ -545,14 +563,18 @@ Removable=true ; end with .iso extension. Block sizes are set to 2 KiB for CD-ROM, DVD and BluRay type devices ; and the physical read only emulation flag is enabled [scsi-device-*.iso] -; Physical block size of emulated device, rounded up to nearest power of two). Default is 512 for -; supporting ancient systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed -; physical block sizes range from 256 to 32768 bytes (32 KiB). Default is 512 bytes +; Default portal group tag (warning) specifying invalid portal group tags will default to first +; portal group!). Default is 1 +PortalGroupTag=2 + +; Physical block size of emulated device). Default is 512 for supporting ancient systems. +; Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed physical block sizes range +; from 1 to 2147483647 bytes (2 GiB). Default is 512 bytes PhysicalBlockSize=2048 -; Logical block size of emulated device, rounded up to nearest power of two). Default is 512 for -; supporting ancient systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed -; logical block sizes range from 256 to 32768 bytes (32 KiB). Default is 512 bytes +; Logical block size of emulated device). Default is 512 for supporting ancient systems. +; Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed logical block sizes range +; from 1 to 2147483647 bytes (2 GiB). Default is 512 bytes LogicalBlockSize=2048 ; Device is emulated as removable. Should be enabled for CD-ROM, DVD and BluRay and disabled for @@ -567,14 +589,18 @@ PhysicalReadOnly=true ; end with .ISO extension. Block sizes are set to 2 KiB for CD-ROM, DVD and BluRay type devices ; and the physical read only emulation flag is enabled [scsi-device-*.ISO] -; Physical block size of emulated device, rounded up to nearest power of two). Default is 512 for -; supporting ancient systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed -; physical block sizes range from 256 to 32768 bytes (32 KiB). Default is 512 bytes +; Default portal group tag (warning) specifying invalid portal group tags will default to first +; portal group!). Default is 1 +PortalGroupTag=2 + +; Physical block size of emulated device). Default is 512 for supporting ancient systems. +; Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed physical block sizes range +; from 1 to 2147483647 bytes (2 GiB). Default is 512 bytes PhysicalBlockSize=2048 -; Logical block size of emulated device, rounded up to nearest power of two). Default is 512 for -; supporting ancient systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed -; logical block sizes range from 256 to 32768 bytes (32 KiB). Default is 512 bytes +; Logical block size of emulated device). Default is 512 for supporting ancient systems. +; Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed logical block sizes range +; from 1 to 2147483647 bytes (2 GiB). Default is 512 bytes LogicalBlockSize=2048 ; Device is emulated as removable. Should be enabled for CD-ROM, DVD and BluRay and disabled for diff --git a/pkg/config/iscsi.conf b/pkg/config/iscsi.conf index 0e1ca49..85c31a1 100644 --- a/pkg/config/iscsi.conf +++ b/pkg/config/iscsi.conf @@ -8,11 +8,29 @@ ; scsi-device- sections will be processed in order, always inherit from the [iscsi] and [scsi] ; sections and are matched case sensitive +[iscsi-portal-group-1] +; Portal group access level (warning) specifying invalid access levels will default to private +; access!). Default is Private +; Private All target nodes in this portal group will not be sent in discovery requests +; Public All target nodes in this portal group are visible in discovery requests +Access=Private + +[iscsi-portal-group-2] +; Portal group access level (warning) specifying invalid access levels will default to private +; access!). Default is Private +; Private All target nodes in this portal group will not be sent in discovery requests +; Public All target nodes in this portal group are visible in discovery requests +Access=Public + [iscsi] -; Target name check level (warning) specifying invalid check levels will default to full +; Default portal group tag (warning) specifying invalid portal group tags will default to first +; portal group!). Default is 1 +PortalGroupTag=1 + +; Target name check level (warning) specifying invalid check levels will default to relaxed ; validation!). Default is Relaxed ; Full Target name is checked for full standaard compliance (recommended) -; Relaxed All invalid characters according to standard will be allowed if not an IQN, NAA or EUI. +; Relaxed All invalid characters according to standard will be allowed if not an IQN, NAA or EUI ; None No checking at all (be careful) TargetNameCheck=Relaxed @@ -170,14 +188,14 @@ ErrorRecoveryLevel=0 ; MediaChanger Media changer device DeviceType=Direct -; Physical block size of emulated device, rounded up to nearest power of two). Default is 512 for -; supporting ancient systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed -; physical block sizes range from 256 to 32768 bytes (32 KiB). Default is 512 bytes +; Physical block size of emulated device). Default is 512 for supporting ancient systems. +; Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed physical block sizes range +; from 1 to 2147483647 bytes (2 GiB). Default is 512 bytes PhysicalBlockSize=512 -; Logical block size of emulated device, rounded up to nearest power of two). Default is 512 for -; supporting ancient systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed -; logical block sizes range from 256 to 32768 bytes (32 KiB). Default is 512 bytes +; Logical block size of emulated device). Default is 512 for supporting ancient systems. +; Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed logical block sizes range +; from 1 to 2147483647 bytes (2 GiB). Default is 512 bytes LogicalBlockSize=512 ; Device is emulated as removable. Should be enabled for CD-ROM, DVD and BluRay and disabled for @@ -208,14 +226,18 @@ WriteCache=false ; end with .iso extension. Block sizes are set to 2 KiB for CD-ROM, DVD and BluRay type devices ; and the physical read only emulation flag is enabled [scsi-device-*.iso] -; Physical block size of emulated device, rounded up to nearest power of two). Default is 512 for -; supporting ancient systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed -; physical block sizes range from 256 to 32768 bytes (32 KiB). Default is 512 bytes +; Default portal group tag (warning) specifying invalid portal group tags will default to first +; portal group!). Default is 1 +PortalGroupTag=2 + +; Physical block size of emulated device). Default is 512 for supporting ancient systems. +; Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed physical block sizes range +; from 1 to 2147483647 bytes (2 GiB). Default is 512 bytes PhysicalBlockSize=2048 -; Logical block size of emulated device, rounded up to nearest power of two). Default is 512 for -; supporting ancient systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed -; logical block sizes range from 256 to 32768 bytes (32 KiB). Default is 512 bytes +; Logical block size of emulated device). Default is 512 for supporting ancient systems. +; Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed logical block sizes range +; from 1 to 2147483647 bytes (2 GiB). Default is 512 bytes LogicalBlockSize=2048 ; Device is emulated as removable. Should be enabled for CD-ROM, DVD and BluRay and disabled for @@ -230,14 +252,18 @@ PhysicalReadOnly=true ; end with .ISO extension. Block sizes are set to 2 KiB for CD-ROM, DVD and BluRay type devices ; and the physical read only emulation flag is enabled [scsi-device-*.ISO] -; Physical block size of emulated device, rounded up to nearest power of two). Default is 512 for -; supporting ancient systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed -; physical block sizes range from 256 to 32768 bytes (32 KiB). Default is 512 bytes +; Default portal group tag (warning) specifying invalid portal group tags will default to first +; portal group!). Default is 1 +PortalGroupTag=2 + +; Physical block size of emulated device). Default is 512 for supporting ancient systems. +; Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed physical block sizes range +; from 1 to 2147483647 bytes (2 GiB). Default is 512 bytes PhysicalBlockSize=2048 -; Logical block size of emulated device, rounded up to nearest power of two). Default is 512 for -; supporting ancient systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed -; logical block sizes range from 256 to 32768 bytes (32 KiB). Default is 512 bytes +; Logical block size of emulated device). Default is 512 for supporting ancient systems. +; Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed logical block sizes range +; from 1 to 2147483647 bytes (2 GiB). Default is 512 bytes LogicalBlockSize=2048 ; Device is emulated as removable. Should be enabled for CD-ROM, DVD and BluRay and disabled for diff --git a/pkg/config/server.conf b/pkg/config/server.conf index 3a7cd04..443a76e 100644 --- a/pkg/config/server.conf +++ b/pkg/config/server.conf @@ -93,11 +93,29 @@ consoleMask=ERROR WARNING MINOR INFO ; Whether timestamps should be output to console too (or just to file if false) consoleTimestamps=false +[iscsi-portal-group-1] +; Portal group access level (warning) specifying invalid access levels will default to private +; access!). Default is Private +; Private All target nodes in this portal group will not be sent in discovery requests +; Public All target nodes in this portal group are visible in discovery requests +Access=Private + +[iscsi-portal-group-2] +; Portal group access level (warning) specifying invalid access levels will default to private +; access!). Default is Private +; Private All target nodes in this portal group will not be sent in discovery requests +; Public All target nodes in this portal group are visible in discovery requests +Access=Public + [iscsi] -; Target name check level (warning) specifying invalid check levels will default to full +; Default portal group tag (warning) specifying invalid portal group tags will default to first +; portal group!). Default is 1 +PortalGroupTag=1 + +; Target name check level (warning) specifying invalid check levels will default to relaxed ; validation!). Default is Relaxed ; Full Target name is checked for full standaard compliance (recommended) -; Relaxed All invalid characters according to standard will be allowed if not an IQN, NAA or EUI. +; Relaxed All invalid characters according to standard will be allowed if not an IQN, NAA or EUI ; None No checking at all (be careful) TargetNameCheck=Relaxed @@ -255,14 +273,14 @@ ErrorRecoveryLevel=0 ; MediaChanger Media changer device DeviceType=Direct -; Physical block size of emulated device, rounded up to nearest power of two). Default is 512 for -; supporting ancient systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed -; physical block sizes range from 256 to 32768 bytes (32 KiB). Default is 512 bytes +; Physical block size of emulated device). Default is 512 for supporting ancient systems. +; Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed physical block sizes range +; from 1 to 2147483647 bytes (2 GiB). Default is 512 bytes PhysicalBlockSize=512 -; Logical block size of emulated device, rounded up to nearest power of two). Default is 512 for -; supporting ancient systems. Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed -; logical block sizes range from 256 to 32768 bytes (32 KiB). Default is 512 bytes +; Logical block size of emulated device). Default is 512 for supporting ancient systems. +; Should be set to 2048 for CD-ROM, DVD and BluRay devices. Allowed logical block sizes range +; from 1 to 2147483647 bytes (2 GiB). Default is 512 bytes LogicalBlockSize=512 ; Device is emulated as removable. Should be enabled for CD-ROM, DVD and BluRay and disabled for diff --git a/src/server/iscsi.c b/src/server/iscsi.c index c4a124c..b56df20 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -30,9 +30,13 @@ #include #include #include +#include +#include #include #include +#include #include +#include #include #include #include @@ -40,6 +44,7 @@ #include #include +#include "altservers.h" #include "fileutil.h" #include "globals.h" #include "helper.h" @@ -1075,7 +1080,7 @@ int iscsi_create() return -1; } - globvec->devices = iscsi_hashmap_create( _maxImages ); + globvec->devices = iscsi_hashmap_create( 0U ); if ( globvec->devices == NULL ) { logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector devices hash map" ); @@ -1096,7 +1101,7 @@ int iscsi_create() return -1; } - globvec->portal_groups = iscsi_hashmap_create( 1U ); + globvec->portal_groups = iscsi_hashmap_create( 0U ); if ( globvec->portal_groups == NULL ) { logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector portal groups hash map" ); @@ -1121,7 +1126,7 @@ int iscsi_create() return -1; } - globvec->target_nodes = iscsi_hashmap_create( _maxImages ); + globvec->target_nodes = iscsi_hashmap_create( 0U ); if ( globvec->target_nodes == NULL ) { logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector target nodes hash map" ); @@ -1150,7 +1155,7 @@ int iscsi_create() return -1; } - globvec->sessions = iscsi_hashmap_create( _maxClients ); + globvec->sessions = iscsi_hashmap_create( 0U ); if ( globvec->sessions == NULL ) { logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector sessions hash map" ); @@ -1312,25 +1317,25 @@ int iscsi_create() return -1; } - globvec->flags = (ISCSI_GLOBALS_FLAGS_CHAP_DISABLE | ISCSI_GLOBALS_FLAGS_INIT_R2T | ISCSI_GLOBALS_FLAGS_IMMEDIATE_DATA | ISCSI_GLOBALS_FLAGS_DATA_PDU_IN_ORDER | ISCSI_GLOBALS_FLAGS_DATA_SEQ_IN_ORDER | ISCSI_GLOBALS_FLAGS_SCSI_IO_REMOVABLE | ISCSI_GLOBALS_FLAGS_SCSI_IO_WRITE_PROTECT); - globvec->target_name_check = ISCSI_GLOBALS_TARGET_NAME_CHECK_RELAXED; - globvec->max_sessions = 0U; - globvec->header_digest = 0; - globvec->data_digest = 0; - globvec->scsi_device_type = ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_DIRECT; - globvec->max_recv_ds_len = ISCSI_DEFAULT_MAX_RECV_DS_LEN; - globvec->max_session_conns = ISCSI_GLOBALS_DEFAULT_MAX_CONNECTIONS; - globvec->max_outstanding_r2t = ISCSI_GLOBALS_DEFAULT_MAX_OUTSTANDING_R2T; - globvec->default_time_to_wait = ISCSI_GLOBALS_DEFAULT_TIME_TO_WAIT; - globvec->default_time_to_retain = ISCSI_GLOBALS_DEFAULT_TIME_TO_RETAIN; - globvec->first_burst_len = ISCSI_GLOBALS_DEFAULT_FIRST_BURST_LEN; - globvec->max_burst_len = ISCSI_GLOBALS_DEFAULT_MAX_BURST_LEN; - globvec->err_recovery_level = ISCSI_GLOBALS_DEFAULT_ERR_RECOVERY_LEVEL; - globvec->chap_group = 0L; - globvec->scsi_physical_block_size = ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE; - globvec->scsi_physical_block_size_shift = iscsi_get_log2_of_pow2( globvec->scsi_physical_block_size ); - globvec->scsi_logical_block_size = ISCSI_SCSI_EMU_BLOCK_SIZE; - globvec->scsi_logical_block_size_shift = iscsi_get_log2_of_pow2( globvec->scsi_logical_block_size ); + globvec->flags = (ISCSI_GLOBALS_FLAGS_CHAP_DISABLE | ISCSI_GLOBALS_FLAGS_INIT_R2T | ISCSI_GLOBALS_FLAGS_IMMEDIATE_DATA | ISCSI_GLOBALS_FLAGS_DATA_PDU_IN_ORDER | ISCSI_GLOBALS_FLAGS_DATA_SEQ_IN_ORDER | ISCSI_GLOBALS_FLAGS_SCSI_IO_REMOVABLE | ISCSI_GLOBALS_FLAGS_SCSI_IO_WRITE_PROTECT); + globvec->target_name_check = ISCSI_GLOBALS_TARGET_NAME_CHECK_RELAXED; + globvec->max_sessions = 0U; + globvec->header_digest = 0; + globvec->data_digest = 0; + globvec->scsi_device_type = ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_DIRECT; + atomic_init( &globvec->connections, 0 ); + globvec->pg_tag = 1UL; + globvec->max_recv_ds_len = ISCSI_DEFAULT_MAX_RECV_DS_LEN; + globvec->max_session_conns = ISCSI_GLOBALS_DEFAULT_MAX_CONNECTIONS; + globvec->max_outstanding_r2t = ISCSI_GLOBALS_DEFAULT_MAX_OUTSTANDING_R2T; + globvec->default_time_to_wait = ISCSI_GLOBALS_DEFAULT_TIME_TO_WAIT; + globvec->default_time_to_retain = ISCSI_GLOBALS_DEFAULT_TIME_TO_RETAIN; + globvec->first_burst_len = ISCSI_GLOBALS_DEFAULT_FIRST_BURST_LEN; + globvec->max_burst_len = ISCSI_GLOBALS_DEFAULT_MAX_BURST_LEN; + globvec->err_recovery_level = ISCSI_GLOBALS_DEFAULT_ERR_RECOVERY_LEVEL; + globvec->chap_group = 0L; + globvec->scsi_physical_block_size = ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE; + globvec->scsi_logical_block_size = ISCSI_SCSI_EMU_BLOCK_SIZE; iscsi_config_load( globvec ); @@ -1553,7 +1558,56 @@ static int iscsi_config_load_from_ini(void *user_data, const char *section, cons { iscsi_globals *globvec = (iscsi_globals *) user_data; - if ( strcasecmp( section, ISCSI_GLOBALS_SECTION_ISCSI ) == 0 ) { + if ( (strncasecmp( section, ISCSI_GLOBALS_SECTION_ISCSI_PORTAL_GROUP_PREFIX, ISCSI_STRLEN(ISCSI_GLOBALS_SECTION_ISCSI_PORTAL_GROUP_PREFIX) ) == 0) ) { + int32_t num_value = iscsi_config_parse_int( (((uint8_t *) section) + ISCSI_STRLEN(ISCSI_GLOBALS_SECTION_ISCSI_PORTAL_GROUP_PREFIX)) ); + + if ( num_value <= 0L ) + num_value = 1L; + + uint64_t pg_tag = (uint64_t) num_value; + int flags = ISCSI_PORTAL_GROUP_FLAGS_PRIVATE; + + pthread_rwlock_rdlock( &globvec->portal_groups_rwlock ); + + iscsi_portal_group *portal_group = NULL; + int rc = iscsi_hashmap_get( globvec->portal_groups, (uint8_t *) &pg_tag, sizeof(pg_tag), (uint8_t **) &portal_group ); + + if ( portal_group == NULL ) { + pthread_rwlock_unlock( &globvec->portal_groups_rwlock ); + pthread_rwlock_wrlock( &globvec->portal_groups_rwlock ); + rc = iscsi_hashmap_get( globvec->portal_groups, (uint8_t *) &pg_tag, sizeof(pg_tag), (uint8_t **) &portal_group ); + + if ( portal_group == NULL ) { + portal_group = iscsi_portal_group_create( pg_tag, flags ); + + if ( portal_group == NULL ) { + pthread_rwlock_unlock( &globvec->portal_groups_rwlock ); + + return 0; + } + + rc = iscsi_hashmap_put( globvec->portal_groups, (uint8_t *) &portal_group->tag, sizeof(portal_group->tag), (uint8_t *) portal_group ); + + if ( rc < 0 ) { + iscsi_portal_group_destroy( portal_group ); + pthread_rwlock_unlock( &globvec->portal_groups_rwlock ); + + return 0; + } + } + } else { + portal_group->tag = pg_tag; + } + + if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_ISCSI_PORTAL_GROUP_KEY_ACCESS ) == 0 ) { + if ( strcasecmp( value, "Public" ) == 0 ) + flags &= ~ISCSI_PORTAL_GROUP_FLAGS_PRIVATE; + + portal_group->flags = flags; + } + + pthread_rwlock_unlock( &globvec->portal_groups_rwlock ); + } else if ( strcasecmp( section, ISCSI_GLOBALS_SECTION_ISCSI ) == 0 ) { const int32_t num_value = iscsi_config_parse_int( (uint8_t *) value ); if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_ISCSI_KEY_TARGET_NAME_CHECK ) == 0 ) { @@ -1569,6 +1623,9 @@ static int iscsi_config_load_from_ini(void *user_data, const char *section, cons globvec->header_digest = ((strcasecmp( value, "CRC32C" ) == 0) ? ISCSI_DIGEST_SIZE : 0); } else if ( strcasecmp( key, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST ) == 0 ) { globvec->data_digest = ((strcasecmp( value, "CRC32C" ) == 0) ? ISCSI_DIGEST_SIZE : 0); + } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_ISCSI_KEY_PORTAL_GROUP_TAG ) == 0 ) { + if ( num_value > 0L ) + globvec->pg_tag = num_value; } else if ( strcasecmp( key, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_RECV_DS_LEN ) == 0 ) { if ( (num_value >= 512L) && (num_value <= (int32_t) ISCSI_MAX_DS_SIZE) ) globvec->max_recv_ds_len = num_value; @@ -1639,19 +1696,11 @@ static int iscsi_config_load_from_ini(void *user_data, const char *section, cons else globvec->scsi_device_type = ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_DIRECT; } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_PHYSICAL_BLOCK_SIZE ) == 0 ) { - num_value = iscsi_align_pow2_ceil( num_value ); - - if ( (num_value >= 256L) && (num_value <= 32768L) ) { - globvec->scsi_physical_block_size = num_value; - globvec->scsi_physical_block_size_shift = iscsi_get_log2_of_pow2( num_value ); - } + if ( num_value > 0L ) + globvec->scsi_physical_block_size = num_value; } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_LOGICAL_BLOCK_SIZE ) == 0 ) { - num_value = iscsi_align_pow2_ceil( num_value ); - - if ( (num_value >= 256L) && (num_value <= 32768L) ) { - globvec->scsi_logical_block_size = num_value; - globvec->scsi_logical_block_size_shift = iscsi_get_log2_of_pow2( num_value ); - } + if ( num_value > 0L ) + globvec->scsi_logical_block_size = num_value; } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_REMOVABLE ) == 0 ) { if ( num_value != ISCSI_GLOBALS_CONFIG_TYPE_VALUE_ERROR ) { if ( num_value != 0L ) @@ -1756,21 +1805,20 @@ static int iscsi_config_load_from_ini(void *user_data, const char *section, cons if ( (globvec->flags & ISCSI_GLOBALS_FLAGS_SCSI_IO_WRITE_CACHE) != 0 ) scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_WRITE_CACHE; - scsi_device_config->header_digest = globvec->header_digest; - scsi_device_config->data_digest = globvec->data_digest; - scsi_device_config->scsi_device_type = globvec->scsi_device_type; - scsi_device_config->max_recv_ds_len = globvec->max_recv_ds_len; - scsi_device_config->max_session_conns = globvec->max_session_conns; - scsi_device_config->max_outstanding_r2t = globvec->max_outstanding_r2t; - scsi_device_config->default_time_to_wait = globvec->default_time_to_wait; - scsi_device_config->default_time_to_retain = globvec->default_time_to_retain; - scsi_device_config->first_burst_len = globvec->first_burst_len; - scsi_device_config->max_burst_len = globvec->max_burst_len; - scsi_device_config->err_recovery_level = globvec->err_recovery_level; - scsi_device_config->scsi_physical_block_size = globvec->scsi_physical_block_size; - scsi_device_config->scsi_physical_block_size_shift = globvec->scsi_physical_block_size_shift; - scsi_device_config->scsi_logical_block_size = globvec->scsi_logical_block_size; - scsi_device_config->scsi_logical_block_size_shift = globvec->scsi_logical_block_size_shift; + scsi_device_config->header_digest = globvec->header_digest; + scsi_device_config->data_digest = globvec->data_digest; + scsi_device_config->scsi_device_type = globvec->scsi_device_type; + scsi_device_config->pg_tag = globvec->pg_tag; + scsi_device_config->max_recv_ds_len = globvec->max_recv_ds_len; + scsi_device_config->max_session_conns = globvec->max_session_conns; + scsi_device_config->max_outstanding_r2t = globvec->max_outstanding_r2t; + scsi_device_config->default_time_to_wait = globvec->default_time_to_wait; + scsi_device_config->default_time_to_retain = globvec->default_time_to_retain; + scsi_device_config->first_burst_len = globvec->first_burst_len; + scsi_device_config->max_burst_len = globvec->max_burst_len; + scsi_device_config->err_recovery_level = globvec->err_recovery_level; + scsi_device_config->scsi_physical_block_size = globvec->scsi_physical_block_size; + scsi_device_config->scsi_logical_block_size = globvec->scsi_logical_block_size; rc = iscsi_hashmap_put( globvec->scsi_device_config, hash_key, key_len, (uint8_t *) scsi_device_config ); @@ -1790,6 +1838,9 @@ static int iscsi_config_load_from_ini(void *user_data, const char *section, cons scsi_device_config->header_digest = ((strcasecmp( value, "CRC32C" ) == 0) ? ISCSI_DIGEST_SIZE : 0); } else if ( strcasecmp( key, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST ) == 0 ) { scsi_device_config->data_digest = ((strcasecmp( value, "CRC32C" ) == 0) ? ISCSI_DIGEST_SIZE : 0); + } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_ISCSI_KEY_PORTAL_GROUP_TAG ) == 0 ) { + if ( num_value > 0L ) + scsi_device_config->pg_tag = num_value; } else if ( strcasecmp( key, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_RECV_DS_LEN ) == 0 ) { if ( (num_value >= 512L) && (num_value <= (int32_t) ISCSI_MAX_DS_SIZE) ) scsi_device_config->max_recv_ds_len = num_value; @@ -1856,19 +1907,11 @@ static int iscsi_config_load_from_ini(void *user_data, const char *section, cons else scsi_device_config->scsi_device_type = ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_DIRECT; } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_PHYSICAL_BLOCK_SIZE ) == 0 ) { - num_value = iscsi_align_pow2_ceil( num_value ); - - if ( (num_value >= 256L) && (num_value <= 32768L) ) { - scsi_device_config->scsi_physical_block_size = num_value; - scsi_device_config->scsi_physical_block_size_shift = iscsi_get_log2_of_pow2( num_value ); - } + if ( num_value > 0L ) + scsi_device_config->scsi_physical_block_size = num_value; } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_LOGICAL_BLOCK_SIZE ) == 0 ) { - num_value = iscsi_align_pow2_ceil( num_value ); - - if ( (num_value >= 256L) && (num_value <= 32768L) ) { - scsi_device_config->scsi_logical_block_size = num_value; - scsi_device_config->scsi_logical_block_size_shift = iscsi_get_log2_of_pow2( num_value ); - } + if ( num_value > 0L ) + scsi_device_config->scsi_logical_block_size = num_value; } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_REMOVABLE ) == 0 ) { if ( num_value != ISCSI_GLOBALS_CONFIG_TYPE_VALUE_ERROR ) { if ( num_value != 0L ) @@ -2091,6 +2134,11 @@ int32_t iscsi_config_get(uint8_t *name, const int type) break; } + case ISCSI_GLOBALS_CONFIG_TYPE_PORTAL_GROUP_TAG : { + return scsi_device_config->pg_tag; + + break; + } case ISCSI_GLOBALS_CONFIG_TYPE_MAX_RECV_DS_LEN : { return scsi_device_config->max_recv_ds_len; @@ -2131,18 +2179,13 @@ int32_t iscsi_config_get(uint8_t *name, const int type) break; } - case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE : { - return scsi_device_config->scsi_physical_block_size; - - break; - } case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_DEVICE_TYPE : { return scsi_device_config->scsi_device_type; break; } - case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE_SHIFT : { - return scsi_device_config->scsi_physical_block_size_shift; + case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE : { + return scsi_device_config->scsi_physical_block_size; break; } @@ -2151,11 +2194,6 @@ int32_t iscsi_config_get(uint8_t *name, const int type) break; } - case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE_SHIFT : { - return scsi_device_config->scsi_logical_block_size_shift; - - break; - } case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_INIT_R2T : { return (((scsi_device_config->flags & ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_INIT_R2T) != 0) ? 1L : 0L); @@ -2224,6 +2262,11 @@ int32_t iscsi_config_get(uint8_t *name, const int type) break; } + case ISCSI_GLOBALS_CONFIG_TYPE_PORTAL_GROUP_TAG : { + return iscsi_globvec->pg_tag; + + break; + } case ISCSI_GLOBALS_CONFIG_TYPE_MAX_RECV_DS_LEN : { return iscsi_globvec->max_recv_ds_len; @@ -2279,21 +2322,11 @@ int32_t iscsi_config_get(uint8_t *name, const int type) break; } - case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE_SHIFT : { - return iscsi_globvec->scsi_physical_block_size_shift; - - break; - } case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE : { return iscsi_globvec->scsi_logical_block_size; break; } - case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE_SHIFT : { - return iscsi_globvec->scsi_logical_block_size_shift; - - break; - } case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_CHAP_DISABLE : { return (((iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_CHAP_DISABLE) != 0) ? 1L : 0L); @@ -2699,7 +2732,7 @@ static int iscsi_get_int_key_value_pair(iscsi_hashmap *key_value_pairs, const ui */ static int iscsi_add_int_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int32_t value) { - const uint8_t *hash_val = iscsi_sprintf_alloc( "%" PRId32, value ); + uint8_t *hash_val = iscsi_sprintf_alloc( "%" PRId32, value ); if ( hash_val == NULL ) { logadd( LOG_ERROR, "iscsi_add_int_key_value_pair: Out of memory allocating integer value." ); @@ -2707,7 +2740,11 @@ static int iscsi_add_int_key_value_pair(iscsi_hashmap *key_value_pairs, const ui return -1; } - return iscsi_add_key_value_pair( key_value_pairs, key, hash_val ); + const int rc = iscsi_add_key_value_pair( key_value_pairs, key, hash_val ); + + free( hash_val ); + + return rc; } /** @@ -3755,10 +3792,11 @@ void iscsi_portal_group_del_portal(iscsi_portal_group *portal_group, iscsi_porta * * @param[in] host Host / IP address of the portal. * @param[in] port Port of the portal. + * @param[in] type Type of socket. * @return Pointer to iSCSI portal structure or NULL * in case of an error (memory exhausted). */ -iscsi_portal *iscsi_portal_create(const uint8_t *host, const uint8_t *port) +iscsi_portal *iscsi_portal_create(const uint8_t *host, const uint8_t *port, const uint type) { iscsi_portal *portal = (iscsi_portal *) malloc( sizeof(struct iscsi_portal) ); @@ -3794,6 +3832,7 @@ iscsi_portal *iscsi_portal_create(const uint8_t *host, const uint8_t *port) memcpy( portal->port, port, port_len ); + portal->type = type; portal->sock = -1; return portal; @@ -3907,6 +3946,12 @@ void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task, iscsi_scsi_task_xfer_com void iscsi_scsi_task_destroy(iscsi_scsi_task *scsi_task) { if ( (scsi_task != NULL) && (--scsi_task->ref == 0UL) ) { + if ( scsi_task->sense_data != NULL ) { + free( scsi_task->sense_data ); + + scsi_task->sense_data = NULL; + } + if ( scsi_task->buf != NULL ) { if ( (scsi_task->flags & ISCSI_SCSI_TASK_FLAGS_XFER_WRITE) == 0 ) free( scsi_task->buf ); @@ -5056,34 +5101,14 @@ static inline bool iscsi_scsi_emu_io_type_is_supported(const dnbd3_image_t *imag */ static inline uint64_t iscsi_scsi_emu_physical_block_get_count(const dnbd3_image_t *image) { - int32_t block_size_shift = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE_SHIFT ); - - if ( block_size_shift <= 0L ) - block_size_shift = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE_SHIFT ); - - return (((image->realFilesize + ((1ULL << (uint64_t) block_size_shift) - 1ULL)) ) >> (uint32_t) block_size_shift); -} + int32_t block_size = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE ); -/** - * @brief Retrieves the bit shift of a physical block in bytes for a DNBD3 image. - * - * This function depends on DNBD3 image - * properties. - * - * @param[in] image Pointer to DNBD3 image to retrieve - * the physical bit shift size. May NOT - * be NULL, so be careful. - * @return The physical block size in bytes as a - * bit shift count. - */ -static inline uint32_t iscsi_scsi_emu_physical_block_get_size_shift(const dnbd3_image_t *image) -{ - int32_t block_size_shift = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE_SHIFT ); + if ( block_size <= 0L ) + block_size = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE ); - if ( block_size_shift <= 0L ) - block_size_shift = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE_SHIFT ); + const uint64_t block_end = (image->realFilesize + ((uint64_t) block_size - 1ULL)); - return block_size_shift; + return (iscsi_is_pow2( block_size ) ? (block_end >> (uint64_t) iscsi_get_log2_of_pow2( block_size )) : (block_end / (uint64_t) block_size)); } /** @@ -5104,7 +5129,7 @@ static inline uint32_t iscsi_scsi_emu_physical_block_get_size(const dnbd3_image_ if ( block_size <= 0L ) block_size = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE ); - return block_size; + return (uint32_t) block_size; } /** @@ -5120,34 +5145,14 @@ static inline uint32_t iscsi_scsi_emu_physical_block_get_size(const dnbd3_image_ */ static inline uint64_t iscsi_scsi_emu_block_get_count(const dnbd3_image_t *image) { - int32_t block_size_shift = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE_SHIFT ); - - if ( block_size_shift <= 0L ) - block_size_shift = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE_SHIFT ); - - return (((image->realFilesize + ((1ULL << (uint64_t) block_size_shift) - 1ULL)) ) >> (uint32_t) block_size_shift); -} + int32_t block_size = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE ); -/** - * @brief Retrieves the bit shift of a logical block in bytes for a DNBD3 image. - * - * This function depends on DNBD3 image - * properties. - * - * @param[in] image Pointer to DNBD3 image to retrieve - * the logical block bit shift size. - * May NOT be NULL, so be careful. - * @return The logical block size in bytes as a - * bit shift count. - */ -static inline uint32_t iscsi_scsi_emu_block_get_size_shift(const dnbd3_image_t *image) -{ - int32_t block_size_shift = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE_SHIFT ); + if ( block_size <= 0L ) + block_size = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE ); - if ( block_size_shift <= 0L ) - block_size_shift = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE_SHIFT ); + const uint64_t block_end = (image->realFilesize + ((uint64_t) block_size - 1ULL)); - return block_size_shift; + return (iscsi_is_pow2( block_size ) ? (block_end >> (uint64_t) iscsi_get_log2_of_pow2( block_size )) : (block_end / (uint64_t) block_size)); } /** @@ -5168,26 +5173,7 @@ static inline uint32_t iscsi_scsi_emu_block_get_size(const dnbd3_image_t *image) if ( block_size <= 0L ) block_size = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE ); - return block_size; -} - -/** - * @brief Retrieves the bit shift ratio between logical and physical block size for a DNBD3 image. - * - * This function depends on DNBD3 image - * properties. - * - * @param[in] image Pointer to DNBD3 image to retrieve - * the ratio between the logical and - * physical block size. May NOT be - * NULL, so be careful. - * @return The ratio between logical and physical - * block size as a logical bit shift - * count. - */ -static inline uint32_t iscsi_scsi_emu_block_get_ratio_shift(const dnbd3_image_t *image) -{ - return (iscsi_scsi_emu_physical_block_get_size_shift( image ) - iscsi_scsi_emu_block_get_size_shift( image )); + return (uint32_t) block_size; } /** @@ -5205,7 +5191,26 @@ static inline uint32_t iscsi_scsi_emu_block_get_ratio_shift(const dnbd3_image_t */ static inline uint32_t iscsi_scsi_emu_block_get_ratio(const dnbd3_image_t *image) { - return (1UL << iscsi_scsi_emu_block_get_ratio_shift( image )); + int32_t physical_block_size = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE ); + + if ( physical_block_size <= 0L ) + physical_block_size = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE ); + + int32_t logical_block_size = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE ); + + if ( logical_block_size <= 0L ) + logical_block_size = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE ); + + if ( physical_block_size < logical_block_size ) + return 0UL; + + const bool logical_block_size_pow2 = iscsi_is_pow2( logical_block_size ); + uint32_t logical_block_size_shift; + + if ( logical_block_size_pow2 ) + logical_block_size_shift = iscsi_get_log2_of_pow2( logical_block_size ); + + return ((logical_block_size_pow2 ? ((uint32_t) physical_block_size >> logical_block_size_shift) : ((uint32_t) physical_block_size / (uint32_t) logical_block_size))); } /** @@ -5656,7 +5661,7 @@ static int iscsi_scsi_emu_block_read_write(dnbd3_image_t *image, iscsi_scsi_task uint32_t block_size_shift; if ( block_size_pow2 ) - block_size_shift = iscsi_scsi_emu_block_get_size_shift( image ); + block_size_shift = iscsi_get_log2_of_pow2( block_size ); const uint32_t max_xfer_len = (block_size_pow2 ? (ISCSI_SCSI_EMU_MAX_XFER_LEN >> block_size_shift) : (ISCSI_SCSI_EMU_MAX_XFER_LEN / block_size)); @@ -6084,7 +6089,8 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) buf->flags = 0; - const uint8_t exponent = (uint8_t) iscsi_scsi_emu_block_get_ratio_shift( lun->image ); + const uint32_t ratio = iscsi_scsi_emu_block_get_ratio( lun->image ); + const uint8_t exponent = (uint8_t) ((ratio != 0UL) ? iscsi_get_log2_of_pow2( ratio ) : 0UL); buf->exponents = ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_PUT_LBPPB_EXPONENT((exponent <= ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LBPPB_EXPONENT_MASK) ? exponent : 0U); @@ -6635,22 +6641,28 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task vpd_page_block_limits_inquiry_data_pkt->flags = 0; - const uint32_t block_size_shift = iscsi_scsi_emu_block_get_size_shift( image ); - uint32_t blocks = (ISCSI_SCSI_EMU_MAX_XFER_LEN >> block_size_shift); + const uint32_t block_size = iscsi_scsi_emu_block_get_size( image ); + const bool block_size_pow2 = iscsi_is_pow2( block_size ); + uint32_t block_size_shift; + + if ( block_size_pow2 ) + block_size_shift = iscsi_get_log2_of_pow2( block_size ); + + uint32_t blocks = (block_size_pow2 ? (ISCSI_SCSI_EMU_MAX_XFER_LEN >> block_size_shift) : (ISCSI_SCSI_EMU_MAX_XFER_LEN / block_size)); if ( blocks > 255UL ) blocks = 255UL; vpd_page_block_limits_inquiry_data_pkt->max_cmp_write_len = (uint8_t) blocks; - uint32_t optimal_blocks = (ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE >> block_size_shift); + uint32_t optimal_blocks = (block_size_pow2 ? (ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE >> block_size_shift) : (ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE / block_size)); if ( optimal_blocks == 0UL ) optimal_blocks = 1UL; iscsi_put_be16( (uint8_t *) &vpd_page_block_limits_inquiry_data_pkt->optimal_granularity_xfer_len, (uint16_t) optimal_blocks ); - blocks = (ISCSI_SCSI_EMU_MAX_UNMAP_LBA_COUNT >> block_size_shift); + blocks = (block_size_pow2 ? (ISCSI_SCSI_EMU_MAX_UNMAP_LBA_COUNT >> block_size_shift) : (ISCSI_SCSI_EMU_MAX_UNMAP_LBA_COUNT / block_size)); iscsi_put_be32( (uint8_t *) &vpd_page_block_limits_inquiry_data_pkt->max_xfer_len, blocks ); iscsi_put_be32( (uint8_t *) &vpd_page_block_limits_inquiry_data_pkt->optimal_xfer_len, blocks ); @@ -8489,7 +8501,7 @@ static iscsi_scsi_lun *iscsi_target_node_device_lun_find(iscsi_device *device, c * @param[in] name Pointer to IQN name of target node, * may NOT be NULL, so be careful. * @param[in] alias Pointer to alias of IQN name. - * @param[in] index Target node index number. + * @param[in] pg_tag Portal group tag associated with this target node. * @param[in] lun_id LUN identifier to associate with underlying SCSI device. * @param[in] queue_depth Maximum queue depth. * @param[in] flags Flags for this target node. @@ -8499,7 +8511,7 @@ static iscsi_scsi_lun *iscsi_target_node_device_lun_find(iscsi_device *device, c * @return Pointer to iSCSI target node on successful * operation or NULL in case of an error. */ -iscsi_target_node *iscsi_target_node_create(uint8_t *name, const uint8_t *alias, const int index, const int lun_id, const uint queue_depth, const int flags, const int32_t chap_group, const int header_digest, const int data_digest) +iscsi_target_node *iscsi_target_node_create(uint8_t *name, const uint8_t *alias, const uint64_t pg_tag, const int lun_id, const uint queue_depth, const int flags, const int32_t chap_group, const int header_digest, const int data_digest) { if ( (name == NULL) || (iscsi_target_node_check_name( name ) < 0) || (iscsi_target_node_check_flags( flags, chap_group ) < 0) ) return NULL; @@ -8667,7 +8679,7 @@ iscsi_target_node *iscsi_target_node_create(uint8_t *name, const uint8_t *alias, return NULL; } - target->num = index; + target->pg_tag = pg_tag; target->queue_depth = queue_depth; target->flags = flags; target->header_digest = header_digest; @@ -8732,7 +8744,38 @@ void iscsi_target_node_destroy(iscsi_target_node *target) } /** - * @brief Sends a buffer from a source iSCSI IQN to target iSCSI IQNs. + * @brief Checks if an initiator iSCSI IQN name is allowed to access a specified target node be shown in discovery. + * + * This function currently checks whether the + * portal group is private and if the IQN name + * itself for correctness since there is no + * access control support. + * + * @param[in] target Pointer to iSCSI target node which + * should be checked. May NOT be NULL, so be + * careful. + * @param[in] iqn Pointer to string containing the IQN + * name to be allowed. NULL is an prohibited + * value here, so take caution. + * @retval true The iSCSI IQN name is allowed for discovery. + * @retval false The iSCSI IQN name is ignored in discovery. + */ +static bool iscsi_target_node_iscsi_name_allowed(iscsi_target_node *target, const uint8_t *iqn) +{ + if ( (target == NULL) || (iqn == NULL) ) + return false; + + iscsi_portal_group *portal_group = NULL; + const int rc = iscsi_hashmap_get( iscsi_globvec->portal_groups, (uint8_t *) &target->pg_tag, sizeof(target->pg_tag), (uint8_t **) &portal_group ); + + if ( (portal_group == NULL) || (portal_group->flags & ISCSI_PORTAL_GROUP_FLAGS_PRIVATE) != 0 ) + return false; + + return (iscsi_target_node_check_name( iqn ) >= 0); +} + +/** + * @brief Sends a target node discovery source iSCSI IQN to a buffer for target IQNs. * * This function sends a buffer starting from a * specified position to one, multiple or all @@ -8740,139 +8783,394 @@ void iscsi_target_node_destroy(iscsi_target_node *target) * 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, + * @param[in] target_send Pointer to iSCSI target discovery send + * structure and may NOT be NULL, so be careful. + * @param[in] src Pointer to string containing the source IQN. * 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. + * @param[in] src_len Length of source buffer in bytes. * @return The new position of the written data or a * negative error code otherwise. */ -int32_t iscsi_target_node_send(iscsi_connection *conn, const uint8_t *dst_iqn, const uint8_t *src_iqn, uint8_t *buf, const uint32_t pos, const uint32_t len) +static bool iscsi_target_node_send_copy(iscsi_target_node_send_name *target_send, const uint8_t *src, const uint32_t src_len) { - // TODO: Implement function. + const uint32_t pos = target_send->pos; + const uint32_t len = target_send->len; - return -1; -} + if ( pos >= len ) + return true; -/** - * @brief Calculates the WWN using 64-bit IEEE Extended NAA for a name. - * - * @param[in] name Pointer to string containing the - * name to calculate the IEEE Extended - * NAA for. NULL is NOT allowed here, so - * take caution. - * @return A 64-bit unsigned integer for - * storing the IEEE Extended NAA. - */ -uint64_t iscsi_target_node_wwn_get(const uint8_t *name) -{ - uint64_t value = 0ULL; - int i = 0; + const uint32_t prev_target_send_total_size = target_send->prev_target_send_total_size; - while ( name[i] != '\0' ) { - value = (value * 131ULL) + name[i++]; - } + if ( prev_target_send_total_size < src_len ) { + const uint32_t exp_len = (src_len - prev_target_send_total_size); + uint32_t buf_len = (len - pos); - const uint64_t id_a = ((value & 0xFFF000000ULL) << 24ULL); + if ( buf_len > exp_len ) + buf_len = exp_len; - return ((value & 0xFFFFFFULL) | 0x2000000347000000ULL | id_a); + memcpy( (target_send->buf + pos), (src + prev_target_send_total_size), buf_len ); + + target_send->pos += buf_len; + target_send->prev_target_send_total_size = 0UL; + } else { + target_send->prev_target_send_total_size -= src_len; + } + + return false; } /** - * @brief Extracts the DNBD3 image out of an iSCSI IQN string and opens the DNBD3 image. + * @brief Sends an iSCSI portal for iSCSI discovery to a buffer. * - * This function uses the : separator as - * specified by the IQN standard.\n - * If no colons are in the IQN string, - * the complete string will be - * considered the image file name.\n - * The image file name is assumed - * before the last colon and is - * either directly opened or if - * that fails, a WWN name by - * IEEE Extended NAA is tried as - * well.\n - * The image revision is assumed - * after the last colon. - * @param[in] iqn Pointer to iSCSI IQN string. This - * is not allowed to be NULL, so be careful. - * @return Pointer to DNBD3 image if successful - * operation or NULL if failed. + * Callback function for each element while iterating + * through the iSCSI portals to send for discovery. + * + * @param[in] key Pointer to zero padded key. NULL is + * an invalid pointer here, so be careful. + * @param[in] key_size Number of bytes for the key. + * @param[in] value Value of the key, NULL creates an + * empty key assignment. + * @param[in,out] user_data Pointer to a data structure + * containing the iSCSI connection, the destination + * target node IQN to be searched for, the source + * target node IQN, the pointer to output buffer, the + * position in bytes to put the next target address, the + * buffer length in bytes and the pointer to a + * boolean value indicating if the buffer is full + * and may NOT be NULL, so be careful. + * @retval -2 Memory exhausted during execution. Therefore, + * no further searching is needed. + * @retval -1 The portal has been sent. Therefore, + * no further searching is needed. + * @retval 0 The portal to be sent has not been found + * yet. */ -dnbd3_image_t *iscsi_target_node_image_get(uint8_t *iqn) +int iscsi_target_node_portals_send_callback(uint8_t *key UNUSED, const size_t key_size UNUSED, uint8_t *value, uint8_t *user_data) { - uint8_t *image_name = iqn; - uint8_t *image_rev = NULL; - uint8_t *tmp = (uint8_t *) strchr( (char *) iqn, ':' ); + iscsi_target_node_send_name *target_send = (iscsi_target_node_send_name *) user_data; + iscsi_portal *portal = (iscsi_portal *) value; + uint8_t *server_host = portal->host; - while ( tmp != NULL ) { - tmp++; + if ( (strcmp( (char *) server_host, "[::]" ) == 0) || (strcmp( (char *) server_host, "0.0.0.0" ) == 0) ) { + switch ( portal->type ) { + case ISCSI_PORTAL_TYPE_IPV4 : + case ISCSI_PORTAL_TYPE_IPV6 : { + server_host = target_send->conn->target_host; - if ( image_rev != NULL ) - image_name = image_rev; + break; + } + default : { + return 0; - image_rev = tmp; - tmp = (uint8_t *) strchr( (char *) tmp, ':' ); + break; + } + } } - if ( image_rev == NULL ) - image_rev = image_name; + const uint32_t len = snprintf( NULL, 0, "%s=%s:%s,%" PRIu64, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, server_host, portal->port, portal->group->tag ); + uint8_t *tmp_buf; - const uint len = (uint) (image_rev - image_name); + if ( len > target_send->tmp_len ) { + target_send->tmp_len = iscsi_align_pow2_ceil((len + len)); - if ( len > 0U ) { - tmp = (uint8_t *) malloc( len ); + tmp_buf = realloc( target_send->tmp_buf, target_send->tmp_len ); - if ( tmp == NULL ) { - logadd( LOG_ERROR, "iscsi_target_node_image_get: Out of memory while allocating DNBD3 image name for iSCSI target node" ); + if ( tmp_buf == NULL ) { + logadd( LOG_ERROR, "iscsi_target_node_portals_send_callback: Out of memory while allocating temporary buffer for iSCSI target node discovery" ); - return NULL; + target_send->pos = -1L; + + return -2; } - memcpy( tmp, image_name, (len - 1) ); - tmp[len - 1] = '\0'; + target_send->tmp_buf = tmp_buf; } else { - tmp = image_name; + tmp_buf = target_send->tmp_buf; } - const uint16_t rev = (uint16_t) ((len > 0U) ? atoi( (char *) image_rev ) : 0); - dnbd3_image_t *image = image_getOrLoad( (char *) image_name, rev ); + memset( tmp_buf, 0, (len + 1UL) ); - if ( image == NULL ) { - image = image_getOrLoad( (char *) tmp, rev ); + if ( target_send->prev_target_send_total_size < len ) + snprintf( (char *) tmp_buf, (len + 1UL), "%s=%s:%s,%" PRIu64, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, server_host, portal->port, portal->group->tag ); - if ( image == NULL ) { - if ( strncasecmp( (char *) image_name, ISCSI_TARGET_NODE_NAME_WWN_PREFIX, ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_WWN_PREFIX) ) == 0 ) { - uint64_t wwn = strtoull( (char *) (image_name + ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_WWN_PREFIX)), NULL, 16 ); + target_send->buf_full = iscsi_target_node_send_copy( target_send, tmp_buf, (len + 1UL) ); - image = image_getByWwn( wwn, rev, true ); + if ( target_send->buf_full ) + return -1; - if ( image == NULL ) { - wwn = strtoull( (char *) (tmp + ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_WWN_PREFIX)), NULL, 16 ); - image = image_getByWwn( wwn, rev, true ); - } - } else if ( strncasecmp( (char *) image_name, ISCSI_TARGET_NODE_NAME_NAA_PREFIX, ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_NAA_PREFIX) ) == 0 ) { - uint64_t wwn = strtoull( (char *) (image_name + ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_NAA_PREFIX)), NULL, 16 ); + return 0; +} - image = image_getByWwn( wwn, rev, true ); +/** + * @brief Sends all iSCSI portals for iSCSI discovery to a buffer. + * + * This function retrieves the relevant portal group + * in order to iterate its portals. + * + * @param[in] target Pointer to iSCSI target node and may + * NOT be NULL, so be careful. + * @param[in] target_send Pointer to iSCSI target discovery send + * structure. NULL is NOT allowed here, so + * take caution. + * @return Current buffer position in bytes or a negative + * error code otherwise. + */ +static uint32_t iscsi_target_node_portals_send(iscsi_target_node *target, iscsi_target_node_send_name *target_send) +{ + iscsi_portal_group *portal_group = NULL; + const int rc = iscsi_hashmap_get( iscsi_globvec->portal_groups, (uint8_t *) &target->pg_tag, sizeof(target->pg_tag), (uint8_t **) &portal_group ); - if ( image == NULL ) { - wwn = strtoull( (char *) (tmp + ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_NAA_PREFIX)), NULL, 16 ); - image = image_getByWwn( wwn, rev, true ); - } - } else if ( strncasecmp( (char *) image_name, ISCSI_TARGET_NODE_NAME_EUI_PREFIX, ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_EUI_PREFIX) ) == 0 ) { - uint64_t wwn = (0x2ULL << 60ULL) | (strtoull( (char *) (image_name + ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_EUI_PREFIX)), NULL, 16 ) & 0x0FFFFFFFFFFFFFFFULL); + if ( (portal_group == NULL) || (portal_group->flags & ISCSI_PORTAL_GROUP_FLAGS_PRIVATE) != 0 ) + return target_send->pos; - image = image_getByWwn( wwn, rev, true ); + iscsi_hashmap_iterate( portal_group->portals, iscsi_target_node_portals_send_callback, (uint8_t *) target_send ); - if ( image == NULL ) { - wwn = (0x2ULL << 60ULL) | (strtoull( (char *) (tmp + ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_EUI_PREFIX)), NULL, 16 ) & 0x0FFFFFFFFFFFFFFFULL); - image = image_getByWwn( wwn, rev, true ); - } + return target_send->pos; +} + +/** + * @brief Sends an iSCSI discovery source target node IQN for a specific target IQN to a buffer. + * + * Callback function for each element while iterating + * through the iSCSI target nodes to send for + * discovery. + * + * @param[in] key Pointer to zero padded key. NULL is + * an invalid pointer here, so be careful. + * @param[in] key_size Number of bytes for the key. + * @param[in] value Value of the key, NULL creates an + * empty key assignment. + * @param[in,out] user_data Pointer to a data structure + * containing the iSCSI connection, the destination + * target node IQN to be searched for, the source + * target node IQN, the pointer to output buffer, the + * position in bytes to put the next target address, the + * buffer length in bytes and the pointer to a + * boolean value indicating if the buffer is full + * and may NOT be NULL, so be careful. + * @retval -2 Memory exhausted during execution. Therefore, + * no further searching is needed. + * @retval -1 The target node has been sent. Therefore, + * no further searching is needed. + * @retval 0 The target node to be sent has not been found + * yet. + */ +int iscsi_target_node_send_callback(uint8_t *key UNUSED, const size_t key_size UNUSED, uint8_t *value, uint8_t *user_data) +{ + iscsi_target_node_send_name *target_send = (iscsi_target_node_send_name *) user_data; + iscsi_target_node *target = (iscsi_target_node *) value; + + if ( (target_send->dst_iqn != NULL) && (strcasecmp( (char *) target_send->dst_iqn, (char *) target->name ) != 0) ) + return 0; + + const bool rc = iscsi_target_node_iscsi_name_allowed( target, target_send->src_iqn ); + + if ( !rc ) + return 0; + + const uint32_t len = snprintf( NULL, 0, "%s=%s", ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_NAME, target->name ); + uint8_t *tmp_buf; + + if ( len > target_send->tmp_len ) { + target_send->tmp_len = iscsi_align_pow2_ceil((len + len)); + + tmp_buf = realloc( target_send->tmp_buf, target_send->tmp_len ); + + if ( tmp_buf == NULL ) { + logadd( LOG_ERROR, "iscsi_target_node_send_callback: Out of memory while allocating temporary buffer for iSCSI target node discovery" ); + + target_send->pos = -1L; + + return -2; + } + + target_send->tmp_buf = tmp_buf; + } else { + tmp_buf = target_send->tmp_buf; + } + + memset( tmp_buf, 0, (len + 1UL) ); + + if ( target_send->prev_target_send_total_size < len ) + snprintf( (char *) tmp_buf, (len + 1UL), "%s=%s", ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_NAME, target->name ); + + target_send->buf_full = iscsi_target_node_send_copy( target_send, tmp_buf, (len + 1UL) ); + + if ( target_send->buf_full ) + return -1; + + target_send->pos = iscsi_target_node_portals_send( target, target_send ); + + if ( target_send->buf_full ) + return -1; + + return 0; +} + +/** + * @brief Sends a target node discovery source iSCSI IQN to a buffer for target 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, + * may NOT be NULL, so be careful. + * @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[out] buf Pointer to output buffer. NULL is forbidden + * here, so take caution. + * @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. + */ +int32_t iscsi_target_node_send(iscsi_connection *conn, uint8_t *dst_iqn, uint8_t *src_iqn, uint8_t *buf, const uint32_t pos, const uint32_t len) +{ + if ( pos >= len ) { + buf[len - 1] = '\0'; + + return (int32_t) len; + } + + iscsi_target_node_send_name target_send = {conn, ((strcasecmp( (char *) dst_iqn, "ALL" ) != 0) ? dst_iqn : NULL), src_iqn, NULL, buf, pos, len, 0UL, conn->target_send_total_size, false}; + + pthread_rwlock_rdlock( &iscsi_globvec->target_nodes_rwlock ); + pthread_rwlock_rdlock( &iscsi_globvec->portal_groups_rwlock ); + iscsi_hashmap_iterate( iscsi_globvec->target_nodes, iscsi_target_node_send_callback, (uint8_t *) &target_send ); + pthread_rwlock_unlock( &iscsi_globvec->portal_groups_rwlock ); + pthread_rwlock_unlock( &iscsi_globvec->target_nodes_rwlock ); + + if ( target_send.tmp_buf != NULL ) + free( target_send.tmp_buf ); + + if ( target_send.buf_full ) + conn->target_send_total_size += target_send.pos; + else + conn->target_send_total_size = 0UL; + + return (int32_t) target_send.pos; +} + +/** + * @brief Calculates the WWN using 64-bit IEEE Extended NAA for a name. + * + * @param[in] name Pointer to string containing the + * name to calculate the IEEE Extended + * NAA for. NULL is NOT allowed here, so + * take caution. + * @return A 64-bit unsigned integer for + * storing the IEEE Extended NAA. + */ +uint64_t iscsi_target_node_wwn_get(const uint8_t *name) +{ + uint64_t value = 0ULL; + int i = 0; + + while ( name[i] != '\0' ) { + value = (value * 131ULL) + name[i++]; + } + + const uint64_t id_a = ((value & 0xFFF000000ULL) << 24ULL); + + return ((value & 0xFFFFFFULL) | 0x2000000347000000ULL | id_a); +} + +/** + * @brief Extracts the DNBD3 image out of an iSCSI IQN string and opens the DNBD3 image. + * + * This function uses the : separator as + * specified by the IQN standard.\n + * If no colons are in the IQN string, + * the complete string will be + * considered the image file name.\n + * The image file name is assumed + * before the last colon and is + * either directly opened or if + * that fails, a WWN name by + * IEEE Extended NAA is tried as + * well.\n + * The image revision is assumed + * after the last colon. + * @param[in] iqn Pointer to iSCSI IQN string. This + * is not allowed to be NULL, so be careful. + * @return Pointer to DNBD3 image if successful + * operation or NULL if failed. + */ +dnbd3_image_t *iscsi_target_node_image_get(uint8_t *iqn) +{ + uint8_t *image_name = iqn; + uint8_t *image_rev = NULL; + uint8_t *tmp = (uint8_t *) strchr( (char *) iqn, ':' ); + + while ( tmp != NULL ) { + tmp++; + + if ( image_rev != NULL ) + image_name = image_rev; + + image_rev = tmp; + tmp = (uint8_t *) strchr( (char *) tmp, ':' ); + } + + if ( image_rev == NULL ) + image_rev = image_name; + + const uint len = (uint) (image_rev - image_name); + + if ( len > 0U ) { + tmp = (uint8_t *) malloc( len ); + + if ( tmp == NULL ) { + logadd( LOG_ERROR, "iscsi_target_node_image_get: Out of memory while allocating DNBD3 image name for iSCSI target node" ); + + return NULL; + } + + memcpy( tmp, image_name, (len - 1) ); + tmp[len - 1] = '\0'; + } else { + tmp = image_name; + } + + const uint16_t rev = (uint16_t) ((len > 0U) ? atoi( (char *) image_rev ) : 0); + dnbd3_image_t *image = image_getOrLoad( (char *) image_name, rev ); + + if ( image == NULL ) { + image = image_getOrLoad( (char *) tmp, rev ); + + if ( image == NULL ) { + if ( strncasecmp( (char *) image_name, ISCSI_TARGET_NODE_NAME_WWN_PREFIX, ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_WWN_PREFIX) ) == 0 ) { + uint64_t wwn = strtoull( (char *) (image_name + ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_WWN_PREFIX)), NULL, 16 ); + + image = image_getByWwn( wwn, rev, true ); + + if ( image == NULL ) { + wwn = strtoull( (char *) (tmp + ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_WWN_PREFIX)), NULL, 16 ); + image = image_getByWwn( wwn, rev, true ); + } + } else if ( strncasecmp( (char *) image_name, ISCSI_TARGET_NODE_NAME_NAA_PREFIX, ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_NAA_PREFIX) ) == 0 ) { + uint64_t wwn = strtoull( (char *) (image_name + ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_NAA_PREFIX)), NULL, 16 ); + + image = image_getByWwn( wwn, rev, true ); + + if ( image == NULL ) { + wwn = strtoull( (char *) (tmp + ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_NAA_PREFIX)), NULL, 16 ); + image = image_getByWwn( wwn, rev, true ); + } + } else if ( strncasecmp( (char *) image_name, ISCSI_TARGET_NODE_NAME_EUI_PREFIX, ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_EUI_PREFIX) ) == 0 ) { + uint64_t wwn = (0x2ULL << 60ULL) | (strtoull( (char *) (image_name + ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_EUI_PREFIX)), NULL, 16 ) & 0x0FFFFFFFFFFFFFFFULL); + + image = image_getByWwn( wwn, rev, true ); + + if ( image == NULL ) { + wwn = (0x2ULL << 60ULL) | (strtoull( (char *) (tmp + ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_EUI_PREFIX)), NULL, 16 ) & 0x0FFFFFFFFFFFFFFFULL); + image = image_getByWwn( wwn, rev, true ); + } } } } @@ -9050,7 +9348,12 @@ iscsi_target_node *iscsi_target_node_find(uint8_t *target_name) if ( data_digest < 0L ) data_digest = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_DATA_DIGEST ); - target = iscsi_target_node_create( target_name, NULL, 0, image->rid, 16U, 0, 0L, (int) header_digest, (int) data_digest ); + int32_t pg_tag = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_PORTAL_GROUP_TAG ); + + if ( pg_tag <= 0L ) + pg_tag = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_PORTAL_GROUP_TAG ); + + target = iscsi_target_node_create( target_name, NULL, pg_tag, image->rid, 16U, 0, 0L, (int) header_digest, (int) data_digest ); if ( target == NULL ) { logadd( LOG_ERROR, "iscsi_target_node_find: iSCSI target node not found" ); @@ -9072,11 +9375,11 @@ iscsi_target_node *iscsi_target_node_find(uint8_t *target_name) return NULL; } - int rc = iscsi_hashmap_put( iscsi_globvec->target_nodes, (uint8_t *) hash_key, key_len, (uint8_t *) target ); + int rc = iscsi_hashmap_put( iscsi_globvec->target_nodes, hash_key, key_len, (uint8_t *) target ); if ( rc < 0 ) { pthread_rwlock_unlock( &iscsi_globvec->target_nodes_rwlock ); - iscsi_hashmap_key_destroy( (uint8_t *) hash_key ); + iscsi_hashmap_key_destroy( hash_key ); iscsi_target_node_destroy( target ); return NULL; @@ -9492,29 +9795,19 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock) } conn->partial_pairs = NULL; - conn->text_key_value_pairs = iscsi_hashmap_create( ((sizeof(iscsi_connection_key_value_pair_lut) / sizeof(struct iscsi_key_value_pair_lut_entry)) - 1) ); - - 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->text_key_value_pairs = 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->target_host = NULL; + conn->portal_host = NULL; + conn->portal_port = NULL; + conn->pdu_processing = NULL; iscsi_list_create( &conn->scsi_data_in_queued_tasks ); @@ -9525,7 +9818,6 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock) iscsi_list_create( &conn->r2t_tasks_active ); iscsi_list_create( &conn->r2t_tasks_queue ); - conn->target_send_total_size = 0U; conn->scsi_data_in_cnt = 0U; conn->scsi_data_out_cnt = 0U; conn->task_cnt = 0U; @@ -9538,6 +9830,7 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock) conn->flags = 0; conn->state = ISCSI_CONNECT_STATE_INVALID; conn->login_phase = ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION; + conn->target_send_total_size = 0UL; conn->max_recv_ds_len = ISCSI_DEFAULT_RECV_DS_LEN; conn->pg_tag = portal->group->tag; conn->isid.a = 0; @@ -9557,7 +9850,7 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock) iscsi_list_create( &conn->exec_queue ); - conn->stat_iscsi_opcodes = iscsi_hashmap_create( 256U ); + conn->stat_iscsi_opcodes = iscsi_hashmap_create( 0U ); if ( conn->stat_iscsi_opcodes == NULL ) { logadd( LOG_ERROR, "iscsi_create: Out of memory while initializing iSCSI global vector iSCSI opcode statistics" ); @@ -9570,7 +9863,7 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock) return NULL; } - conn->stat_scsi_opcodes = iscsi_hashmap_create( 256U ); + conn->stat_scsi_opcodes = iscsi_hashmap_create( 0U ); if ( conn->stat_scsi_opcodes == NULL ) { logadd( LOG_ERROR, "iscsi_create: Out of memory while initializing iSCSI global vector iSCSI SCSI opcode statistics" ); @@ -9723,6 +10016,12 @@ void iscsi_connection_destroy(iscsi_connection *conn) conn->portal_host = NULL; } + if ( conn->target_host != NULL ) { + free( conn->target_host ); + + conn->target_host = NULL; + } + if ( conn->target_name_short != NULL ) { free( conn->target_name_short ); @@ -10246,7 +10545,7 @@ static uint8_t *iscsi_negotiate_key_value_pair_all(const iscsi_key_value_pair *k } } - return key_value_pair->value; + return old_value; } /** @@ -10407,7 +10706,7 @@ int iscsi_negotiate_key_value_pair_callback(uint8_t *key, const size_t key_size, */ int32_t iscsi_negotiate_key_value_pairs(iscsi_connection *conn, iscsi_hashmap *key_value_pairs, uint8_t *buf, const uint32_t pos, const uint32_t len) { - if ( pos > len ) { + if ( pos >= len ) { buf[len - 1UL] = '\0'; return len; @@ -11094,13 +11393,9 @@ static iscsi_session *iscsi_session_get_by_tsih(const uint16_t tsih) const uint64_t hash_key = tsih; iscsi_session *session; - pthread_rwlock_rdlock( &iscsi_globvec->sessions_rwlock ); + const int rc = iscsi_hashmap_get( iscsi_globvec->sessions, (uint8_t *) &hash_key, sizeof(hash_key), (uint8_t **) &session ); - int rc = iscsi_hashmap_get( iscsi_globvec->sessions, (uint8_t *) &hash_key, sizeof(hash_key), (uint8_t **) &session ); - - pthread_rwlock_unlock( &iscsi_globvec->sessions_rwlock ); - - return ((rc == 0) ? session : NULL); + return ((rc >= 0) ? session : NULL); } /** @@ -11120,20 +11415,46 @@ static iscsi_session *iscsi_session_get_by_tsih(const uint16_t tsih) */ static uint16_t iscsi_session_append(iscsi_connection *conn, const uint8_t *init_port_name, const uint16_t tsih) { + pthread_rwlock_rdlock( &iscsi_globvec->sessions_rwlock ); iscsi_session *session = iscsi_session_get_by_tsih( tsih ); - if ( (session == NULL) || (conn->pg_tag != session->tag) || (strcasecmp( (char *) init_port_name, (char *) iscsi_port_get_name( session->init_port ) ) != 0) || (conn->target != session->target) ) + if ( (session == NULL) || (conn->pg_tag != session->tag) || (strcasecmp( (char *) init_port_name, (char *) iscsi_port_get_name( session->init_port ) ) != 0) || (conn->target != session->target) ) { + pthread_rwlock_unlock( &iscsi_globvec->sessions_rwlock ); + return (ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR << 8U) | ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_NO_SESSION_SPANNING; + } + + if ( atomic_load_explicit( &session->conns, memory_order_acquire ) >= session->max_conns ) { + pthread_rwlock_unlock( &iscsi_globvec->sessions_rwlock ); - if ( atomic_load_explicit( &session->conns, memory_order_acquire ) >= session->max_conns ) return (ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR << 8U) | ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_TOO_MANY_CONNECTIONS; + } - conn->session = session; + pthread_rwlock_unlock( &iscsi_globvec->sessions_rwlock ); + pthread_rwlock_wrlock( &iscsi_globvec->sessions_rwlock ); + + session = iscsi_session_get_by_tsih( tsih ); + + if ( (session == NULL) || (conn->pg_tag != session->tag) || (strcasecmp( (char *) init_port_name, (char *) iscsi_port_get_name( session->init_port ) ) != 0) || (conn->target != session->target) ) { + pthread_rwlock_unlock( &iscsi_globvec->sessions_rwlock ); + + return (ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR << 8U) | ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_NO_SESSION_SPANNING; + } + + if ( atomic_load_explicit( &session->conns, memory_order_acquire ) >= session->max_conns ) { + pthread_rwlock_unlock( &iscsi_globvec->sessions_rwlock ); + + return (ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR << 8U) | ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_TOO_MANY_CONNECTIONS; + } iscsi_list_enqueue( &session->conn_list, &conn->node ); atomic_fetch_add_explicit( &session->conns, 1UL, memory_order_release ); + pthread_rwlock_unlock( &iscsi_globvec->sessions_rwlock ); + + conn->session = session; + return (ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SUCCESS << 8U) | ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SUCCESS; } @@ -12226,7 +12547,7 @@ static int iscsi_connection_pdu_header_handle_text_req(iscsi_connection *conn, i if ( conn->session->current_text_init_task_tag == 0xFFFFFFFFUL ) conn->session->current_text_init_task_tag = init_task_tag; - else + else if ( conn->session->current_text_init_task_tag != init_task_tag ) return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); return ISCSI_CONNECT_PDU_READ_OK; @@ -12317,7 +12638,6 @@ int iscsi_r2t_send(iscsi_connection *conn, iscsi_task *task, uint32_t *r2t_sn, c iscsi_put_be32( (uint8_t *) &r2t_pkt->stat_sn, conn->stat_sn ); iscsi_put_be32( (uint8_t *) &r2t_pkt->exp_cmd_sn, atomic_load_explicit( &conn->session->exp_cmd_sn, memory_order_acquire ) ); iscsi_put_be32( (uint8_t *) &r2t_pkt->max_cmd_sn, atomic_load_explicit( &conn->session->max_cmd_sn, memory_order_acquire ) ); - r2t_pkt->data_sn = 0UL; iscsi_put_be32( (uint8_t *) &r2t_pkt->r2t_sn, (*r2t_sn)++ ); task->r2t_data_sn = 0UL; @@ -13181,10 +13501,19 @@ static int iscsi_connection_login_session_normal(iscsi_connection *conn, iscsi_p return rc; iscsi_device *device = target->device; + uint64_t pg_tag = (uint64_t) iscsi_config_get( device->name, ISCSI_GLOBALS_CONFIG_TYPE_PORTAL_GROUP_TAG ); + + if ((int64_t) pg_tag <= 0L ) + pg_tag = (uint64_t) iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_PORTAL_GROUP_TAG ); + + if ( conn->pg_tag != pg_tag ) { + conn->pg_tag = pg_tag; + target->pg_tag = pg_tag; + } conn->device = device; conn->target = target; - conn->target_port = iscsi_device_find_port_by_portal_group_tag( device, conn->pg_tag ); + conn->target_port = iscsi_device_find_port_by_portal_group_tag( device, pg_tag ); rc = iscsi_connection_login_check_session( conn, login_response_pdu, init_port_name, cid ); @@ -13452,7 +13781,7 @@ static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscs if ( type == ISCSI_SESSION_TYPE_DISCOVERY ) { conn->session->max_conns = 1UL; - rc = iscsi_add_int_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS, conn->session->max_conns ); + rc = iscsi_update_int_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS, conn->session->max_conns ); if ( rc < 0 ) return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER; @@ -13790,10 +14119,13 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } - if ( (pdu->ds_len == 0UL) && (iscsi_hashmap_size( key_value_pairs ) == 0U) ) { - iscsi_hashmap *tmp_hashmap = key_value_pairs; + const bool text_ack = ((pdu->ds_len == 0UL) && (iscsi_hashmap_size( key_value_pairs ) == 0U) && (conn->text_key_value_pairs != NULL)); + + if ( text_ack ) { + iscsi_hashmap_destroy( key_value_pairs ); + key_value_pairs = conn->text_key_value_pairs; - conn->text_key_value_pairs = tmp_hashmap; + conn->text_key_value_pairs = NULL; } iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 0U, conn->header_digest, conn->max_recv_ds_len, conn->data_digest ); @@ -13830,7 +14162,7 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc if ( (text_req_pkt->flags & ISCSI_TEXT_REQ_FLAGS_FINAL) != 0 ) text_response_pkt->flags |= (int8_t) ISCSI_TEXT_RESPONSE_FLAGS_FINAL; - text_req_pkt->reserved = 0U; + text_response_pkt->reserved = 0U; 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 ); @@ -13857,7 +14189,7 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc ds_len = iscsi_target_node_send( conn, send_targets_val, conn->init_name, (uint8_t *) response_pdu->ds_cmd_data, ds_len, response_pdu->len ); } else { if ( send_targets_val[0] == '\0' ) - send_targets_val = conn->target_port->name; + send_targets_val = conn->target->name; if ( strcasecmp( (char *) send_targets_val, "ALL" ) == 0 ) { iscsi_key_value_pair *key_value_pair; @@ -13877,7 +14209,7 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc } } - if ( conn->target_send_total_size == 0U ) { + if ( conn->target_send_total_size != 0UL ) { text_response_pkt->flags |= (int8_t) ISCSI_TEXT_RESPONSE_FLAGS_CONTINUE; text_response_pkt->flags &= (int8_t) ~ISCSI_TEXT_RESPONSE_FLAGS_FINAL; } @@ -13891,7 +14223,10 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } - if ( conn->target_send_total_size == 0U ) { + if ( conn->target_send_total_size == 0UL ) { + if ( text_ack ) + conn->text_key_value_pairs = NULL; + iscsi_hashmap_iterate( key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL ); iscsi_hashmap_destroy( key_value_pairs ); } else { @@ -13904,7 +14239,7 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc 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 ) { + if ( (text_response_pkt->flags & ISCSI_TEXT_RESPONSE_FLAGS_FINAL) != 0 ) { text_response_pkt->target_xfer_tag = 0xFFFFFFFFUL; // Minus one does not require endianess conversion conn->session->current_text_init_task_tag = 0xFFFFFFFFUL; @@ -13914,13 +14249,13 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc iscsi_put_be32( (uint8_t *) &text_response_pkt->stat_sn, conn->stat_sn++ ); - if ( (text_response_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) + if ( (text_req_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) atomic_fetch_add_explicit( &conn->session->max_cmd_sn, 1UL, memory_order_release ); iscsi_put_be32( (uint8_t *) &text_response_pkt->exp_cmd_sn, atomic_load_explicit( &conn->session->exp_cmd_sn, memory_order_acquire ) ); iscsi_put_be32( (uint8_t *) &text_response_pkt->max_cmd_sn, atomic_load_explicit( &conn->session->max_cmd_sn, memory_order_acquire ) ); - text_response_pkt->reserved2[0] = 0ULL; - text_response_pkt->reserved2[1] = 0ULL; + text_response_pkt->reserved2 = 0ULL; + text_response_pkt->reserved3 = 0UL; iscsi_connection_pdu_write( conn, response_pdu, iscsi_connection_pdu_text_complete, (uint8_t *) conn ); @@ -14313,6 +14648,310 @@ int iscsi_connection_pdu_handle(iscsi_connection *conn) return i; } +/** + * @brief Adds iSCSI portals to a specified iSCSI portal group. + * + * Callback function for each element while iterating + * through the iSCSI portal groups. + * + * @param[in] key Pointer to zero padded key. NULL is + * an invalid pointer here, so be careful. + * @param[in] key_size Number of bytes for the key. + * @param[in] value Value of the key, NULL creates an + * empty key assignment. + * @param[in,out] user_data Pointer to a data structure + * containing the client socket, the iSCSI portal of + * this running instance, the host name, the port and + * a write locked flag which indicates changes to either + * the portal group or portals have been added and may + * NOT be NULL, so be careful. + * @retval -1 One of the iSCSI portals could NOT be + * added and therefore, an error occured and no further + * processing is needed. + * @retval 0 The iSCSI portal has been created and added + * successfully. + */ +int iscsi_connection_handle_add_portals_callback(uint8_t *key UNUSED, const size_t key_size UNUSED, uint8_t *value, uint8_t *user_data) +{ + struct sockaddr_storage server_addr; + char server_hostname[sizeof(struct sockaddr_un)]; + iscsi_connection_handle_add_portals *add_portals = (iscsi_connection_handle_add_portals *) user_data; + iscsi_portal_group *portal_group = (iscsi_target_node *) value; + socklen_t server_addr_len = sizeof(server_addr); + const int sock = add_portals->sock; + + if ( getpeername( sock, (struct sockaddr *) &server_addr, &server_addr_len ) != 0 ) + return -1; + + void *server_host; + int server_port; + uint server_type; + + switch ( server_addr.ss_family ) { + case AF_UNIX : { + server_host = &((struct sockaddr_un *) &server_addr)->sun_path; + + break; + } + case AF_INET : { + server_host = &((struct sockaddr_in *) &server_addr)->sin_addr; + + break; + } + case AF_INET6 : { + server_host = &((struct sockaddr_in6 *) &server_addr)->sin6_addr; + + break; + } + default : { + return -1; + + break; + } + } + + inet_ntop( server_addr.ss_family, server_host, server_hostname, sizeof(server_hostname) ); + + if ( getsockname( sock, (struct sockaddr *) &server_addr, &server_addr_len ) != 0 ) + return -1; + + switch ( server_addr.ss_family ) { + case AF_UNIX : { + server_port = ntohs( ((struct sockaddr_un *) &server_addr)->sun_family ); + server_type = ISCSI_PORTAL_TYPE_SOCK; + + break; + } + case AF_INET : { + server_port = ntohs( ((struct sockaddr_in *) &server_addr)->sin_port ); + server_type = ISCSI_PORTAL_TYPE_IPV4; + + break; + } + case AF_INET6 : { + server_port = ntohs( ((struct sockaddr_in6 *) &server_addr)->sin6_port ); + server_type = ISCSI_PORTAL_TYPE_IPV6; + + break; + } + default : { + return -1; + + break; + } + } + + uint8_t *port = iscsi_sprintf_alloc( "%" PRIu16, server_port ); + + if ( port == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_handle_add_portals_callback: Out of memory while allocating iSCSI portal port number" ); + + return -1; + } + + uint host_len = (uint) strlen( server_hostname ); + uint8_t *host = malloc( (host_len + 1U) ); + + if ( host == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_handle_add_portals_callback: Out of memory while allocating iSCSI portal host name" ); + + free( port ); + + return -1; + } + + memcpy( host, server_hostname, host_len ); + host[host_len] = '\0'; + + uint8_t *tmp_buf = iscsi_sprintf_alloc( "%s:%s", host, port ); + + if ( tmp_buf == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_handle_add_portals_callback: Out of memory while allocating temporarily iSCSI portal name" ); + + free( host ); + free( port ); + + return -1; + } + + uint key_len = (uint) (strlen( (char *) tmp_buf ) + 1U); + uint8_t *hash_key = iscsi_hashmap_key_create( tmp_buf, key_len ); + + free( tmp_buf ); + + if ( hash_key == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_handle_add_portals_callback: Out of memory while allocating temporarily iSCSI portal name hash key" ); + + free( host ); + free( port ); + + return -1; + } + + iscsi_portal *portal = NULL; + int rc = iscsi_hashmap_get( portal_group->portals, hash_key, key_len, (uint8_t **) &portal ); + + if ( portal == NULL ) { + if ( !add_portals->write_locked ) { + add_portals->write_locked = true; + + pthread_rwlock_unlock( &iscsi_globvec->portal_groups_rwlock ); + pthread_rwlock_wrlock( &iscsi_globvec->portal_groups_rwlock ); + } + + rc = iscsi_hashmap_get( portal_group->portals, hash_key, key_len, (uint8_t **) &portal ); + + if ( portal == NULL ) { + portal = iscsi_portal_create( host, port, server_type ); + + if ( portal == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_handle_add_portals_callback: Out of memory while allocating iSCSI portal" ); + + iscsi_hashmap_key_destroy( hash_key ); + free( host ); + free( port ); + + return -1; + } + + rc = iscsi_portal_group_add_portal( portal_group, portal ); + + if ( rc < 0 ) { + iscsi_portal_destroy( portal ); + iscsi_hashmap_key_destroy( hash_key ); + free( host ); + free( port ); + + return -1; + } + } + } + + iscsi_hashmap_key_destroy( hash_key ); + + if ( add_portals->portal == NULL ) + add_portals->portal = portal; + + if ( add_portals->host == NULL ) + add_portals->host = host; + + if ( add_portals->port == NULL ) + add_portals->port = port; + + for ( int i = 0; i < SERVER_MAX_ALTS; i++ ) { + altservers_toString( i, server_hostname, sizeof(server_hostname) ); + + if ( (server_hostname[0] == '\0') || (server_hostname[0] == '<') ) + continue; + + uint8_t *alt_server_port = memchr( server_hostname, ':', sizeof(server_hostname) ); + const uint alt_server_host_len = ((alt_server_port != NULL) ? (uint) (alt_server_port++ - (uint8_t *) server_hostname) : (uint) strlen( server_hostname )); + uint8_t *alt_server_host = malloc( (alt_server_host_len + 1U) ); + const uint alt_server_type = ((server_hostname[0] == '[') ? ISCSI_PORTAL_TYPE_IPV6 : ISCSI_PORTAL_TYPE_IPV4); + + if ( alt_server_host == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_handle_add_portals_callback: Out of memory while allocating iSCSI alternative portal host name" ); + + free( host ); + free( port ); + + return -1; + } + + memcpy( alt_server_host, server_hostname, alt_server_host_len ); + alt_server_host[alt_server_host_len] = '\0'; + + uint8_t *tmp_buf; + + if ( alt_server_port != NULL ) + tmp_buf = iscsi_sprintf_alloc( "%s:%s", alt_server_host, alt_server_port ); + else + tmp_buf = iscsi_sprintf_alloc( "%s:%u", alt_server_host, PORT ); + + if ( tmp_buf == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_handle_add_portals_callback: Out of memory while allocating temporarily iSCSI alternative portal name" ); + + free( alt_server_host ); + free( host ); + free( port ); + + return -1; + } + + key_len = (uint) (strlen( (char *) tmp_buf ) + 1U); + hash_key = iscsi_hashmap_key_create( tmp_buf, key_len ); + + free( tmp_buf ); + + if ( hash_key == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_handle_add_portals_callback: Out of memory while allocating temporarily iSCSI alternative portal name hash key" ); + + free( alt_server_host ); + free( host ); + free( port ); + + return -1; + } + + if ( alt_server_port == NULL ) { + alt_server_port = (uint8_t *) strchr( (char *) hash_key, ':' ); + alt_server_port++; + } + + iscsi_portal *portal = NULL; + rc = iscsi_hashmap_get( portal_group->portals, hash_key, key_len, (uint8_t **) &portal ); + + if ( portal == NULL ) { + if ( !add_portals->write_locked ) { + add_portals->write_locked = true; + + pthread_rwlock_unlock( &iscsi_globvec->portal_groups_rwlock ); + pthread_rwlock_wrlock( &iscsi_globvec->portal_groups_rwlock ); + } + + rc = iscsi_hashmap_get( portal_group->portals, hash_key, key_len, (uint8_t **) &portal ); + + if ( portal == NULL ) { + portal = iscsi_portal_create( alt_server_host, alt_server_port, alt_server_type ); + + if ( portal == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_handle_add_portals_callback: Out of memory while allocating iSCSI alternative portal" ); + + iscsi_hashmap_key_destroy( hash_key ); + free( alt_server_host ); + free( host ); + free( port ); + + return -1; + } + + rc = iscsi_portal_group_add_portal( portal_group, portal ); + + if ( rc < 0 ) { + iscsi_portal_destroy( portal ); + iscsi_hashmap_key_destroy( hash_key ); + free( alt_server_host ); + free( host ); + free( port ); + + return -1; + } + } + } + + iscsi_hashmap_key_destroy( hash_key ); + free( alt_server_host ); + } + + if ( add_portals->host != host ) + free( host ); + + if ( add_portals->port != port ) + free( port ); + + return 0; +} + /** * @brief Handles an iSCSI connection until connection is closed. * @@ -14339,8 +14978,7 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ return; } - // sock_setTimeout( client->sock, 1000L * 3600L ); // TODO: Remove after finishing iSCSI implementation - + const int sock = client->sock; uint recv_buf_len = (uint) iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_FIRST_BURST_LEN ); if ( recv_buf_len < 4096U ) @@ -14354,53 +14992,38 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ uint send_buf_len = recv_buf_len; recv_buf_len = (uint) ISCSI_ALIGN(recv_buf_len, ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE); - setsockopt( client->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. + setsockopt( 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. send_buf_len += (uint) (ISCSI_DEFAULT_RECV_DS_LEN << 2UL); send_buf_len = (uint) ISCSI_ALIGN(send_buf_len, ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE); - setsockopt( client->sock, SOL_SOCKET, SO_SNDBUF, &send_buf_len, sizeof(send_buf_len)); // Not being able to set the buffer is NOT fatal, so ignore error. + setsockopt( sock, SOL_SOCKET, SO_SNDBUF, &send_buf_len, sizeof(send_buf_len)); // Not being able to set the buffer is NOT fatal, so ignore error. + + // sock_setTimeout( sock, 1000L * 3600L ); // TODO: Remove after finishing iSCSI implementation pthread_rwlock_rdlock( &iscsi_globvec->portal_groups_rwlock ); - uint64_t *hash_key; iscsi_portal_group *portal_group = NULL; - const uint64_t pg_tag = 1ULL; + const uint64_t pg_tag = (uint64_t) iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_PORTAL_GROUP_TAG ); int rc = iscsi_hashmap_get( iscsi_globvec->portal_groups, (uint8_t *) &pg_tag, sizeof(pg_tag), (uint8_t **) &portal_group ); + bool write_locked = false; if ( portal_group == NULL ) { + write_locked = true; + pthread_rwlock_unlock( &iscsi_globvec->portal_groups_rwlock ); pthread_rwlock_wrlock( &iscsi_globvec->portal_groups_rwlock ); rc = iscsi_hashmap_get( iscsi_globvec->portal_groups, (uint8_t *) &pg_tag, sizeof(pg_tag), (uint8_t **) &portal_group ); if ( portal_group == NULL ) { - hash_key = (uint64_t *) malloc( sizeof(uint64_t) ); - - if ( hash_key == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI portal group" ); - - pthread_rwlock_unlock( &iscsi_globvec->portal_groups_rwlock ); - pthread_rwlock_unlock( &iscsi_globvec_rwlock ); - - return; - } - - iscsi_hashmap_key_create_id( iscsi_globvec->portal_groups, hash_key ); - portal_group = iscsi_portal_group_create( *hash_key, 0 ); + portal_group = iscsi_portal_group_create( pg_tag, ISCSI_PORTAL_GROUP_FLAGS_PRIVATE ); if ( portal_group == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI portal group" ); - - iscsi_hashmap_key_destroy( (uint8_t *) hash_key ); pthread_rwlock_unlock( &iscsi_globvec->portal_groups_rwlock ); pthread_rwlock_unlock( &iscsi_globvec_rwlock ); return; } - portal_group->tag = *hash_key; - - iscsi_hashmap_key_destroy( (uint8_t *) hash_key ); - rc = iscsi_hashmap_put( iscsi_globvec->portal_groups, (uint8_t *) &portal_group->tag, sizeof(portal_group->tag), (uint8_t *) portal_group ); if ( rc < 0 ) { @@ -14413,141 +15036,78 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ } } + iscsi_connection_handle_add_portals add_portals = {sock, NULL, NULL, NULL, write_locked}; + rc = iscsi_hashmap_iterate( iscsi_globvec->portal_groups, iscsi_connection_handle_add_portals_callback, (uint8_t *) &add_portals ); pthread_rwlock_unlock( &iscsi_globvec->portal_groups_rwlock ); - host_to_string( &client->host, client->hostName, HOSTNAMELEN ); - - const uint8_t *port = memchr( client->hostName, ':', HOSTNAMELEN ); - const uint host_len = ((port != NULL) ? (uint) (port++ - (uint8_t *) client->hostName) : (uint) strlen( client->hostName )); - uint8_t *host = malloc( (host_len + 1U) ); - - if ( host == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI portal host name" ); - - pthread_rwlock_unlock( &iscsi_globvec_rwlock ); - return; - } - - memcpy( host, client->hostName, host_len ); - host[host_len] = '\0'; - - uint8_t *tmp_buf; - - if ( port != NULL ) - tmp_buf = iscsi_sprintf_alloc( "%s:%s", host, port ); - else - tmp_buf = iscsi_sprintf_alloc( "%s:%u", host, PORT ); - - if ( tmp_buf == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating temporarily iSCSI portal name" ); - - free( host ); - pthread_rwlock_unlock( &iscsi_globvec_rwlock ); - - return; - } - - const uint key_len = (uint) (strlen( (char *) tmp_buf ) + 1U); - - hash_key = (uint64_t *) iscsi_hashmap_key_create( tmp_buf, key_len ); - - free( tmp_buf ); - - if ( hash_key == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating temporarily iSCSI portal name hash key" ); - - free( host ); - pthread_rwlock_unlock( &iscsi_globvec_rwlock ); - - return; - } - - if ( port == NULL ) { - port = (uint8_t *) strchr( (char *) hash_key, ':' ); - port++; - } - - iscsi_portal *portal = iscsi_portal_create( host, port ); - - if ( portal == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI portal" ); - - iscsi_hashmap_key_destroy( (uint8_t *) hash_key ); - free( host ); + if ( rc < 0 ) { pthread_rwlock_unlock( &iscsi_globvec_rwlock ); return; } - pthread_rwlock_wrlock( &iscsi_globvec->portal_groups_rwlock ); + iscsi_connection *conn = iscsi_connection_create( add_portals.portal, sock ); - rc = iscsi_portal_group_add_portal( portal_group, portal ); + if ( conn == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI connection" ); - if ( rc < 0 ) { - pthread_rwlock_unlock( &iscsi_globvec->portal_groups_rwlock ); - iscsi_portal_destroy( portal ); - iscsi_hashmap_key_destroy( (uint8_t *) hash_key ); - free( host ); + free( add_portals.host ); + free( add_portals.port ); pthread_rwlock_unlock( &iscsi_globvec_rwlock ); return; } - iscsi_hashmap_key_destroy( (uint8_t *) hash_key ); - free( host ); - - iscsi_connection *conn = iscsi_connection_create( portal, client->sock ); + conn->target_host = (uint8_t *) strdup( (char *) add_portals.host ); - if ( conn == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI connection" ); + if ( conn->target_host == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI connection target host name" ); - iscsi_portal_group_del_portal( portal_group, portal ); - pthread_rwlock_unlock( &iscsi_globvec->portal_groups_rwlock ); - iscsi_portal_destroy( portal ); + iscsi_connection_destroy( conn ); + free( add_portals.host ); + free( add_portals.port ); pthread_rwlock_unlock( &iscsi_globvec_rwlock ); return; } + conn->portal_host = add_portals.host; + conn->portal_port = add_portals.port; conn->pdu_processing = iscsi_connection_pdu_create( conn, 0U, 0, 0UL, 0 ); if ( conn->pdu_processing == NULL ) { iscsi_connection_destroy( conn ); - iscsi_portal_group_del_portal( portal_group, portal ); - pthread_rwlock_unlock( &iscsi_globvec->portal_groups_rwlock ); - iscsi_portal_destroy( portal ); pthread_rwlock_unlock( &iscsi_globvec_rwlock ); return; } - pthread_rwlock_unlock( &iscsi_globvec->portal_groups_rwlock ); - memcpy( conn->pdu_processing->bhs_pkt, request, len ); conn->pdu_processing->bhs_pos = len; + conn->id = atomic_fetch_add_explicit( &iscsi_globvec->connections, 1, memory_order_acq_rel ); conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_HDR; logadd( LOG_INFO, "" ); - logadd( LOG_INFO, "iSCSI connection opened for device %s from initiator %s using port %s and portal %s:%s", (conn->device != NULL ? (char *) conn->device->name : "(null)"), (char *) conn->init_name, ((conn->init_port != NULL) ? (char *) conn->init_port->name : "(null)"), (char *) portal->host, (char *) portal->port ); + logadd( LOG_INFO, "iSCSI connection opened for device %s from initiator %s using port %s and portal %s:%s", (conn->device != NULL ? (char *) conn->device->name : "(null)"), (char *) conn->init_name, ((conn->init_port != NULL) ? (char *) conn->init_port->name : "(null)"), (char *) add_portals.portal->host, (char *) add_portals.portal->port ); while ( iscsi_connection_pdu_handle( conn ) >= ISCSI_CONNECT_PDU_READ_OK ) { } iscsi_hashmap_bucket *stat_bucket; - logadd( LOG_INFO, "iSCSI connection closed for device %s from initiator %s using port %s and portal %s:%s", (conn->device != NULL ? (char *) conn->device->name : "(null)"), (char *) conn->init_name, ((conn->init_port != NULL) ? (char *) conn->init_port->name : "(null)"), (char *) portal->host, (char *) portal->port ); + logadd( LOG_INFO, "iSCSI connection closed for device %s from initiator %s using port %s and portal %s:%s", (conn->device != NULL ? (char *) conn->device->name : "(null)"), (char *) conn->init_name, ((conn->init_port != NULL) ? (char *) conn->init_port->name : "(null)"), (char *) add_portals.portal->host, (char *) add_portals.portal->port ); iscsi_list_foreach_node ( &conn->stat_iscsi_opcodes->list, stat_bucket ) { uint64_t *stat_opcode = (uint64_t *) stat_bucket->value; - logadd( LOG_INFO, "iSCSI opcode usage statistics for device %s from initiator %s using port %s and portal %s:%s: Opcode 0x%02" PRIX64 " has been received %" PRIu64 " times until connection drop.", (conn->device != NULL ? (char *) conn->device->name : "(null)"), (char *) conn->init_name, ((conn->init_port != NULL) ? (char *) conn->init_port->name : "(null)"), (char *) portal->host, (char *) portal->port, *(uint64_t *) stat_bucket->key, *stat_opcode ); + logadd( LOG_INFO, "iSCSI opcode usage statistics for device %s from initiator %s using port %s and portal %s:%s: Opcode 0x%02" PRIX64 " has been received %" PRIu64 " times until connection drop.", (conn->device != NULL ? (char *) conn->device->name : "(null)"), (char *) conn->init_name, ((conn->init_port != NULL) ? (char *) conn->init_port->name : "(null)"), (char *) add_portals.portal->host, (char *) add_portals.portal->port, *(uint64_t *) stat_bucket->key, *stat_opcode ); } iscsi_list_foreach_node ( &conn->stat_scsi_opcodes->list, stat_bucket ) { uint64_t *stat_opcode = (uint64_t *) stat_bucket->value; - logadd( LOG_INFO, "iSCSI SCSI CDB opcode usage statistics for device %s from initiator %s using port %s and portal %s:%s: SCSI CDB opcode 0x%02" PRIX64 " has been received %" PRIu64 " times until connection drop.", (conn->device != NULL ? (char *) conn->device->name : "(null)"), (char *) conn->init_name, ((conn->init_port != NULL) ? (char *) conn->init_port->name : "(null)"), (char *) portal->host, (char *) portal->port, *(uint64_t *) stat_bucket->key, *stat_opcode ); + logadd( LOG_INFO, "iSCSI SCSI CDB opcode usage statistics for device %s from initiator %s using port %s and portal %s:%s: SCSI CDB opcode 0x%02" PRIX64 " has been received %" PRIu64 " times until connection drop.", (conn->device != NULL ? (char *) conn->device->name : "(null)"), (char *) conn->init_name, ((conn->init_port != NULL) ? (char *) conn->init_port->name : "(null)"), (char *) add_portals.portal->host, (char *) add_portals.portal->port, *(uint64_t *) stat_bucket->key, *stat_opcode ); } iscsi_session *session = conn->session; @@ -14579,13 +15139,13 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ conn->device = NULL; const uint key_len = (uint) (strlen( (char *) device->name ) + 1U); - hash_key = (uint64_t *) iscsi_hashmap_key_create( device->name, key_len ); + uint8_t *hash_key = iscsi_hashmap_key_create( device->name, key_len ); if ( hash_key != NULL ) { target->device = NULL; - iscsi_hashmap_remove_free( iscsi_globvec->devices, (uint8_t *) hash_key, key_len, iscsi_device_destroy_callback, NULL ); - iscsi_hashmap_key_destroy( (uint8_t *) hash_key ); + iscsi_hashmap_remove_free( iscsi_globvec->devices, hash_key, key_len, iscsi_device_destroy_callback, NULL ); + iscsi_hashmap_key_destroy( hash_key ); } } @@ -14601,11 +15161,11 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ conn->target = NULL; const uint key_len = (uint) (strlen( (char *) target->name ) + 1U); - hash_key = (uint64_t *) iscsi_hashmap_key_create( target->name, key_len ); + uint8_t *hash_key = iscsi_hashmap_key_create( target->name, key_len ); if ( hash_key != NULL ) { - iscsi_hashmap_remove_free( iscsi_globvec->target_nodes, (uint8_t *) hash_key, key_len, iscsi_target_node_destroy_callback, NULL ); - iscsi_hashmap_key_destroy( (uint8_t *) hash_key ); + iscsi_hashmap_remove_free( iscsi_globvec->target_nodes, hash_key, key_len, iscsi_target_node_destroy_callback, NULL ); + iscsi_hashmap_key_destroy( hash_key ); } } @@ -14615,10 +15175,5 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ iscsi_connection_destroy( conn ); - pthread_rwlock_wrlock( &iscsi_globvec->portal_groups_rwlock ); - iscsi_portal_group_del_portal( portal_group, portal ); - iscsi_portal_destroy( portal ); - pthread_rwlock_unlock( &iscsi_globvec->portal_groups_rwlock ); - pthread_rwlock_unlock( &iscsi_globvec_rwlock ); } diff --git a/src/server/iscsi.h b/src/server/iscsi.h index 29121c6..4d825f4 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -776,7 +776,6 @@ void iscsi_hashmap_destroy(iscsi_hashmap *map); // Deallocates the hash map obje // Use iscsi_hashmap_iterate to deallocate the elements themselves uint8_t *iscsi_hashmap_key_create(const uint8_t *data, const size_t len); // Creates a key suitable for hashmap usage (ensures 8-byte boundary and zero padding) -void iscsi_hashmap_key_create_id(iscsi_hashmap *map, uint64_t *key); // Creates an unique key identifier suitable for hashmap usage (ensures 8-byte boundary and zero padding) void iscsi_hashmap_key_destroy(uint8_t *key); // Deallocates all resources acquired by iscsi_hashmap_create_key int iscsi_hashmap_key_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Deallocates a key in a hash map int iscsi_hashmap_destroy_value_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Deallocates a value in a hash map @@ -6026,9 +6025,6 @@ typedef struct __attribute__((packed)) iscsi_r2t_packet { /// MaxCmdSN. uint32_t max_cmd_sn; - /// DataSN. - uint32_t data_sn; - /// Ready To Transfer Sequence Number (R2TSN) is the R2T PDU input PDU number within the command identified by the Initiator Task Tag. For bidirectional commands, R2T and Data-In PDUs share the input PDU numbering sequence. uint32_t r2t_sn; @@ -6488,7 +6484,10 @@ typedef struct __attribute__((packed)) iscsi_text_response_packet { uint32_t max_cmd_sn; /// Reserved for future usage, always MUST be 0. - uint64_t reserved2[2]; + uint64_t reserved2; + + /// Reserved for future usage, always MUST be 0. + uint32_t reserved3; /// Optional header digest. iscsi_header_digest hdr_digest; @@ -9340,7 +9339,7 @@ typedef struct __attribute__((packed)) iscsi_snack_req_packet { uint32_t exp_stat_sn; /// Reserved for future usage, always MUST be 0. - uint32_t reserved3; + uint64_t reserved3; /** * @brief BegRun. @@ -9938,6 +9937,9 @@ int iscsi_parse_key_value_pairs(iscsi_hashmap *key_value_pairs, const uint8_t *p #define ISCSI_GLOBALS_SECTION_SCSI "scsi" +/// iSCSI main global data: iSCSI INI configuration iSCSI section portal group tag key identifier string. +#define ISCSI_GLOBALS_SECTION_ISCSI_KEY_PORTAL_GROUP_TAG "PortalGroupTag" + /// iSCSI main global data: iSCSI INI configuration iSCSI section target name check key identifier string. #define ISCSI_GLOBALS_SECTION_ISCSI_KEY_TARGET_NAME_CHECK "TargetNameCheck" @@ -9976,96 +9978,101 @@ int iscsi_parse_key_value_pairs(iscsi_hashmap *key_value_pairs, const uint8_t *p #define ISCSI_GLOBALS_SECTION_SCSI_KEY_WRITE_CACHE "WriteCache" +/// iSCSI main global data: iSCSI portal group specific INI configuration section prefix identifier string. +#define ISCSI_GLOBALS_SECTION_ISCSI_PORTAL_GROUP_PREFIX "iscsi-portal-group-" + + +/// iSCSI main global data: iSCSI INI configuration iSCSI section portal group tag key identifier string. +#define ISCSI_GLOBALS_SECTION_ISCSI_PORTAL_GROUP_KEY_ACCESS "Access" + + /// iSCSI main global data: iSCSI SCSI device specific INI configuration section prefix identifier string. #define ISCSI_GLOBALS_SECTION_SCSI_DEVICE_PREFIX "scsi-device-" +/// iSCSI main global data config type: Portal group tag. +#define ISCSI_GLOBALS_CONFIG_TYPE_PORTAL_GROUP_TAG 0 + /// iSCSI main global data config type: Header digest (CRC32), always MUST be 0 or 4 for now. -#define ISCSI_GLOBALS_CONFIG_TYPE_HEADER_DIGEST 0 +#define ISCSI_GLOBALS_CONFIG_TYPE_HEADER_DIGEST 1 /// iSCSI main global data config type: Data digest (CRC32), always MUST be 0 or 4 for now. -#define ISCSI_GLOBALS_CONFIG_TYPE_DATA_DIGEST 1 +#define ISCSI_GLOBALS_CONFIG_TYPE_DATA_DIGEST 2 /// iSCSI main global data config type: Maximum receive DataSegment length in bytes. -#define ISCSI_GLOBALS_CONFIG_TYPE_MAX_RECV_DS_LEN 2 +#define ISCSI_GLOBALS_CONFIG_TYPE_MAX_RECV_DS_LEN 3 /// iSCSI main global data config type: Maximum number of connections per session. -#define ISCSI_GLOBALS_CONFIG_TYPE_MAX_SESSION_CONNS 3 +#define ISCSI_GLOBALS_CONFIG_TYPE_MAX_SESSION_CONNS 4 /// iSCSI main global data config type: Ready to transfer maximum outstanding value. -#define ISCSI_GLOBALS_CONFIG_TYPE_MAX_OUTSTANDING_R2T 4 +#define ISCSI_GLOBALS_CONFIG_TYPE_MAX_OUTSTANDING_R2T 5 /// iSCSI main global data config type: Default time to wait. -#define ISCSI_GLOBALS_CONFIG_TYPE_DEFAULT_TIME_TO_WAIT 5 +#define ISCSI_GLOBALS_CONFIG_TYPE_DEFAULT_TIME_TO_WAIT 6 /// iSCSI main global data config type: Default time to retain. -#define ISCSI_GLOBALS_CONFIG_TYPE_DEFAULT_TIME_TO_RETAIN 6 +#define ISCSI_GLOBALS_CONFIG_TYPE_DEFAULT_TIME_TO_RETAIN 7 /// iSCSI main global data config type: First burst length. -#define ISCSI_GLOBALS_CONFIG_TYPE_FIRST_BURST_LEN 7 +#define ISCSI_GLOBALS_CONFIG_TYPE_FIRST_BURST_LEN 8 /// iSCSI main global data config type: Maximum burst length. -#define ISCSI_GLOBALS_CONFIG_TYPE_MAX_BURST_LEN 8 +#define ISCSI_GLOBALS_CONFIG_TYPE_MAX_BURST_LEN 9 /// iSCSI main global data config type: Error recovery level. -#define ISCSI_GLOBALS_CONFIG_TYPE_ERR_RECOVERY_LEVEL 9 +#define ISCSI_GLOBALS_CONFIG_TYPE_ERR_RECOVERY_LEVEL 10 /// iSCSI main global data config type: CHAP authentication group. -#define ISCSI_GLOBALS_CONFIG_TYPE_CHAP_GROUP 10 +#define ISCSI_GLOBALS_CONFIG_TYPE_CHAP_GROUP 11 /// iSCSI main global data config type: SCSI emulation for device type. -#define ISCSI_GLOBALS_CONFIG_TYPE_SCSI_DEVICE_TYPE 11 +#define ISCSI_GLOBALS_CONFIG_TYPE_SCSI_DEVICE_TYPE 12 /// iSCSI main global data config type: SCSI emulation for physical block size. -#define ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE 12 - -/// iSCSI main global data config type: SCSI emulation for physical block size shift count. -#define ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE_SHIFT 13 +#define ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE 13 /// iSCSI main global data config type: SCSI emulation for logical block size. #define ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE 14 -/// iSCSI main global data config type: SCSI emulation for logical block size shift count. -#define ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE_SHIFT 15 - /// iSCSI main global data config type: CHAP authentication is disabled. -#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_CHAP_DISABLE 16 +#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_CHAP_DISABLE 15 /// iSCSI main global data config type: CHAP authentication is required. -#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_CHAP_REQUIRE 17 +#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_CHAP_REQUIRE 16 /// iSCSI main global data config type: CHAP authentication is mutual. -#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_CHAP_MUTUAL 18 +#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_CHAP_MUTUAL 17 /// iSCSI main global data config type: Initial ready to transfer. -#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_INIT_R2T 19 +#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_INIT_R2T 18 /// iSCSI main global data config type: Immediate data. -#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_IMMEDIATE_DATA 20 +#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_IMMEDIATE_DATA 19 /// iSCSI main global data config type: Data PDU in order. -#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_DATA_PDU_IN_ORDER 21 +#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_DATA_PDU_IN_ORDER 20 /// iSCSI main global data config type: Data sequence in order. -#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_DATA_SEQ_IN_ORDER 22 +#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_DATA_SEQ_IN_ORDER 21 /// iSCSI main global data config type: SCSI emulation for I/O removable device. -#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_REMOVABLE 23 +#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_REMOVABLE 22 /// iSCSI main global data config type: SCSI emulation for I/O UNMAP supporting device. -#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_UNMAP 24 +#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_UNMAP 23 /// iSCSI main global data config type: SCSI emulation for I/O non-rotating device. -#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_NO_ROTATION 25 +#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_NO_ROTATION 24 /// iSCSI main global data config type: SCSI emulation for I/O physical read only device. -#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_PHYSICAL_READ_ONLY 26 +#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_PHYSICAL_READ_ONLY 25 /// iSCSI main global data config type: SCSI emulation for I/O write protected device. -#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_WRITE_PROTECT 27 +#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_WRITE_PROTECT 26 /// iSCSI main global data config type: SCSI emulation for I/O write cache device. -#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_WRITE_CACHE 28 +#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_WRITE_CACHE 27 /// iSCSI main global data config type: Value error result. @@ -10123,6 +10130,9 @@ typedef struct iscsi_scsi_device_config { /// SCSI emulation: Device type. uint scsi_device_type; + /// Default portal group tag; + uint32_t pg_tag; + /// Maximum receive DataSegment length in bytes. uint32_t max_recv_ds_len; @@ -10150,14 +10160,8 @@ typedef struct iscsi_scsi_device_config { /// SCSI emulation: Physical block size. uint32_t scsi_physical_block_size; - /// SCSI emulation: Physical block size shift count. - uint32_t scsi_physical_block_size_shift; - /// SCSI emulation: Logical block size. uint32_t scsi_logical_block_size; - - /// SCSI emulation: Logical block size shift count. - uint32_t scsi_logical_block_size_shift; } iscsi_scsi_device_config; @@ -10315,6 +10319,12 @@ typedef struct iscsi_globals { /// SCSI emulation: Device type. uint scsi_device_type; + /// Total number of connections so far. + _Atomic int connections; + + /// Default portal group tag; + uint32_t pg_tag; + /// Maximum receive DataSegment length in bytes. uint32_t max_recv_ds_len; @@ -10345,14 +10355,8 @@ typedef struct iscsi_globals { /// SCSI emulation: Physical block size. uint32_t scsi_physical_block_size; - /// SCSI emulation: Physical block size shift count. - uint32_t scsi_physical_block_size_shift; - /// SCSI emulation: Logical block size. uint32_t scsi_logical_block_size; - - /// SCSI emulation: Logical block size shift count. - uint32_t scsi_logical_block_size_shift; } iscsi_globals; @@ -10372,7 +10376,7 @@ int32_t iscsi_config_get(uint8_t *name, const int type); // Retrieves a configur /** - * @brief iSCSI portal group: Private portal group if set, public otherwise. + * @brief iSCSI portal group flags: Private portal group if set, public otherwise. * * When redirecting logins, there are two portal group types: public and * private.\n @@ -10382,16 +10386,16 @@ int32_t iscsi_config_get(uint8_t *name, const int type); // Retrieves a configur * Private portal groups instead do not return their portals during * the discovery session. */ -#define ISCSI_PORTAL_GROUP_PRIVATE (1 << 0) +#define ISCSI_PORTAL_GROUP_FLAGS_PRIVATE (1 << 0) -/// iSCSI portal group: CHAP authentication is disabled. -#define ISCSI_PORTAL_GROUP_CHAP_DISABLE (1 << 1) +/// iSCSI portal group flags: CHAP authentication is disabled. +#define ISCSI_PORTAL_GROUP_FLAGS_CHAP_DISABLE (1 << 1) -/// iSCSI portal group: CHAP authentication is required. -#define ISCSI_PORTAL_GROUP_CHAP_REQUIRE (1 << 2) +/// iSCSI portal group flags: CHAP authentication is required. +#define ISCSI_PORTAL_GROUP_FLAGS_CHAP_REQUIRE (1 << 2) -/// iSCSI portal group: CHAP authentication is mutual. -#define ISCSI_PORTAL_GROUP_CHAP_MUTUAL (1 << 3) +/// iSCSI portal group flags: CHAP authentication is mutual. +#define ISCSI_PORTAL_GROUP_FLAGS_CHAP_MUTUAL (1 << 3) /** @@ -10418,6 +10422,19 @@ typedef struct iscsi_portal_group { } iscsi_portal_group; +/// iSCSI portal type: None. +#define ISCSI_PORTAL_TYPE_NONE 0U + +/// iSCSI portal type: Local (unix) socket. +#define ISCSI_PORTAL_TYPE_SOCK 1U + +/// iSCSI portal type: IPv4 socket. +#define ISCSI_PORTAL_TYPE_IPV4 2U + +/// iSCSI portal type: IPv6 socket. +#define ISCSI_PORTAL_TYPE_IPV6 3U + + /** * @brief iSCSI portal. * @@ -10434,7 +10451,10 @@ typedef struct iscsi_portal { /// Port of the portal. uint8_t *port; - /// TCP/IP socket for the portal. + /// Socket type. + uint type; + + /// Socket file descriptor for the portal. int sock; } iscsi_portal; @@ -10445,7 +10465,7 @@ void iscsi_portal_group_destroy(iscsi_portal_group *portal_group); // Deallocate int iscsi_portal_group_add_portal(iscsi_portal_group *portal_group, iscsi_portal *portal); // Adds an iSCSI portal to the iSCSI portal group hash map void iscsi_portal_group_del_portal(iscsi_portal_group *portal_group, iscsi_portal *portal); // Removes an iSCSI portal from the iSCSI portal group hash map -iscsi_portal *iscsi_portal_create(const uint8_t *host, const uint8_t *port); // Allocates and initializes an iSCSI portal structure +iscsi_portal *iscsi_portal_create(const uint8_t *host, const uint8_t *port, const uint type); // Allocates and initializes an iSCSI portal structure int iscsi_portal_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // iSCSI portal destructor callback for hash map void iscsi_portal_destroy(iscsi_portal *portal); @@ -11171,8 +11191,8 @@ typedef struct iscsi_target_node { /// Associated iSCSI device. iscsi_device *device; - /// Target node number. - uint num; + /// Portal group tag associated with this target node. + uint64_t pg_tag; /// Queue depth. uint queue_depth; @@ -11194,6 +11214,45 @@ typedef struct iscsi_target_node { } iscsi_target_node; +/** + * @brief iSCSI target node send by name. + * + * This structure is used by iterating through + * all iSCSI target nodes sending by name. + */ +typedef struct iscsi_target_node_send_name { + /// iSCSI connection sending the target nodes. + iscsi_connection *conn; + + /// The IQN of the target node to send. NULL matches all target nodes. + uint8_t *dst_iqn; + + /// The initiator IQN to send. + uint8_t *src_iqn; + + /// Copy buffer for key and value pairs. + uint8_t *tmp_buf; + + /// Buffer where to store the key and value pairs of target nodes to send. + uint8_t *buf; + + /// Position of buffer in bytes where to start filling key and value pairs of target nodes to send. + uint32_t pos; + + /// Length of buffer in bytes where to start filling key and value pairs of target nodes to send. + uint32_t len; + + /// Length of copy buffer in bytes for key and value pairs. + uint32_t tmp_len; + + /// Previous iSCSI SendTargets total number of bytes completed. + uint32_t prev_target_send_total_size; + + /// True if the submitted buffer would overflow. + bool buf_full; +} iscsi_target_node_send_name; + + /** * @brief iSCSI target node search by name. * @@ -11458,6 +11517,9 @@ typedef struct iscsi_connection { /// iSCSI target short name. uint8_t *target_name_short; + /// iSCSI target host name. + uint8_t *target_host; + /// iSCSI portal host name. uint8_t *portal_host; @@ -11485,9 +11547,6 @@ typedef struct iscsi_connection { /// Doubly linked list containing queued Ready To Transfer (R2T) tasks. iscsi_list r2t_tasks_queue; - /// iSCSI SendTargets total number of bytes completed. - uint target_send_total_size; - /// iSCSI SCSI Data In count. uint scsi_data_in_cnt; @@ -11506,7 +11565,7 @@ typedef struct iscsi_connection { /// 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). + /// Internal unique connection identifier. int id; /// Connected TCP/IP socket. @@ -11524,6 +11583,9 @@ typedef struct iscsi_connection { /// iSCSI connection login phase. int login_phase; + /// iSCSI SendTargets total number of bytes completed. + uint32_t target_send_total_size; + /// Maximum receive DataSegment length in bytes. uint32_t max_recv_ds_len; @@ -11845,6 +11907,31 @@ typedef struct iscsi_task { } iscsi_task; +/** + * @brief iSCSI portal group iterator for adding portals. + * + * This structure is used by iterating through + * all iSCSI portal groups and adding all + * portals to each portal group. + */ +typedef struct iscsi_connection_handle_add_portals { + /// DNBD3 client socket to be used for a specified iSCSI connection. + int sock; + + /// iSCSI portal which contains the portal to be used for a specified iSCSI connection. + iscsi_portal *portal; + + /// Host IP address string to be used for a specified iSCSI connection. + uint8_t *host; + + /// Port string to be used for a specified iSCSI connection. + uint8_t *port; + + /// iSCSI portal group or portals have been added and write lock is necessary. + bool write_locked; +} iscsi_connection_handle_add_portals; + + iscsi_task *iscsi_task_create(iscsi_connection *conn, iscsi_task *parent, iscsi_scsi_task_xfer_complete_callback callback); // Allocates and initializes an iSCSI task structure void iscsi_task_destroy_callback(iscsi_scsi_task *scsi_task); // Deallocates all resources of the iSCSI task of an iSCSI SCSI task void iscsi_task_destroy(iscsi_task *task); // Deallocates resources acquired by iscsi_task_create @@ -11869,11 +11956,13 @@ int iscsi_device_port_add(iscsi_device *device, const uint8_t *name, const uint6 void iscsi_device_scsi_task_queue(iscsi_device *device, iscsi_scsi_task *scsi_task); // Enqueues an iSCSI SCSI task to the first LUN of an iSCSI device int iscsi_target_node_create_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Creates, initializes and adds a portal group to an iSCSI target node -iscsi_target_node *iscsi_target_node_create(uint8_t *name, const uint8_t *alias, const int index, const int lun_id, const uint queue_depth, const int flags, const int32_t chap_group, const int header_digest, const int data_digest); // Creates and initializes an iSCSI target node +iscsi_target_node *iscsi_target_node_create(uint8_t *name, const uint8_t *alias, const uint64_t pg_tag, const int lun_id, const uint queue_depth, const int flags, const int32_t chap_group, const int header_digest, const int data_digest); // Creates and initializes an iSCSI target node int iscsi_target_node_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // iSCSI target node destructor callback for hash map void iscsi_target_node_destroy(iscsi_target_node *target); // Deallocates all resources acquired by iscsi_target_node_create -int32_t iscsi_target_node_send(iscsi_connection *conn, const uint8_t *dst_iqn, const uint8_t *src_iqn, uint8_t *buf, const uint32_t pos, const uint32_t len); // Sends a buffer from a source iSCSI IQN to target iSCSI IQNs +int iscsi_target_node_portals_send_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Sends an iSCSI portal for iSCSI discovery to a buffer +int iscsi_target_node_send_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Sends an iSCSI discovery source target node IQN for a specific target IQN to a buffer +int32_t iscsi_target_node_send(iscsi_connection *conn, uint8_t *dst_iqn, uint8_t *src_iqn, uint8_t *buf, const uint32_t pos, const uint32_t len); // Sends a target node discovery source iSCSI IQN to a buffer for target IQNs uint64_t iscsi_target_node_wwn_get(const uint8_t *name); // Calculates the WWN using 64-bit IEEE Extended NAA for a name dnbd3_image_t *iscsi_target_node_image_get(uint8_t *iqn); // Extracts the DNBD3 image out of an iSCSI IQN string and opens the DNBD3 image iscsi_device *iscsi_target_node_device_attach(iscsi_target_node *target, uint8_t *target_name); // Creates a new iSCSI device and attaches it to a specified iSCSI target node if no device was connected yet @@ -11928,6 +12017,7 @@ int iscsi_connection_read_iov_data(iscsi_connection *conn, struct iovec *iov, in 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 +int iscsi_connection_handle_add_portals_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Adds iSCSI portals to a specified iSCSI portal group void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *request, const int len); // Handles an iSCSI connection until connection is closed #ifdef __cplusplus -- cgit v1.2.3-55-g7522