Merge pull request #460 from plieven/feat/verify_sn

feat: read and validate unit serial number after login
This commit is contained in:
Ronnie Sahlberg
2025-09-12 17:32:34 +10:00
committed by GitHub
5 changed files with 133 additions and 20 deletions

View File

@@ -48,10 +48,11 @@ target_password=<password>
header_digest=<crc32c|none>
data_digest=<crc32c|none>
auth=<md5|sha1|sha-256|sha3-256>
force_usn=<unit_serial_number>
Transport:
iser
Example:
iscsi://server/iqn.ronnie.test/1
@@ -193,7 +194,7 @@ To run those tests you would specify
Test discovery
--------------
To discover which tests exist you can use the command
To discover which tests exist you can use the command
iscsi-test-cu --list
Examples

View File

@@ -116,6 +116,7 @@ struct iscsi_context {
char portal[MAX_STRING_SIZE+1];
char alias[MAX_STRING_SIZE+1];
char bind_interfaces[MAX_STRING_SIZE+1];
char unit_serial_number[MAX_STRING_SIZE+1];
enum iscsi_chap_auth chap_auth;
char user[MAX_STRING_SIZE+1];

View File

@@ -198,7 +198,7 @@ iscsi_get_auth(struct iscsi_context *iscsi);
EXTERN void
iscsi_set_auth(struct iscsi_context *iscsi, enum iscsi_chap_auth auth);
/*
* This function is used to parse an iSCSI URL into a iscsi_url structure.
* iSCSI URL format :
@@ -301,6 +301,24 @@ EXTERN int iscsi_set_alias(struct iscsi_context *iscsi, const char *alias);
*/
EXTERN int iscsi_set_targetname(struct iscsi_context *iscsi, const char *targetname);
/*
* Set the unit serial number (usn) as reported by VPD page 0x80.
* If set the usn is validated after logging in and especially after reconnecting
* to a target to avoid accidently mismatch between LUN ids on the same target.
* If not set explicitely the usn is learned at the first successful login to the target.
*
* Returns:
* 0: success
* <0: error
*/
EXTERN int iscsi_set_unit_serial_number(struct iscsi_context *iscsi, const char *usn);
/*
* This function returns a pointer to the unit serial number that is valid if explicitely
* set or after the first successful login to the target.
*/
EXTERN const char *iscsi_get_unit_serial_number(struct iscsi_context *iscsi);
/*
* This function returns any target address supplied in a login response when
* the target has moved.
@@ -685,7 +703,7 @@ EXTERN void iscsi_free_discovery_data(struct iscsi_context *iscsi,
* structure containing the data returned from
* the server.
* SCSI_STATUS_CANCELLED : Discovery was aborted. Command_data is NULL.
*
*
* The callback may be NULL if you only want to let libiscsi count the in-flight
* NOPs.
*/
@@ -1684,7 +1702,7 @@ iscsi_set_noautoreconnect(struct iscsi_context *iscsi, int state);
/* This function is to set if we should retry a failed reconnect
count is defined as follows:
-1 -> retry forever (default)
0 -> never retry
@@ -1707,7 +1725,7 @@ iscsi_set_fd_dup_cb(struct iscsi_context *iscsi,
/*
* MULTITHREADING
*/
*/
/*
* This function starts a separate service thread for multithreading support.
*/
@@ -1716,7 +1734,7 @@ EXTERN int iscsi_mt_service_thread_start(struct iscsi_context *iscsi);
* Shutdown multithreading support.
*/
EXTERN void iscsi_mt_service_thread_stop(struct iscsi_context *iscsi);
#ifdef __cplusplus
}
#endif

View File

@@ -76,6 +76,55 @@ iscsi_testunitready_connect(struct iscsi_context *iscsi, int lun,
return task;
}
static struct scsi_task *
iscsi_inquiry_page_0x80_connect(struct iscsi_context *iscsi, int lun,
iscsi_command_cb cb, void *private_data)
{
struct scsi_task *task;
struct iscsi_context *old_iscsi = iscsi->old_iscsi;
iscsi->old_iscsi = NULL;
task = iscsi_inquiry_task(iscsi, lun, 1, 0x80, MAX_STRING_SIZE + 64,
cb, private_data);
iscsi->old_iscsi = old_iscsi;
return task;
}
static void
iscsi_inquiry_page_0x80_cb(struct iscsi_context *iscsi, int status,
void *command_data, void *private_data)
{
struct connect_task *ct = private_data;
struct scsi_task *task = command_data;
struct scsi_inquiry_unit_serial_number *inq;
if (!status) {
inq = scsi_datain_unmarshall(task);
if (inq != NULL) {
if (!iscsi->unit_serial_number[0]) {
ISCSI_LOG(iscsi, 2, "unit serial number is [%s]", inq->usn);
strncpy(iscsi->unit_serial_number, inq->usn, MAX_STRING_SIZE);
} else if (strncmp(iscsi->unit_serial_number, inq->usn, MAX_STRING_SIZE)) {
iscsi_set_error(iscsi, "unit serial number mismatch. got [%s] expected [%s]",
inq->usn, iscsi->unit_serial_number);
status = 1;
} else {
ISCSI_LOG(iscsi, 2, "successfully validated unit serial number [%s]", inq->usn);
}
} else {
iscsi_set_error(iscsi, "iscsi_inquiry_task datain_unmarshall failed. could not read vpd page 0x80.");
status = 1;
}
} else {
iscsi_set_error(iscsi, "iscsi_inquiry_task failed. could not read vpd page 0x80.");
}
ct->cb(iscsi, status?SCSI_STATUS_ERROR:SCSI_STATUS_GOOD, NULL, ct->private_data);
scsi_free_scsi_task(task);
iscsi_free(iscsi, ct);
}
static void
iscsi_testunitready_cb(struct iscsi_context *iscsi, int status,
void *command_data, void *private_data)
@@ -137,10 +186,21 @@ iscsi_testunitready_cb(struct iscsi_context *iscsi, int status,
status = 0;
}
ct->cb(iscsi, status?SCSI_STATUS_ERROR:SCSI_STATUS_GOOD, NULL,
ct->private_data);
scsi_free_scsi_task(task);
iscsi_free(iscsi, ct);
if (status != 0) {
ct->cb(iscsi, SCSI_STATUS_ERROR, NULL,
ct->private_data);
scsi_free_scsi_task(task);
iscsi_free(iscsi, ct);
return;
}
if (iscsi_inquiry_page_0x80_connect(iscsi, ct->lun,
iscsi_inquiry_page_0x80_cb,
ct) == NULL) {
iscsi_set_error(iscsi, "iscsi_inquiry_task failed.");
ct->cb(iscsi, SCSI_STATUS_ERROR, NULL, ct->private_data);
iscsi_free(iscsi, ct);
}
}
static void
@@ -178,8 +238,13 @@ iscsi_login_cb(struct iscsi_context *iscsi, int status, void *command_data,
iscsi_free(iscsi, ct);
}
} else {
ct->cb(iscsi, SCSI_STATUS_GOOD, NULL, ct->private_data);
iscsi_free(iscsi, ct);
if (iscsi_inquiry_page_0x80_connect(iscsi, ct->lun,
iscsi_inquiry_page_0x80_cb,
ct) == NULL) {
iscsi_set_error(iscsi, "iscsi_inquiry_task failed.");
ct->cb(iscsi, SCSI_STATUS_ERROR, NULL, ct->private_data);
iscsi_free(iscsi, ct);
}
}
}
@@ -371,7 +436,7 @@ void iscsi_reconnect_cb(struct iscsi_context *iscsi, int status,
iscsi->frees += old_iscsi->frees;
free(old_iscsi);
/* avoid a reconnect faster than 3 seconds */
iscsi->next_reconnect = time(NULL) + 3;
@@ -443,10 +508,12 @@ static int reconnect(struct iscsi_context *iscsi, int force)
tmp_iscsi->lun = iscsi->lun;
strncpy(tmp_iscsi->portal, iscsi->portal, MAX_STRING_SIZE);
strncpy(tmp_iscsi->bind_interfaces, iscsi->bind_interfaces, MAX_STRING_SIZE);
tmp_iscsi->bind_interfaces_cnt = iscsi->bind_interfaces_cnt;
strncpy(tmp_iscsi->unit_serial_number, iscsi->unit_serial_number, MAX_STRING_SIZE);
tmp_iscsi->log_level = iscsi->log_level;
tmp_iscsi->log_fn = iscsi->log_fn;
tmp_iscsi->tcp_user_timeout = iscsi->tcp_user_timeout;

View File

@@ -215,7 +215,7 @@ iscsi_create_context(const char *initiator_name)
iscsi->tcp_keepcnt=3;
iscsi->tcp_keepintvl=30;
iscsi->tcp_keepidle=30;
iscsi->reconnect_max_retries = -1;
iscsi->chap_auth = ISCSI_CHAP_MD5;
@@ -345,6 +345,26 @@ iscsi_set_targetname(struct iscsi_context *iscsi, const char *target_name)
return 0;
}
int
iscsi_set_unit_serial_number(struct iscsi_context *iscsi, const char *usn)
{
if (iscsi->is_loggedin != 0) {
iscsi_set_error(iscsi, "Already logged in when adding "
"unit_serial_number");
return -1;
}
strncpy(iscsi->unit_serial_number,usn,MAX_STRING_SIZE);
return 0;
}
const char *
iscsi_get_unit_serial_number(struct iscsi_context *iscsi)
{
return iscsi ? iscsi->unit_serial_number : "";
}
int
iscsi_destroy_context(struct iscsi_context *iscsi)
{
@@ -383,7 +403,7 @@ iscsi_destroy_context(struct iscsi_context *iscsi)
iscsi_mt_spin_destroy(&iscsi->iscsi_lock);
iscsi_mt_mutex_destroy(&iscsi->iscsi_mutex);
memset(iscsi, 0, sizeof(struct iscsi_context));
free(iscsi);
@@ -521,6 +541,7 @@ iscsi_parse_url(struct iscsi_context *iscsi, const char *url, int full)
char *passwd = NULL;
char *target_user = NULL;
char *target_passwd = NULL;
char *usn = NULL;
char *target = NULL;
char *lun;
char *tmp;
@@ -628,6 +649,9 @@ iscsi_parse_url(struct iscsi_context *iscsi, const char *url, int full)
iscsi->rdma_ack_timeout = atoi(value);
#endif
}
if (!strcmp(key, "force_usn")) {
usn = value;
}
tmp = next;
}
}
@@ -691,7 +715,7 @@ iscsi_parse_url(struct iscsi_context *iscsi, const char *url, int full)
*tmp=0;
}
}
if (iscsi != NULL) {
iscsi_url = iscsi_malloc(iscsi, sizeof(struct iscsi_url));
} else {
@@ -743,6 +767,9 @@ iscsi_parse_url(struct iscsi_context *iscsi, const char *url, int full)
iscsi_set_targetname(iscsi, iscsi_url->target);
iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user, iscsi_url->passwd);
iscsi_set_target_username_pwd(iscsi, iscsi_url->target_user, iscsi_url->target_passwd);
if (usn) {
iscsi_set_unit_serial_number(iscsi, usn);
}
}
return iscsi_url;
@@ -854,4 +881,3 @@ iscsi_set_auth(struct iscsi_context *iscsi, enum iscsi_chap_auth auth)
{
iscsi->chap_auth = auth;
}