diff options
| author | Sebastian Vater | 2025-08-19 10:36:01 +0200 |
|---|---|---|
| committer | Sebastian Vater | 2025-08-19 10:36:01 +0200 |
| commit | 196c02500f9f52ddd2fd4aca16fc83a8972f91f0 (patch) | |
| tree | 2a1335d3930865af41af484a08246bceec19582a | |
| parent | Finished iSCSI login handling. Also fixed some bugs and simplified certain st... (diff) | |
| download | dnbd3-196c02500f9f52ddd2fd4aca16fc83a8972f91f0.tar.gz dnbd3-196c02500f9f52ddd2fd4aca16fc83a8972f91f0.tar.xz dnbd3-196c02500f9f52ddd2fd4aca16fc83a8972f91f0.zip | |
Added iSCSI NOP-In and NOP-Out implementation for keep alive. Also increased memory size alignment to 16 bytes for safe manipulation of integer values in order to prevent buffer overflows for key and value pair handling. Finally, again fixed some bugs.
| -rw-r--r-- | src/server/iscsi.c | 114 | ||||
| -rw-r--r-- | src/server/iscsi.h | 50 |
2 files changed, 130 insertions, 34 deletions
diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 51b9c7d..bfda85b 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -43,7 +43,7 @@ * @see https://www.rfc-editor.org/rfc/rfc7143 */ -/// iSCSI connection key and value pair lookup table. +/// iSCSI connection negotation key and value pair lookup table. static const iscsi_key_value_pair_lut_entry iscsi_connection_key_value_pair_lut[] = { { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_HEADER_DIGEST, (uint8_t *) "None", (uint8_t *) "CRC32C\0None\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST, 0L }, { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST, (uint8_t *) "None", (uint8_t *) "CRC32C\0None\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST, 0L }, @@ -61,7 +61,7 @@ static const iscsi_key_value_pair_lut_entry iscsi_connection_key_value_pair_lut[ { NULL, NULL, NULL, ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_INVALID, 0L } }; -/// iSCSI session key and value pair lookup table. +/// iSCSI session negotation key and value pair lookup table. static const iscsi_key_value_pair_lut_entry iscsi_session_key_value_pair_lut[] = { { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS, (uint8_t *) "1", (uint8_t *) "1\065535\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE }, { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_SEND_TARGETS, (uint8_t *) "", (uint8_t *) "\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_SPECIAL_HANDLING }, @@ -1179,7 +1179,7 @@ iscsi_bhs_packet *iscsi_append_ahs_packet(iscsi_bhs_packet *packet_data, const u const uint32_t old_pkt_size = (const uint32_t) sizeof(struct iscsi_bhs_packet) + (packet_data->total_ahs_len << 2UL); const uint32_t new_pkt_size = (uint32_t) (old_pkt_size + iscsi_align(ahs_len, ISCSI_ALIGN_SIZE)); - if ( new_pkt_size > (sizeof(struct iscsi_bhs_packet) + 1020UL) ) { + if ( new_pkt_size > (sizeof(struct iscsi_bhs_packet) + ISCSI_MAX_AHS_SIZE) ) { logadd( LOG_ERROR, "iscsi_append_ahs_packet: Total numer of AHS packet size exceeds 255 DWORDs" ); return NULL; @@ -1938,7 +1938,7 @@ static int iscsi_parse_text_key_value_pair(iscsi_hashmap *pairs, const uint8_t * return -1L; } - uint8_t *hash_val = (uint8_t *) malloc( iscsi_align(val_len + 1UL, ISCSI_HASHMAP_KEY_ALIGN) ); + uint8_t *hash_val = (uint8_t *) malloc( iscsi_align(val_len + 1UL, ISCSI_HASHMAP_VALUE_ALIGN) ); if ( hash_val == NULL ) { logadd( LOG_ERROR, "iscsi_parse_text_key_value_pair: Out of memory allocating memory for value string" ); @@ -2100,7 +2100,7 @@ static int iscsi_add_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_ } const uint val_len = (uint) strlen( (char *) value ) + 1; - uint8_t *hash_val = (uint8_t *) malloc( iscsi_align(val_len, ISCSI_HASHMAP_KEY_ALIGN) ); + uint8_t *hash_val = (uint8_t *) malloc( iscsi_align(val_len, ISCSI_HASHMAP_VALUE_ALIGN) ); if ( hash_val == NULL ) { logadd( LOG_ERROR, "iscsi_add_key_value_pair: Out of memory allocating string value" ); @@ -2144,7 +2144,7 @@ static int iscsi_update_key_value_pair(iscsi_hashmap *key_value_pairs, const uin } const uint val_len = (uint) strlen( (char *) value ) + 1; - uint8_t *hash_val = (uint8_t *) malloc( iscsi_align(val_len, ISCSI_HASHMAP_KEY_ALIGN) ); + uint8_t *hash_val = (uint8_t *) malloc( iscsi_align(val_len, ISCSI_HASHMAP_VALUE_ALIGN) ); if ( hash_val == NULL ) { logadd( LOG_ERROR, "iscsi_update_key_value_pair: Out of memory allocating string value" ); @@ -3063,6 +3063,7 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock) conn->data_digest = 0L; conn->pdu_processing = NULL; conn->login_response_pdu = NULL; + conn->id = 0L; conn->sock = sock; conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_READY; conn->flags = 0L; @@ -3653,7 +3654,7 @@ int iscsi_negotiate_key_value_pair_callback(uint8_t *key, const size_t key_size, max_burst_len = (rc < 0) ? ISCSI_SESSION_DEFAULT_MAX_BURST_LEN : (uint) atol( (char *) max_burst_len_val ); - if ( (first_burst_len < 16777215) && (first_burst_len > max_burst_len) ) + if ( (first_burst_len < ISCSI_MAX_DS_SIZE) && (first_burst_len > max_burst_len) ) sprintf( (char *) value, "%d", first_burst_len ); } @@ -3901,7 +3902,7 @@ static int iscsi_connection_update_key_value_pairs(iscsi_connection *conn) else if ( recv_buf_len > 8192 ) recv_buf_len = 8192UL; - recv_buf_len += (uint) (sizeof(struct iscsi_bhs_packet) + 1020UL + conn->header_digest + conn->data_digest); // BHS + maximum AHS size + header and data digest overhead + recv_buf_len += (uint) (sizeof(struct iscsi_bhs_packet) + ISCSI_MAX_AHS_SIZE + conn->header_digest + conn->data_digest); // BHS + maximum AHS size + header and data digest overhead recv_buf_len <<= 2UL; // Receive up to four streams at once. setsockopt( conn->sock, SOL_SOCKET, SO_RCVBUF, &recv_buf_len, sizeof(recv_buf_len)); // Not being able to set the buffer is NOT fatal, so ignore error. @@ -4570,20 +4571,20 @@ static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn ); if ( response_pdu == NULL ) { - logadd( LOG_ERROR, "iscsi_pdu_create: Out of memory while allocating iSCSI reject response PDU" ); + logadd( LOG_ERROR, "iscsi_connection_handle_reject: Out of memory while allocating iSCSI reject response PDU" ); - return -1L; + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } const uint32_t ds_len = (uint32_t) sizeof(struct iscsi_bhs_packet) + ((uint32_t) pdu->bhs_pkt->total_ahs_len << 2UL) + conn->header_digest; - iscsi_reject_packet *reject_pkt = (iscsi_reject_packet *) iscsi_append_ds_packet( pdu->bhs_pkt, conn->header_digest, ds_len, conn->data_digest ); + iscsi_reject_packet *reject_pkt = (iscsi_reject_packet *) iscsi_append_ds_packet( response_pdu->bhs_pkt, conn->header_digest, ds_len, conn->data_digest ); if ( reject_pkt == NULL ) { - logadd( LOG_ERROR, "iscsi_pdu_create: Out of memory while allocating iSCSI reject packet data" ); + logadd( LOG_ERROR, "iscsi_connection_handle_reject: Out of memory while allocating iSCSI reject packet data" ); iscsi_connection_pdu_destroy( response_pdu ); - return -1L; + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; } response_pdu->bhs_pkt = (iscsi_bhs_packet *) reject_pkt; @@ -4620,7 +4621,7 @@ static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu iscsi_connection_pdu_write( conn, response_pdu, NULL, NULL ); - return 0L; + return ISCSI_CONNECT_PDU_READ_OK; } /** @@ -4706,9 +4707,23 @@ static int iscsi_connection_pdu_header_handle_login_req(iscsi_connection *conn, */ static int iscsi_connection_pdu_header_handle_nop_out(iscsi_connection *conn, iscsi_pdu *pdu) { - // TODO: Implement opcode. + if ( conn->session->type == ISCSI_SESSION_TYPE_DISCOVERY ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - return 0; + if ( pdu->ds_len > ISCSI_DEFAULT_MAX_RECV_DS_LEN ) + return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); + + iscsi_nop_out_packet *nop_out_pkt = (iscsi_nop_out_packet *) pdu->bhs_pkt; + const uint32_t init_task_tag = iscsi_get_be32(nop_out_pkt->init_task_tag); + const uint32_t target_xfer_tag = iscsi_get_be32(nop_out_pkt->target_xfer_tag); + + if ( (target_xfer_tag != 0xFFFFFFFFUL) && (target_xfer_tag != conn->id) ) + return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_INVALID_PDU_FIELD ); // TODO: Check if this is the correct error code. + + if ( (init_task_tag == 0xFFFFFFFFUL) && (nop_out_pkt->opcode & 0x40) == 0 ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + + return 0L; } /** @@ -4950,9 +4965,72 @@ static int iscsi_connection_pdu_header_handle(iscsi_connection *conn, iscsi_pdu */ static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscsi_pdu *pdu) { - // TODO: Implement opcode. + iscsi_nop_out_packet *nop_out_pkt = (iscsi_nop_out_packet *) pdu->bhs_pkt; + uint32_t ds_len = pdu->ds_len; - return 0L; + if ( ds_len > conn->max_recv_ds_len ) + ds_len = conn->max_recv_ds_len; + + const uint64_t lun = iscsi_get_be64(nop_out_pkt->lun); + const uint32_t init_task_tag = iscsi_get_be32(nop_out_pkt->init_task_tag); + + conn->flags &= ~ISCSI_CONNECT_FLAGS_NOP_OUTSTANDING; + + if ( init_task_tag == 0xFFFFFFFFUL ) + return ISCSI_CONNECT_PDU_READ_OK; + + iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn ); + + if ( response_pdu == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_pdu_data_handle_nop_out: Out of memory while allocating iSCSI NOP-In response PDU" ); + + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + } + + iscsi_nop_in_packet *nop_in_pkt = (iscsi_nop_in_packet *) iscsi_append_ds_packet( response_pdu->bhs_pkt, conn->header_digest, ds_len, conn->data_digest ); + + if ( nop_in_pkt == NULL ) { + logadd( LOG_ERROR, "iscsi_connection_pdu_data_handle_nop_out: Out of memory while allocating iSCSI NOP-In packet data" ); + + iscsi_connection_pdu_destroy( response_pdu ); + + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + } + + response_pdu->bhs_pkt = (iscsi_bhs_packet *) nop_in_pkt; + + if ( conn->header_digest != 0 ) { + response_pdu->header_digest = (iscsi_header_digest *) (((iscsi_bhs_packet *) nop_in_pkt) + 1); + response_pdu->header_digest_size = conn->header_digest; + } + + response_pdu->ds_cmd_data = (iscsi_ds_cmd_data *) (((uint8_t *) nop_in_pkt) + sizeof(struct iscsi_bhs_packet) + conn->header_digest); + response_pdu->ds_len = ds_len; + + if ( conn->data_digest != 0 ) { + response_pdu->data_digest = (iscsi_data_digest *) (((uint8_t *) response_pdu->ds_cmd_data) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE)); + response_pdu->data_digest_size = conn->data_digest; + } + + nop_in_pkt->opcode = ISCSI_SERVER_NOP_IN; + nop_in_pkt->flags = -0x80; + iscsi_put_be24( (uint8_t *) &nop_in_pkt->ds_len, ds_len ); + iscsi_put_be64( (uint8_t *) &nop_in_pkt->lun, lun ); + nop_in_pkt->target_xfer_tag = 0xFFFFFFFFUL; + iscsi_put_be32( (uint8_t *) &nop_in_pkt->init_task_tag, init_task_tag ); + iscsi_put_be32( (uint8_t *) &nop_in_pkt->stat_sn, conn->stat_sn++ ); + + if ( (nop_out_pkt->opcode & 0x40) == 0 ) + conn->session->max_cmd_sn++; + + iscsi_put_be32( (uint8_t *) &nop_in_pkt->exp_cmd_sn, conn->session->exp_cmd_sn ); + iscsi_put_be32( (uint8_t *) &nop_in_pkt->max_cmd_sn, conn->session->max_cmd_sn ); + + iscsi_connection_pdu_write( conn, response_pdu, NULL, NULL ); + + // conn->nop_in_last = iscsi_get_ticks(); + + return ISCSI_CONNECT_PDU_READ_OK; } /** diff --git a/src/server/iscsi.h b/src/server/iscsi.h index c61826c..5990d69 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -141,6 +141,12 @@ uint8_t *iscsi_sprintf_alloc(const char *format, ... ); // Allocates a buffer an /// Key data size must be multiple of 8 bytes by now. #define ISCSI_HASHMAP_KEY_ALIGN (1UL << (ISCSI_HASHMAP_KEY_ALIGN_SHIFT)) +/// Value data shift value for alignment enforcement. +#define ISCSI_HASHMAP_VALUE_ALIGN_SHIFT 4UL + +/// Value data size is a multiple of 16 bytes for key and value pairs, which allows us to change their integer values without reallocation of memory. +#define ISCSI_HASHMAP_VALUE_ALIGN (1UL << (ISCSI_HASHMAP_VALUE_ALIGN_SHIFT)) + /// Initial hash code. #define ISCSI_HASHMAP_HASH_INITIAL 0x811C9DC5UL @@ -253,9 +259,21 @@ int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, u /* iSCSI protocol stuff (all WORD/DWORD/QWORD values are big endian by default unless specified otherwise). */ -/// iSCSI Basic Header Segment size. +/// iSCSI Basic Header Segment (BHS) size. #define ISCSI_BHS_SIZE 48UL +/// iSCSI Advanced Header Segment (AHS) maximum allowed size. +#define ISCSI_MAX_AHS_SIZE (255UL << 2UL) + +/// iSCSI DataSegment maximum allowed size. +#define ISCSI_MAX_DS_SIZE 16777215UL + +/// iSCSI packet data alignment (BHS, AHS and DataSegment). +#define ISCSI_ALIGN_SIZE 4UL + +/// iSCSI header and data digest size (CRC32C). +#define ISCSI_DIGEST_SIZE 4UL + /// iSCSI Default receive DataSegment (DS) size in bytes. #define ISCSI_DEFAULT_RECV_DS_LEN 8192UL @@ -267,13 +285,6 @@ int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, u #define ISCSI_DEFAULT_MAX_DATA_OUT_PER_CONNECTION 16UL -/// iSCSI header and data digest size (CRC32C). -#define ISCSI_DIGEST_SIZE 4UL - -/// iSCSI packet data alignment (BHS, AHS and DataSegment). -#define ISCSI_ALIGN_SIZE 4UL - - /// Current minimum iSCSI protocol version supported by this implementation. #define ISCSI_VERSION_MIN 0 @@ -381,6 +392,7 @@ int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, u /// Macro which extracts iSCSI packet data opcode out of opcode byte #define ISCSI_GET_OPCODE(x) ((x) & ISCSI_OPCODE_MASK) + /** * @brief iSCSI Basic Header Segment packet data. * @@ -6271,28 +6283,31 @@ typedef struct iscsi_session { /// iSCSI connection flags: Stopped. -#define ISCSI_CONNECT_FLAGS_STOPPED (1 << 0L) +#define ISCSI_CONNECT_FLAGS_STOPPED (1 << 0L) /// iSCSI connection flags: Rejected. -#define ISCSI_CONNECT_FLAGS_REJECTED (1 << 1L) +#define ISCSI_CONNECT_FLAGS_REJECTED (1 << 1L) /// iSCSI connection flags: Logged out. -#define ISCSI_CONNECT_FLAGS_LOGGED_OUT (1 << 2L) +#define ISCSI_CONNECT_FLAGS_LOGGED_OUT (1 << 2L) /// iSCSI connection flags: Full feature. -#define ISCSI_CONNECT_FLAGS_FULL_FEATURE (1 << 3L) +#define ISCSI_CONNECT_FLAGS_FULL_FEATURE (1 << 3L) /// iSCSI connection flags: CHAP authentication is disabled. -#define ISCSI_CONNECT_FLAGS_CHAP_DISABLE (1 << 4L) +#define ISCSI_CONNECT_FLAGS_CHAP_DISABLE (1 << 4L) /// iSCSI connection flags: CHAP authentication is required. -#define ISCSI_CONNECT_FLAGS_CHAP_REQUIRE (1 << 5L) +#define ISCSI_CONNECT_FLAGS_CHAP_REQUIRE (1 << 5L) /// iSCSI connection flags: CHAP authentication is mutual. -#define ISCSI_CONNECT_FLAGS_CHAP_MUTUAL (1 << 6L) +#define ISCSI_CONNECT_FLAGS_CHAP_MUTUAL (1 << 6L) /// iSCSI connection flags: Authenticated. -#define ISCSI_CONNECT_FLAGS_AUTH (1 << 7L) +#define ISCSI_CONNECT_FLAGS_AUTH (1 << 7L) + +/// iSCSI connection flags: Oustanding NOP. +#define ISCSI_CONNECT_FLAGS_NOP_OUTSTANDING (1 << 8L) /// Ready to wait for PDU. @@ -6378,6 +6393,9 @@ typedef struct iscsi_connection { /// Login response PDU. struct iscsi_pdu *login_response_pdu; + /// Internal connection identifier (key of iSCSI global vector hash map). + int id; + /// Connected TCP/IP socket. int sock; |
