feat: read and validate unit serial number after login
this patch adds a read of VPD page 0x80 (unit serial number) after successful login. The serial is then validated on secutive reconnects to avoid the accidental mismatch of LUN ids if some kind of remapping appears between loss of connection and a later reconnect. An additional url parameter force_usn is added to enforce the usn right from the beginning. If not set via url or the new iscsi_set_unit_serial_number function the usn is learned at the first successful login. Signed-off-by: Peter Lieven <pl@dlhnet.de>
This commit is contained in:
@@ -48,10 +48,11 @@ target_password=<password>
|
|||||||
header_digest=<crc32c|none>
|
header_digest=<crc32c|none>
|
||||||
data_digest=<crc32c|none>
|
data_digest=<crc32c|none>
|
||||||
auth=<md5|sha1|sha-256|sha3-256>
|
auth=<md5|sha1|sha-256|sha3-256>
|
||||||
|
force_usn=<unit_serial_number>
|
||||||
|
|
||||||
Transport:
|
Transport:
|
||||||
iser
|
iser
|
||||||
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
iscsi://server/iqn.ronnie.test/1
|
iscsi://server/iqn.ronnie.test/1
|
||||||
|
|
||||||
@@ -193,7 +194,7 @@ To run those tests you would specify
|
|||||||
|
|
||||||
Test discovery
|
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
|
iscsi-test-cu --list
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ struct iscsi_context {
|
|||||||
char portal[MAX_STRING_SIZE+1];
|
char portal[MAX_STRING_SIZE+1];
|
||||||
char alias[MAX_STRING_SIZE+1];
|
char alias[MAX_STRING_SIZE+1];
|
||||||
char bind_interfaces[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;
|
enum iscsi_chap_auth chap_auth;
|
||||||
char user[MAX_STRING_SIZE+1];
|
char user[MAX_STRING_SIZE+1];
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ iscsi_get_auth(struct iscsi_context *iscsi);
|
|||||||
|
|
||||||
EXTERN void
|
EXTERN void
|
||||||
iscsi_set_auth(struct iscsi_context *iscsi, enum iscsi_chap_auth auth);
|
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.
|
* This function is used to parse an iSCSI URL into a iscsi_url structure.
|
||||||
* iSCSI URL format :
|
* 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);
|
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
|
* This function returns any target address supplied in a login response when
|
||||||
* the target has moved.
|
* the target has moved.
|
||||||
@@ -685,7 +703,7 @@ EXTERN void iscsi_free_discovery_data(struct iscsi_context *iscsi,
|
|||||||
* structure containing the data returned from
|
* structure containing the data returned from
|
||||||
* the server.
|
* the server.
|
||||||
* SCSI_STATUS_CANCELLED : Discovery was aborted. Command_data is NULL.
|
* 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
|
* The callback may be NULL if you only want to let libiscsi count the in-flight
|
||||||
* NOPs.
|
* 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
|
/* This function is to set if we should retry a failed reconnect
|
||||||
|
|
||||||
count is defined as follows:
|
count is defined as follows:
|
||||||
-1 -> retry forever (default)
|
-1 -> retry forever (default)
|
||||||
0 -> never retry
|
0 -> never retry
|
||||||
@@ -1707,7 +1725,7 @@ iscsi_set_fd_dup_cb(struct iscsi_context *iscsi,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* MULTITHREADING
|
* MULTITHREADING
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
* This function starts a separate service thread for multithreading support.
|
* 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.
|
* Shutdown multithreading support.
|
||||||
*/
|
*/
|
||||||
EXTERN void iscsi_mt_service_thread_stop(struct iscsi_context *iscsi);
|
EXTERN void iscsi_mt_service_thread_stop(struct iscsi_context *iscsi);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -76,6 +76,50 @@ iscsi_testunitready_connect(struct iscsi_context *iscsi, int lun,
|
|||||||
return task;
|
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 (!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 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
|
static void
|
||||||
iscsi_testunitready_cb(struct iscsi_context *iscsi, int status,
|
iscsi_testunitready_cb(struct iscsi_context *iscsi, int status,
|
||||||
void *command_data, void *private_data)
|
void *command_data, void *private_data)
|
||||||
@@ -137,10 +181,21 @@ iscsi_testunitready_cb(struct iscsi_context *iscsi, int status,
|
|||||||
status = 0;
|
status = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ct->cb(iscsi, status?SCSI_STATUS_ERROR:SCSI_STATUS_GOOD, NULL,
|
if (status != 0) {
|
||||||
ct->private_data);
|
ct->cb(iscsi, SCSI_STATUS_ERROR, NULL,
|
||||||
scsi_free_scsi_task(task);
|
ct->private_data);
|
||||||
iscsi_free(iscsi, ct);
|
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
|
static void
|
||||||
@@ -178,8 +233,13 @@ iscsi_login_cb(struct iscsi_context *iscsi, int status, void *command_data,
|
|||||||
iscsi_free(iscsi, ct);
|
iscsi_free(iscsi, ct);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ct->cb(iscsi, SCSI_STATUS_GOOD, NULL, ct->private_data);
|
if (iscsi_inquiry_page_0x80_connect(iscsi, ct->lun,
|
||||||
iscsi_free(iscsi, ct);
|
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 +431,7 @@ void iscsi_reconnect_cb(struct iscsi_context *iscsi, int status,
|
|||||||
iscsi->frees += old_iscsi->frees;
|
iscsi->frees += old_iscsi->frees;
|
||||||
|
|
||||||
free(old_iscsi);
|
free(old_iscsi);
|
||||||
|
|
||||||
/* avoid a reconnect faster than 3 seconds */
|
/* avoid a reconnect faster than 3 seconds */
|
||||||
iscsi->next_reconnect = time(NULL) + 3;
|
iscsi->next_reconnect = time(NULL) + 3;
|
||||||
|
|
||||||
@@ -443,10 +503,12 @@ static int reconnect(struct iscsi_context *iscsi, int force)
|
|||||||
tmp_iscsi->lun = iscsi->lun;
|
tmp_iscsi->lun = iscsi->lun;
|
||||||
|
|
||||||
strncpy(tmp_iscsi->portal, iscsi->portal, MAX_STRING_SIZE);
|
strncpy(tmp_iscsi->portal, iscsi->portal, MAX_STRING_SIZE);
|
||||||
|
|
||||||
strncpy(tmp_iscsi->bind_interfaces, iscsi->bind_interfaces, MAX_STRING_SIZE);
|
strncpy(tmp_iscsi->bind_interfaces, iscsi->bind_interfaces, MAX_STRING_SIZE);
|
||||||
tmp_iscsi->bind_interfaces_cnt = iscsi->bind_interfaces_cnt;
|
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_level = iscsi->log_level;
|
||||||
tmp_iscsi->log_fn = iscsi->log_fn;
|
tmp_iscsi->log_fn = iscsi->log_fn;
|
||||||
tmp_iscsi->tcp_user_timeout = iscsi->tcp_user_timeout;
|
tmp_iscsi->tcp_user_timeout = iscsi->tcp_user_timeout;
|
||||||
|
|||||||
34
lib/init.c
34
lib/init.c
@@ -215,7 +215,7 @@ iscsi_create_context(const char *initiator_name)
|
|||||||
iscsi->tcp_keepcnt=3;
|
iscsi->tcp_keepcnt=3;
|
||||||
iscsi->tcp_keepintvl=30;
|
iscsi->tcp_keepintvl=30;
|
||||||
iscsi->tcp_keepidle=30;
|
iscsi->tcp_keepidle=30;
|
||||||
|
|
||||||
iscsi->reconnect_max_retries = -1;
|
iscsi->reconnect_max_retries = -1;
|
||||||
iscsi->chap_auth = ISCSI_CHAP_MD5;
|
iscsi->chap_auth = ISCSI_CHAP_MD5;
|
||||||
|
|
||||||
@@ -345,6 +345,26 @@ iscsi_set_targetname(struct iscsi_context *iscsi, const char *target_name)
|
|||||||
return 0;
|
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
|
int
|
||||||
iscsi_destroy_context(struct iscsi_context *iscsi)
|
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_spin_destroy(&iscsi->iscsi_lock);
|
||||||
iscsi_mt_mutex_destroy(&iscsi->iscsi_mutex);
|
iscsi_mt_mutex_destroy(&iscsi->iscsi_mutex);
|
||||||
|
|
||||||
memset(iscsi, 0, sizeof(struct iscsi_context));
|
memset(iscsi, 0, sizeof(struct iscsi_context));
|
||||||
free(iscsi);
|
free(iscsi);
|
||||||
|
|
||||||
@@ -521,6 +541,7 @@ iscsi_parse_url(struct iscsi_context *iscsi, const char *url, int full)
|
|||||||
char *passwd = NULL;
|
char *passwd = NULL;
|
||||||
char *target_user = NULL;
|
char *target_user = NULL;
|
||||||
char *target_passwd = NULL;
|
char *target_passwd = NULL;
|
||||||
|
char *usn = NULL;
|
||||||
char *target = NULL;
|
char *target = NULL;
|
||||||
char *lun;
|
char *lun;
|
||||||
char *tmp;
|
char *tmp;
|
||||||
@@ -628,6 +649,9 @@ iscsi_parse_url(struct iscsi_context *iscsi, const char *url, int full)
|
|||||||
iscsi->rdma_ack_timeout = atoi(value);
|
iscsi->rdma_ack_timeout = atoi(value);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
if (!strcmp(key, "force_usn")) {
|
||||||
|
usn = value;
|
||||||
|
}
|
||||||
tmp = next;
|
tmp = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -691,7 +715,7 @@ iscsi_parse_url(struct iscsi_context *iscsi, const char *url, int full)
|
|||||||
*tmp=0;
|
*tmp=0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iscsi != NULL) {
|
if (iscsi != NULL) {
|
||||||
iscsi_url = iscsi_malloc(iscsi, sizeof(struct iscsi_url));
|
iscsi_url = iscsi_malloc(iscsi, sizeof(struct iscsi_url));
|
||||||
} else {
|
} 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_targetname(iscsi, iscsi_url->target);
|
||||||
iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user, iscsi_url->passwd);
|
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);
|
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;
|
return iscsi_url;
|
||||||
@@ -854,4 +881,3 @@ iscsi_set_auth(struct iscsi_context *iscsi, enum iscsi_chap_auth auth)
|
|||||||
{
|
{
|
||||||
iscsi->chap_auth = auth;
|
iscsi->chap_auth = auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user