diff --git a/README.md b/README.md index e091cb7..4dea9e4 100644 --- a/README.md +++ b/README.md @@ -48,10 +48,11 @@ target_password= header_digest= data_digest= auth= +force_usn= + 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 diff --git a/include/iscsi-private.h b/include/iscsi-private.h index 69c9e3b..94fd825 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -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]; diff --git a/include/iscsi.h b/include/iscsi.h index 3a6e360..f333025 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -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 diff --git a/lib/connect.c b/lib/connect.c index 5b944a5..0b762fc 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -76,6 +76,50 @@ 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 (!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 iscsi_testunitready_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data) @@ -137,10 +181,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 +233,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 +431,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 +503,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; diff --git a/lib/init.c b/lib/init.c index bd843ca..a6cd795 100644 --- a/lib/init.c +++ b/lib/init.c @@ -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; } -