#include <sys/cpuvar.h>
#include <sys/types.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/socket.h>
#include <inet/tcp.h>
#include <sys/sdt.h>
#include <sys/stmf.h>
#include <sys/stmf_ioctl.h>
#include <sys/portif.h>
#include <sys/idm/idm.h>
#include <sys/idm/idm_so.h>
#include <sys/iscsit/iscsit_common.h>
#include <sys/iscsit/isns_protocol.h>
#include <sys/ksocket.h>
#include "iscsit.h"
#include "iscsit_isns.h"
#define MAX_XID (2^16)
#define ISNS_IDLE_TIME 60
#define MAX_RETRY (3)
#define ISNS_RCV_TIMER_SECONDS 5
#define VALID_NAME(NAME, LEN) \
((LEN) > 0 && (NAME)[0] != 0 && (NAME)[(LEN) - 1] == 0)
#define ISNST_LOG if (iscsit_isns_logging) cmn_err
static kmutex_t isns_monitor_mutex;
volatile kthread_t *isns_monitor_thr_id;
static kt_did_t isns_monitor_thr_did;
static boolean_t isns_monitor_thr_running;
static kcondvar_t isns_idle_cv;
static uint16_t xid;
#define GET_XID() atomic_inc_16_nv(&xid)
static clock_t monitor_idle_interval;
#define ISNS_GLOBAL_LOCK() \
mutex_enter(&iscsit_global.global_isns_cfg.isns_mutex)
#define ISNS_GLOBAL_LOCK_HELD() \
MUTEX_HELD(&iscsit_global.global_isns_cfg.isns_mutex)
#define ISNS_GLOBAL_UNLOCK() \
mutex_exit(&iscsit_global.global_isns_cfg.isns_mutex)
boolean_t iscsit_isns_logging = B_FALSE;
int isns_max_retry = MAX_RETRY;
boolean_t isns_use_esi = B_FALSE;
int isns_default_esi_interval = ISNS_DEFAULT_ESI_INTERVAL;
int isns_registration_period = ISNS_DEFAULT_REGISTRATION_PERIOD;
uint32_t isns_timeout_usec = ISNS_RCV_TIMER_SECONDS * 1000000;
uint32_t isns_message_buf_size = ISNSP_MAX_PDU_SIZE;
int isns_initial_delay = ISNS_INITIAL_DELAY;
boolean_t isns_modify_must_replace = B_TRUE;
#define ISNST_MAX_MSG_SIZE (16 * ISNSP_MAX_PDU_SIZE)
static isns_esi_tinfo_t esi;
static kmutex_t iscsit_isns_mutex;
static avl_tree_t isns_target_list;
static boolean_t isns_targets_changed;
static boolean_t isns_portals_changed;
static avl_tree_t isns_tpg_portals;
static boolean_t default_portal_online;
static avl_tree_t isns_all_portals;
static int num_default_portals;
static int num_tpg_portals;
static char *isns_eid = NULL;
static const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
static void
isnst_start();
static void
isnst_stop();
static void
iscsit_set_isns(boolean_t state);
static void
iscsit_add_isns(it_portal_t *cfg_svr);
static void
isnst_mark_delete_isns(iscsit_isns_svr_t *svr);
static void
isnst_finish_delete_isns(iscsit_isns_svr_t *svr);
static iscsit_isns_svr_t *
iscsit_isns_svr_lookup(struct sockaddr_storage *sa);
static void
isnst_monitor(void *arg);
static int
isnst_monitor_one_server(iscsit_isns_svr_t *svr, boolean_t enabled);
static void
isnst_monitor_awaken(void);
static boolean_t
isnst_update_server_timestamp(struct sockaddr_storage *sa);
static void
isnst_copy_global_status_changes(void);
static void
isnst_mark_deleted_targets(iscsit_isns_svr_t *svr);
static int
isnst_update_one_server(iscsit_isns_svr_t *svr, isns_target_t *target,
isns_reg_type_t reg);
static boolean_t isnst_retry_registration(int rsp_status_code);
static int isnst_register(iscsit_isns_svr_t *svr, isns_target_t *itarget,
isns_reg_type_t regtype);
static int isnst_deregister(iscsit_isns_svr_t *svr, isns_target_t *itarget);
static size_t
isnst_make_dereg_pdu(iscsit_isns_svr_t *svr, isns_pdu_t **pdu,
isns_target_t *itarge);
static int isnst_keepalive(iscsit_isns_svr_t *svr);
static size_t
isnst_make_keepalive_pdu(iscsit_isns_svr_t *svr, isns_pdu_t **pdu);
static isns_target_t *
isnst_get_registered_source(iscsit_isns_svr_t *srv);
static isns_target_t *
isnst_get_registered_source_locked(iscsit_isns_svr_t *srv);
static int
isnst_verify_rsp(iscsit_isns_svr_t *svr, isns_pdu_t *pdu,
isns_pdu_t *rsp, size_t rsp_size);
static uint16_t
isnst_pdu_get_op(isns_pdu_t *pdu, uint8_t **pp);
static size_t
isnst_make_reg_pdu(isns_pdu_t **pdu, isns_target_t *target,
iscsit_isns_svr_t *svr, isns_reg_type_t regtype);
static int
isnst_reg_pdu_add_entity_portals(isns_pdu_t *pdu, size_t pdu_size);
static int
isnst_reg_pdu_add_pg(isns_pdu_t *pdu, size_t pdu_size, isns_target_t *target);
static int
isnst_add_default_pg(isns_pdu_t *pdu, size_t pdu_size,
avl_tree_t *null_portal_list);
static int
isnst_add_tpg_pg(isns_pdu_t *pdu, size_t pdu_size,
isns_tpgt_t *tig, avl_tree_t *null_portal_list);
static int
isnst_add_null_pg(isns_pdu_t *pdu, size_t pdu_size,
avl_tree_t *null_portal_list);
static int
isnst_add_portal_attr(isns_pdu_t *pdu, size_t pdu_size,
uint32_t ip_attr_id, uint32_t port_attr_id,
struct sockaddr_storage *ss, boolean_t esi_info);
static size_t
isnst_create_pdu_header(uint16_t func_id, isns_pdu_t **pdu, uint16_t flags);
static int
isnst_add_attr(isns_pdu_t *pdu,
size_t max_pdu_size,
uint32_t attr_id,
uint32_t attr_len,
void *attr_data,
uint32_t attr_numeric_data);
static int
isnst_send_pdu(void *so, isns_pdu_t *pdu);
static size_t
isnst_rcv_pdu(void *so, isns_pdu_t **pdu);
static void *
isnst_open_so(struct sockaddr_storage *sa);
static void
isnst_close_so(void *);
static void
isnst_esi_thread(void *arg);
static void
isnst_handle_esi_req(ksocket_t so, isns_pdu_t *pdu, size_t pl_size);
static void isnst_esi_start(void);
static void isnst_esi_stop(void);
static isns_target_t *isnst_latch_to_target_list(isns_target_t *target,
avl_tree_t *list);
static void isnst_clear_target_list(iscsit_isns_svr_t *svr);
static void isnst_clear_from_target_list(isns_target_t *target,
avl_tree_t *target_list);
static int isnst_tgt_avl_compare(const void *t1, const void *t2);
static void isnst_set_server_status(iscsit_isns_svr_t *svr,
boolean_t registered);
static void isnst_monitor_start(void);
static void isnst_monitor_stop(void);
static void
isnst_monitor_default_portal_list(void);
static int
isnst_find_default_portals(idm_addr_list_t *alist);
static int
isnst_add_default_portals(idm_addr_list_t *alist);
static void
isnst_clear_default_portals(void);
static void
isnst_clear_portal_list(avl_tree_t *portal_list);
static void
isnst_copy_portal_list(avl_tree_t *t1, avl_tree_t *t2);
static isns_portal_t *
isnst_lookup_portal(struct sockaddr_storage *sa);
static isns_portal_t *
isnst_add_to_portal_list(struct sockaddr_storage *sa, avl_tree_t *portal_list);
static void
isnst_remove_from_portal_list(struct sockaddr_storage *sa,
avl_tree_t *portal_list);
static int
isnst_portal_avl_compare(const void *t1, const void *t2);
it_cfg_status_t
isnst_config_merge(it_config_t *cfg)
{
boolean_t new_isns_state = B_FALSE;
iscsit_isns_svr_t *isns_svr, *next_isns_svr;
it_portal_t *cfg_isns_svr;
ISNS_GLOBAL_LOCK();
(void) nvlist_lookup_boolean_value(cfg->config_global_properties,
PROP_ISNS_ENABLED, &new_isns_state);
for (isns_svr = list_head(&iscsit_global.global_isns_cfg.isns_svrs);
isns_svr != NULL;
isns_svr = next_isns_svr) {
next_isns_svr = list_next(
&iscsit_global.global_isns_cfg.isns_svrs, isns_svr);
if (it_sns_svr_lookup(cfg, &isns_svr->svr_sa) == NULL)
isnst_mark_delete_isns(isns_svr);
}
for (cfg_isns_svr = cfg->config_isns_svr_list;
cfg_isns_svr != NULL;
cfg_isns_svr = cfg_isns_svr->portal_next) {
isns_svr = iscsit_isns_svr_lookup(&cfg_isns_svr->portal_addr);
if (isns_svr == NULL) {
iscsit_add_isns(cfg_isns_svr);
} else if (isns_svr->svr_delete_needed) {
isns_svr->svr_delete_needed = B_FALSE;
isns_svr->svr_reset_needed = B_TRUE;
}
}
iscsit_set_isns(new_isns_state);
ISNS_GLOBAL_UNLOCK();
isnst_monitor_awaken();
return (0);
}
int
iscsit_isns_init(iscsit_hostinfo_t *hostinfo)
{
mutex_init(&iscsit_global.global_isns_cfg.isns_mutex, NULL,
MUTEX_DEFAULT, NULL);
ISNS_GLOBAL_LOCK();
mutex_init(&iscsit_isns_mutex, NULL, MUTEX_DEFAULT, NULL);
iscsit_global.global_isns_cfg.isns_state = B_FALSE;
list_create(&iscsit_global.global_isns_cfg.isns_svrs,
sizeof (iscsit_isns_svr_t), offsetof(iscsit_isns_svr_t, svr_ln));
avl_create(&isns_tpg_portals, isnst_portal_avl_compare,
sizeof (isns_portal_t), offsetof(isns_portal_t, portal_node));
avl_create(&isns_all_portals, isnst_portal_avl_compare,
sizeof (isns_portal_t), offsetof(isns_portal_t, portal_node));
num_default_portals = 0;
if (hostinfo->length > ISCSIT_MAX_HOSTNAME_LEN)
hostinfo->length = ISCSIT_MAX_HOSTNAME_LEN;
isns_eid = kmem_alloc(hostinfo->length, KM_SLEEP);
(void) strlcpy(isns_eid, hostinfo->fqhn, hostinfo->length);
avl_create(&isns_target_list, isnst_tgt_avl_compare,
sizeof (isns_target_t), offsetof(isns_target_t, target_node));
mutex_init(&isns_monitor_mutex, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&esi.esi_mutex, NULL, MUTEX_DEFAULT, NULL);
isns_monitor_thr_id = NULL;
monitor_idle_interval = ISNS_IDLE_TIME * drv_usectohz(1000000);
cv_init(&isns_idle_cv, NULL, CV_DEFAULT, NULL);
cv_init(&esi.esi_cv, NULL, CV_DEFAULT, NULL);
xid = 0;
ISNS_GLOBAL_UNLOCK();
return (0);
}
void
iscsit_isns_fini()
{
ISNS_GLOBAL_LOCK();
iscsit_set_isns(B_FALSE);
mutex_destroy(&isns_monitor_mutex);
cv_destroy(&isns_idle_cv);
mutex_destroy(&esi.esi_mutex);
cv_destroy(&esi.esi_cv);
mutex_destroy(&iscsit_isns_mutex);
if (isns_eid) {
kmem_free(isns_eid, strlen(isns_eid) + 1);
isns_eid = NULL;
}
iscsit_global.global_isns_cfg.isns_state = B_FALSE;
avl_destroy(&isns_target_list);
list_destroy(&iscsit_global.global_isns_cfg.isns_svrs);
avl_destroy(&isns_tpg_portals);
avl_destroy(&isns_all_portals);
num_default_portals = 0;
ISNS_GLOBAL_UNLOCK();
mutex_destroy(&iscsit_global.global_isns_cfg.isns_mutex);
}
static void
iscsit_set_isns(boolean_t state)
{
iscsit_isns_svr_t *svr;
ASSERT(ISNS_GLOBAL_LOCK_HELD());
if (iscsit_global.global_isns_cfg.isns_state != state) {
for (svr = list_head(&iscsit_global.global_isns_cfg.isns_svrs);
svr != NULL;
svr = list_next(&iscsit_global.global_isns_cfg.isns_svrs,
svr)) {
svr->svr_retry_count = 0;
}
iscsit_global.global_isns_cfg.isns_state = state;
if (state) {
isnst_start();
} else {
isnst_stop();
}
}
}
void
iscsit_add_isns(it_portal_t *cfg_svr)
{
iscsit_isns_svr_t *svr;
ASSERT(ISNS_GLOBAL_LOCK_HELD());
svr = kmem_zalloc(sizeof (iscsit_isns_svr_t), KM_SLEEP);
bcopy(&cfg_svr->portal_addr, &svr->svr_sa,
sizeof (struct sockaddr_storage));
avl_create(&svr->svr_target_list, isnst_tgt_avl_compare,
sizeof (isns_target_t), offsetof(isns_target_t, target_node));
svr->svr_esi_interval = isns_default_esi_interval;
list_insert_tail(&iscsit_global.global_isns_cfg.isns_svrs, svr);
}
void
isnst_mark_delete_isns(iscsit_isns_svr_t *svr)
{
ASSERT(ISNS_GLOBAL_LOCK_HELD());
if (iscsit_global.global_isns_cfg.isns_state == B_FALSE) {
isnst_finish_delete_isns(svr);
} else {
svr->svr_delete_needed = B_TRUE;
}
}
void
isnst_finish_delete_isns(iscsit_isns_svr_t *svr)
{
ASSERT(ISNS_GLOBAL_LOCK_HELD());
isnst_clear_target_list(svr);
list_remove(&iscsit_global.global_isns_cfg.isns_svrs, svr);
avl_destroy(&svr->svr_target_list);
kmem_free(svr, sizeof (*svr));
}
static iscsit_isns_svr_t *
iscsit_isns_svr_lookup(struct sockaddr_storage *sa)
{
iscsit_isns_svr_t *svr;
it_portal_t portal1;
ASSERT(ISNS_GLOBAL_LOCK_HELD());
bcopy(sa, &portal1.portal_addr, sizeof (struct sockaddr_storage));
for (svr = list_head(&iscsit_global.global_isns_cfg.isns_svrs);
svr != NULL;
svr = list_next(&iscsit_global.global_isns_cfg.isns_svrs, svr)) {
if (it_sa_compare(&svr->svr_sa, sa) == 0)
return (svr);
}
return (NULL);
}
static isns_target_info_t *
isnst_create_target_info(iscsit_tgt_t *target)
{
isns_target_info_t *ti;
isns_tpgt_t *tig;
isns_tpgt_addr_t *tip;
iscsit_tpgt_t *tpgt;
iscsit_tpg_t *tpg;
iscsit_portal_t *tp;
char *str;
ASSERT(! mutex_owned(&iscsit_isns_mutex));
ti = kmem_zalloc(sizeof (isns_target_info_t), KM_SLEEP);
list_create(&ti->ti_tpgt_list,
sizeof (isns_tpgt_t), offsetof(isns_tpgt_t, ti_tpgt_ln));
idm_refcnt_init(&ti->ti_refcnt, ti);
mutex_enter(&target->target_mutex);
(void) strncpy(ti->ti_tgt_name, target->target_name,
MAX_ISCSI_NODENAMELEN);
if (nvlist_lookup_string(target->target_props, PROP_ALIAS,
&str) == 0) {
(void) strncpy(ti->ti_tgt_alias, str, MAX_ISCSI_NODENAMELEN);
}
tpgt = avl_first(&target->target_tpgt_list);
ASSERT(tpgt != NULL);
do {
tig = kmem_zalloc(sizeof (isns_tpgt_t), KM_SLEEP);
list_create(&tig->ti_portal_list, sizeof (isns_tpgt_addr_t),
offsetof(isns_tpgt_addr_t, portal_ln));
tig->ti_tpgt_tag = tpgt->tpgt_tag;
if (tpgt->tpgt_tag != ISCSIT_DEFAULT_TPGT) {
tpg = tpgt->tpgt_tpg;
mutex_enter(&tpg->tpg_mutex);
tp = avl_first(&tpg->tpg_portal_list);
do {
tip = kmem_zalloc(sizeof (isns_tpgt_addr_t),
KM_SLEEP);
bcopy(&tp->portal_addr, &tip->portal_addr,
sizeof (tip->portal_addr));
list_insert_tail(&tig->ti_portal_list, tip);
tp = AVL_NEXT(&tpg->tpg_portal_list, tp);
} while (tp != NULL);
mutex_exit(&tpg->tpg_mutex);
}
list_insert_tail(&ti->ti_tpgt_list, tig);
tpgt = AVL_NEXT(&target->target_tpgt_list, tpgt);
} while (tpgt != NULL);
mutex_exit(&target->target_mutex);
return (ti);
}
static void
isnst_clear_target_info_cb(void *arg)
{
isns_target_info_t *ti = (isns_target_info_t *)arg;
isns_tpgt_t *tig;
isns_tpgt_addr_t *tip;
while ((tig = list_remove_head(&ti->ti_tpgt_list)) != NULL) {
while ((tip = list_remove_head(&tig->ti_portal_list)) != NULL) {
kmem_free(tip, sizeof (isns_tpgt_addr_t));
}
list_destroy(&tig->ti_portal_list);
kmem_free(tig, sizeof (isns_tpgt_t));
}
list_destroy(&ti->ti_tpgt_list);
idm_refcnt_destroy(&ti->ti_refcnt);
kmem_free(ti, sizeof (isns_target_info_t));
}
int
iscsit_isns_register(iscsit_tgt_t *target)
{
isns_target_t *itarget, tmptgt;
avl_index_t where;
isns_target_info_t *ti;
ti = isnst_create_target_info(target);
mutex_enter(&iscsit_isns_mutex);
tmptgt.target = target;
if ((itarget = (isns_target_t *)avl_find(&isns_target_list,
&tmptgt, &where)) == NULL) {
itarget = kmem_zalloc(sizeof (isns_target_t), KM_SLEEP);
itarget->target = target;
avl_insert(&isns_target_list, (void *)itarget, where);
} else {
ASSERT(0);
}
itarget->target_info = ti;
idm_refcnt_hold(&ti->ti_refcnt);
isns_targets_changed = B_TRUE;
mutex_exit(&iscsit_isns_mutex);
isnst_monitor_awaken();
return (0);
}
int
iscsit_isns_deregister(iscsit_tgt_t *target)
{
isns_target_t *itarget, tmptgt;
isns_target_info_t *ti;
tmptgt.target = target;
mutex_enter(&iscsit_isns_mutex);
itarget = avl_find(&isns_target_list, &tmptgt, NULL);
ASSERT(itarget != NULL);
ti = itarget->target_info;
idm_refcnt_rele(&ti->ti_refcnt);
idm_refcnt_async_wait_ref(&ti->ti_refcnt,
(idm_refcnt_cb_t *)&isnst_clear_target_info_cb);
itarget->target_info = NULL;
avl_remove(&isns_target_list, itarget);
kmem_free(itarget, sizeof (isns_target_t));
isns_targets_changed = B_TRUE;
mutex_exit(&iscsit_isns_mutex);
isnst_monitor_awaken();
return (0);
}
void
iscsit_isns_target_update(iscsit_tgt_t *target)
{
isns_target_t *itarget, tmptgt;
isns_target_info_t *ti;
ti = isnst_create_target_info(target);
mutex_enter(&iscsit_isns_mutex);
tmptgt.target = target;
itarget = avl_find(&isns_target_list, &tmptgt, NULL);
if (itarget == NULL) {
mutex_exit(&iscsit_isns_mutex);
isnst_clear_target_info_cb(ti);
return;
}
idm_refcnt_rele(&itarget->target_info->ti_refcnt);
idm_refcnt_async_wait_ref(&itarget->target_info->ti_refcnt,
(idm_refcnt_cb_t *)&isnst_clear_target_info_cb);
itarget->target_info = ti;
idm_refcnt_hold(&ti->ti_refcnt);
itarget->target_update_needed = B_TRUE;
isns_targets_changed = B_TRUE;
mutex_exit(&iscsit_isns_mutex);
isnst_monitor_awaken();
}
static void
isnst_start()
{
ISNST_LOG(CE_NOTE, "**** isnst_start");
ASSERT(ISNS_GLOBAL_LOCK_HELD());
isnst_esi_start();
isnst_monitor_start();
}
static void
isnst_stop()
{
ASSERT(ISNS_GLOBAL_LOCK_HELD());
ISNST_LOG(CE_NOTE, "**** isnst_stop");
ISNS_GLOBAL_UNLOCK();
isnst_esi_stop();
isnst_monitor_stop();
ISNS_GLOBAL_LOCK();
}
static void
isnst_monitor_start(void)
{
ISNST_LOG(CE_NOTE, "isnst_monitor_start");
mutex_enter(&isns_monitor_mutex);
ASSERT(!isns_monitor_thr_running);
isns_monitor_thr_id = thread_create(NULL, 0,
isnst_monitor, NULL, 0, &p0, TS_RUN, minclsyspri);
while (!isns_monitor_thr_running)
cv_wait(&isns_idle_cv, &isns_monitor_mutex);
mutex_exit(&isns_monitor_mutex);
}
static void
isnst_monitor_stop(void)
{
ISNST_LOG(CE_NOTE, "isnst_monitor_stop");
mutex_enter(&isns_monitor_mutex);
if (isns_monitor_thr_running) {
isns_monitor_thr_running = B_FALSE;
cv_signal(&isns_idle_cv);
mutex_exit(&isns_monitor_mutex);
thread_join(isns_monitor_thr_did);
return;
}
mutex_exit(&isns_monitor_mutex);
}
static boolean_t
isnst_update_server_timestamp(struct sockaddr_storage *ss)
{
iscsit_isns_svr_t *svr;
ASSERT(ISNS_GLOBAL_LOCK_HELD());
for (svr = list_head(&iscsit_global.global_isns_cfg.isns_svrs);
svr != NULL;
svr = list_next(&iscsit_global.global_isns_cfg.isns_svrs, svr)) {
if (idm_ss_compare(ss, &svr->svr_sa,
B_TRUE ,
B_FALSE ) == 0) {
break;
}
}
if (svr != NULL) {
svr->svr_last_msg = ddi_get_lbolt();
if (!svr->svr_registered) {
isnst_monitor_awaken();
}
return (B_TRUE);
}
return (B_FALSE);
}
static void
isnst_monitor_all_servers()
{
iscsit_isns_svr_t *svr, *next_svr;
boolean_t enabled;
list_t *svr_list;
int rc;
svr_list = &iscsit_global.global_isns_cfg.isns_svrs;
ISNS_GLOBAL_LOCK();
isnst_copy_global_status_changes();
enabled = iscsit_global.global_isns_cfg.isns_state;
for (svr = list_head(svr_list); svr != NULL; svr = next_svr) {
svr->svr_monitor_hold = B_TRUE;
rc = isnst_monitor_one_server(svr, enabled);
if (rc != 0) {
svr->svr_retry_count++;
if (svr->svr_registered &&
svr->svr_retry_count > isns_max_retry) {
char server_buf[IDM_SA_NTOP_BUFSIZ];
if (! svr->svr_reset_needed) {
ISNST_LOG(CE_WARN,
"isnst: iSNS server %s"
" not responding (rc=%d).",
idm_sa_ntop(&svr->svr_sa,
server_buf, sizeof (server_buf)),
rc);
svr->svr_reset_needed = B_TRUE;
}
}
} else {
svr->svr_retry_count = 0;
}
svr->svr_monitor_hold = B_FALSE;
next_svr = list_next(svr_list, svr);
if (svr->svr_delete_needed == B_TRUE &&
svr->svr_registered == B_FALSE) {
isnst_finish_delete_isns(svr);
}
}
ISNS_GLOBAL_UNLOCK();
}
static void
isnst_monitor_awaken(void)
{
mutex_enter(&isns_monitor_mutex);
if (isns_monitor_thr_running) {
DTRACE_PROBE(iscsit__isns__monitor__awaken);
cv_signal(&isns_idle_cv);
}
mutex_exit(&isns_monitor_mutex);
}
static void
isnst_monitor(void *arg)
{
mutex_enter(&isns_monitor_mutex);
isns_monitor_thr_did = curthread->t_did;
isns_monitor_thr_running = B_TRUE;
cv_signal(&isns_idle_cv);
mutex_exit(&isns_monitor_mutex);
delay(drv_usectohz(isns_initial_delay * 1000000));
mutex_enter(&isns_monitor_mutex);
mutex_enter(&iscsit_isns_mutex);
isns_portals_changed = B_TRUE;
mutex_exit(&iscsit_isns_mutex);
while (isns_monitor_thr_running) {
mutex_exit(&isns_monitor_mutex);
isnst_monitor_all_servers();
mutex_enter(&isns_monitor_mutex);
mutex_enter(&iscsit_isns_mutex);
if (isns_targets_changed || isns_portals_changed) {
DTRACE_PROBE(iscsit__isns__monitor__reenter);
mutex_exit(&iscsit_isns_mutex);
continue;
}
mutex_exit(&iscsit_isns_mutex);
if (! isns_monitor_thr_running)
break;
DTRACE_PROBE(iscsit__isns__monitor__sleep);
(void) cv_reltimedwait(&isns_idle_cv, &isns_monitor_mutex,
monitor_idle_interval, TR_CLOCK_TICK);
DTRACE_PROBE1(iscsit__isns__monitor__wakeup,
boolean_t, isns_monitor_thr_running);
}
mutex_exit(&isns_monitor_mutex);
isnst_monitor_all_servers();
ISNS_GLOBAL_LOCK();
isnst_clear_default_portals();
ISNS_GLOBAL_UNLOCK();
thread_exit();
}
static int
isnst_monitor_one_server(iscsit_isns_svr_t *svr, boolean_t enabled)
{
int rc = 0;
isns_target_t *itarget;
ASSERT(ISNS_GLOBAL_LOCK_HELD());
if (enabled == B_FALSE || svr->svr_delete_needed) {
(void) isnst_update_one_server(svr, NULL, ISNS_DEREGISTER_ALL);
isnst_set_server_status(svr, B_FALSE);
return (0);
}
retry_replace_all:
if (svr->svr_registered && svr->svr_reset_needed) {
isns_target_t *jtarget;
for (jtarget = avl_first(&svr->svr_target_list);
jtarget != NULL;
jtarget = AVL_NEXT(&svr->svr_target_list, jtarget)) {
if (!jtarget->target_delete_needed) {
break;
}
}
if (jtarget == NULL) {
rc = isnst_update_one_server(svr, NULL,
ISNS_DEREGISTER_ALL);
if (rc != 0) {
return (rc);
}
isnst_set_server_status(svr, B_FALSE);
return (0);
}
}
if (! svr->svr_registered || svr->svr_reset_needed) {
if (avl_numnodes(&svr->svr_target_list) == 0) {
return (0);
}
if ((rc = isnst_update_one_server(svr, NULL,
ISNS_REGISTER_ALL)) != 0) {
return (rc);
}
isnst_set_server_status(svr, B_TRUE);
}
if (svr->svr_targets_changed) {
isns_target_t *next_target;
for (itarget = avl_first(&svr->svr_target_list);
itarget != NULL;
itarget = next_target) {
next_target = AVL_NEXT(&svr->svr_target_list, itarget);
if (itarget->target_delete_needed) {
isns_target_t *jtarget;
ASSERT(itarget->target_registered);
for (jtarget =
avl_first(&svr->svr_target_list);
jtarget != NULL;
jtarget = AVL_NEXT(&svr->svr_target_list,
jtarget)) {
if (jtarget->target_registered &&
!jtarget->target_delete_needed) {
break;
}
}
if (jtarget == NULL) {
rc = isnst_update_one_server(svr,
NULL, ISNS_DEREGISTER_ALL);
if (rc != 0) {
return (rc);
}
isnst_set_server_status(svr, B_FALSE);
return (0);
}
rc = isnst_update_one_server(svr,
itarget, ISNS_DEREGISTER_TARGET);
if (rc != 0 && isnst_retry_registration(rc)) {
svr->svr_reset_needed = B_TRUE;
goto retry_replace_all;
}
if (rc != 0) {
return (rc);
}
isnst_clear_from_target_list(itarget,
&svr->svr_target_list);
}
}
itarget = avl_first(&svr->svr_target_list);
while (itarget) {
if (!itarget->target_registered ||
itarget->target_update_needed) {
if (isns_modify_must_replace) {
svr->svr_reset_needed = B_TRUE;
goto retry_replace_all;
}
rc = isnst_update_one_server(svr,
itarget,
ISNS_MODIFY_TARGET);
if (rc != 0 && isnst_retry_registration(rc)) {
svr->svr_reset_needed = B_TRUE;
goto retry_replace_all;
}
if (rc != 0) {
return (rc);
}
itarget->target_update_needed =
B_FALSE;
itarget->target_registered = B_TRUE;
}
itarget = AVL_NEXT(&svr->svr_target_list,
itarget);
}
svr->svr_targets_changed = B_FALSE;
}
if (isns_use_esi) {
if (ddi_get_lbolt() >= (svr->svr_last_msg +
drv_usectohz(svr->svr_esi_interval * 1000000 *
MAX_ESI_INTERVALS))) {
svr->svr_reset_needed = B_TRUE;
goto retry_replace_all;
}
} else {
if (ddi_get_lbolt() >= (svr->svr_last_msg +
drv_usectohz(isns_registration_period * (1000000/3)))) {
ISNS_GLOBAL_UNLOCK();
rc = isnst_keepalive(svr);
ISNS_GLOBAL_LOCK();
if (rc != 0 && isnst_retry_registration(rc)) {
svr->svr_reset_needed = B_TRUE;
goto retry_replace_all;
}
if (rc != 0) {
return (rc);
}
}
}
return (0);
}
static void
isnst_mark_deleted_targets(iscsit_isns_svr_t *svr)
{
isns_target_t *itarget, *nxt_target, tmptgt;
ASSERT(ISNS_GLOBAL_LOCK_HELD());
ASSERT(mutex_owned(&iscsit_isns_mutex));
for (itarget = avl_first(&svr->svr_target_list);
itarget != NULL;
itarget = nxt_target) {
tmptgt.target = itarget->target;
nxt_target = AVL_NEXT(&svr->svr_target_list, itarget);
if (avl_find(&isns_target_list, &tmptgt, NULL) == NULL) {
if (itarget->target_registered) {
itarget->target_delete_needed = B_TRUE;
} else {
isnst_clear_from_target_list(itarget,
&svr->svr_target_list);
}
}
}
}
static isns_target_t *
isnst_latch_to_target_list(isns_target_t *jtarget, avl_tree_t *target_list)
{
isns_target_t *itarget, tmptgt;
avl_index_t where;
ASSERT(ISNS_GLOBAL_LOCK_HELD());
ASSERT(mutex_owned(&iscsit_isns_mutex));
tmptgt.target = jtarget->target;
if ((itarget = (isns_target_t *)avl_find(target_list,
&tmptgt, &where)) == NULL) {
itarget = kmem_zalloc(sizeof (isns_target_t), KM_SLEEP);
itarget->target = jtarget->target;
itarget->target_info = jtarget->target_info;
idm_refcnt_hold(&itarget->target_info->ti_refcnt);
avl_insert(target_list, (void *)itarget, where);
} else {
ASSERT(0);
}
return (itarget);
}
static void
isnst_clear_target_list(iscsit_isns_svr_t *svr)
{
isns_target_t *itarget;
while ((itarget = avl_first(&svr->svr_target_list)) != NULL) {
isnst_clear_from_target_list(itarget,
&svr->svr_target_list);
}
}
static void
isnst_clear_from_target_list(isns_target_t *jtarget, avl_tree_t *target_list)
{
isns_target_t *itarget, tmptgt;
tmptgt.target = jtarget->target;
if ((itarget = avl_find(target_list, &tmptgt, NULL))
!= NULL) {
avl_remove(target_list, itarget);
idm_refcnt_rele(&itarget->target_info->ti_refcnt);
kmem_free(itarget, sizeof (isns_target_t));
} else {
ASSERT(0);
}
}
static void
isnst_copy_global_status_changes(void)
{
isns_target_t *ttarget, *itarget, tmptgt;
iscsit_isns_svr_t *svr;
ASSERT(ISNS_GLOBAL_LOCK_HELD());
mutex_enter(&iscsit_isns_mutex);
isnst_monitor_default_portal_list();
for (svr = list_head(&iscsit_global.global_isns_cfg.isns_svrs);
svr != NULL;
svr = list_next(&iscsit_global.global_isns_cfg.isns_svrs,
svr)) {
if (isns_portals_changed && svr->svr_registered) {
svr->svr_reset_needed = B_TRUE;
}
if (!svr->svr_registered) {
isnst_clear_target_list(svr);
isns_targets_changed = B_TRUE;
} else if (isns_targets_changed || svr->svr_reset_needed) {
isnst_mark_deleted_targets(svr);
svr->svr_targets_changed = B_TRUE;
}
}
if (isns_targets_changed) {
ttarget = avl_first(&isns_target_list);
while (ttarget) {
for (svr = list_head(
&iscsit_global.global_isns_cfg.isns_svrs);
svr != NULL;
svr = list_next(
&iscsit_global.global_isns_cfg.isns_svrs,
svr)) {
tmptgt.target = ttarget->target;
itarget = avl_find(
&svr->svr_target_list,
&tmptgt, NULL);
if (itarget == NULL) {
(void) isnst_latch_to_target_list(
ttarget, &svr->svr_target_list);
} else if (ttarget->target_update_needed) {
itarget->target_update_needed =
B_TRUE;
idm_refcnt_rele(
&itarget->target_info->ti_refcnt);
itarget->target_info =
ttarget->target_info;
idm_refcnt_hold(
&itarget->target_info->ti_refcnt);
}
}
ttarget->target_update_needed = B_FALSE;
ttarget = AVL_NEXT(&isns_target_list, ttarget);
}
}
isns_targets_changed = B_FALSE;
isns_portals_changed = B_FALSE;
mutex_exit(&iscsit_isns_mutex);
}
static int
isnst_update_one_server(iscsit_isns_svr_t *svr, isns_target_t *itarget,
isns_reg_type_t reg)
{
int rc = 0;
ASSERT(ISNS_GLOBAL_LOCK_HELD());
ISNS_GLOBAL_UNLOCK();
switch (reg) {
case ISNS_DEREGISTER_TARGET:
rc = isnst_deregister(svr, itarget);
break;
case ISNS_DEREGISTER_ALL:
rc = isnst_deregister(svr, NULL);
break;
case ISNS_MODIFY_TARGET:
case ISNS_REGISTER_TARGET:
rc = isnst_register(svr, itarget, reg);
break;
case ISNS_REGISTER_ALL:
rc = isnst_register(svr, NULL, reg);
break;
default:
ASSERT(0);
}
ISNS_GLOBAL_LOCK();
return (rc);
}
static boolean_t
isnst_retry_registration(int rsp_status_code)
{
boolean_t retry;
switch (rsp_status_code) {
case ISNS_RSP_INVALID_REGIS:
case ISNS_RSP_SRC_UNAUTHORIZED:
case ISNS_RSP_BUSY:
case ISNS_RSP_INVALID_UPDATE:
case ISNS_RSP_NO_SUCH_ENTRY:
retry = B_TRUE;
break;
default:
retry = B_FALSE;
break;
}
return (retry);
}
static int
isnst_register(iscsit_isns_svr_t *svr, isns_target_t *itarget,
isns_reg_type_t regtype)
{
struct sonode *so;
int rc = 0;
isns_pdu_t *pdu, *rsp;
size_t pdu_size, rsp_size;
so = isnst_open_so(&svr->svr_sa);
if (so == NULL) {
return (-1);
}
pdu_size = isnst_make_reg_pdu(&pdu, itarget, svr, regtype);
if (pdu_size == 0) {
isnst_close_so(so);
return (-1);
}
rc = isnst_send_pdu(so, pdu);
if (rc != 0) {
kmem_free(pdu, pdu_size);
isnst_close_so(so);
return (rc);
}
rsp_size = isnst_rcv_pdu(so, &rsp);
if (rsp_size == 0) {
kmem_free(pdu, pdu_size);
isnst_close_so(so);
return (-1);
}
rc = isnst_verify_rsp(svr, pdu, rsp, rsp_size);
kmem_free(pdu, pdu_size);
kmem_free(rsp, rsp_size);
isnst_close_so(so);
return (rc);
}
static size_t
isnst_make_reg_pdu(isns_pdu_t **pdu, isns_target_t *itarget,
iscsit_isns_svr_t *svr, isns_reg_type_t regtype)
{
size_t pdu_size;
char *str;
int len;
isns_target_t *src;
boolean_t reg_all = B_FALSE;
uint16_t flags = 0;
ISNS_GLOBAL_LOCK();
ASSERT(svr->svr_monitor_hold);
if (avl_numnodes(&svr->svr_target_list) == 0) {
ISNS_GLOBAL_UNLOCK();
return (0);
}
if (itarget != NULL && ! svr->svr_registered) {
src = itarget;
} else if (svr->svr_registered) {
src = isnst_get_registered_source_locked(svr);
} else {
int i;
isns_target_t *jtarget;
if (svr->svr_last_target_index >=
avl_numnodes(&svr->svr_target_list) - 1) {
svr->svr_last_target_index = 0;
} else {
svr->svr_last_target_index++;
}
for (i = 0, jtarget = avl_first(&svr->svr_target_list);
i < svr->svr_last_target_index;
i++, jtarget = AVL_NEXT(&svr->svr_target_list, jtarget)) {
ASSERT(jtarget != NULL);
}
src = jtarget;
ASSERT(src != NULL);
}
if (itarget == NULL) {
reg_all = B_TRUE;
flags = ISNS_FLAG_REPLACE_REG;
itarget = (isns_target_t *)avl_first(&svr->svr_target_list);
} else if (regtype == ISNS_REGISTER_TARGET) {
flags = ISNS_FLAG_REPLACE_REG;
ASSERT(!itarget->target_delete_needed);
}
pdu_size = isnst_create_pdu_header(ISNS_DEV_ATTR_REG, pdu, flags);
if (pdu_size == 0) {
ISNS_GLOBAL_UNLOCK();
return (0);
}
len = strlen(src->target_info->ti_tgt_name) + 1;
if (isnst_add_attr(*pdu, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
len, src->target_info->ti_tgt_name, 0) != 0) {
goto pdu_error;
}
len = strlen(isns_eid) + 1;
if (isnst_add_attr(*pdu, pdu_size, ISNS_EID_ATTR_ID,
len, isns_eid, 0) != 0) {
goto pdu_error;
}
if (isnst_add_attr(*pdu, pdu_size, ISNS_DELIMITER_ATTR_ID,
0, 0, 0) != 0) {
goto pdu_error;
}
if (isnst_add_attr(*pdu, pdu_size, ISNS_EID_ATTR_ID, len,
isns_eid, 0) != 0) {
goto pdu_error;
}
if (isnst_add_attr(*pdu, pdu_size,
ISNS_ENTITY_PROTOCOL_ATTR_ID,
4, 0, ISNS_ENTITY_PROTOCOL_ISCSI) != 0) {
goto pdu_error;
}
if (reg_all) {
if (!isns_use_esi &&
isnst_add_attr(*pdu, pdu_size,
ISNS_ENTITY_REG_PERIOD_ATTR_ID, 4,
0, isns_registration_period) != 0) {
goto pdu_error;
}
if (isnst_reg_pdu_add_entity_portals(*pdu, pdu_size) != 0) {
goto pdu_error;
}
while (itarget->target_delete_needed) {
itarget = AVL_NEXT(&svr->svr_target_list,
itarget);
ASSERT(itarget != NULL);
}
}
do {
str = itarget->target_info->ti_tgt_name;
len = strlen(str) + 1;
if (isnst_add_attr(*pdu, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
len, str, 0) != 0) {
goto pdu_error;
}
if (isnst_add_attr(*pdu, pdu_size,
ISNS_ISCSI_NODE_TYPE_ATTR_ID, 4, 0,
ISNS_TARGET_NODE_TYPE) != 0) {
goto pdu_error;
}
str = itarget->target_info->ti_tgt_alias;
len = strnlen(str,
sizeof (itarget->target_info->ti_tgt_alias));
if (len) {
if (isnst_add_attr(*pdu, pdu_size,
ISNS_ISCSI_ALIAS_ATTR_ID, len+1, str, 0) != 0) {
goto pdu_error;
}
}
if (isnst_reg_pdu_add_pg(*pdu, pdu_size, itarget) != 0) {
goto pdu_error;
}
if (!reg_all) {
break;
}
do {
itarget = AVL_NEXT(&svr->svr_target_list, itarget);
} while (itarget != NULL && itarget->target_delete_needed);
} while (itarget != NULL);
ISNS_GLOBAL_UNLOCK();
return (pdu_size);
pdu_error:
len = ntohs((*pdu)->payload_len);
if (len + 1000 > isns_message_buf_size) {
if (isns_message_buf_size * 2 <= ISNST_MAX_MSG_SIZE) {
isns_message_buf_size *= 2;
ISNST_LOG(CE_NOTE,
"Increasing isns_message_buf_size to %d",
isns_message_buf_size);
} else {
cmn_err(CE_WARN, "iscsit: isns: no space"
" to send required PDU");
}
}
kmem_free(*pdu, pdu_size);
*pdu = NULL;
ISNS_GLOBAL_UNLOCK();
return (0);
}
static int
isnst_reg_pdu_add_entity_portals(isns_pdu_t *pdu, size_t pdu_size)
{
int rc = 0;
isns_portal_t *iportal;
ASSERT(ISNS_GLOBAL_LOCK_HELD());
iportal = (isns_portal_t *)avl_first(&isns_all_portals);
while (iportal != NULL) {
if (isnst_add_portal_attr(pdu, pdu_size,
ISNS_PORTAL_IP_ADDR_ATTR_ID,
ISNS_PORTAL_PORT_ATTR_ID,
&iportal->portal_addr,
isns_use_esi ) != 0) {
rc = -1;
break;
}
iportal = AVL_NEXT(&isns_all_portals, iportal);
}
return (rc);
}
static int
isnst_reg_pdu_add_pg(isns_pdu_t *pdu, size_t pdu_size, isns_target_t *itarget)
{
int rval = 0;
avl_tree_t null_portals;
isns_target_info_t *ti;
isns_tpgt_t *tig;
ASSERT(ISNS_GLOBAL_LOCK_HELD());
ti = itarget->target_info;
if (num_tpg_portals == 0)
return (0);
avl_create(&null_portals, isnst_portal_avl_compare,
sizeof (isns_portal_t), offsetof(isns_portal_t, portal_node));
isnst_copy_portal_list(&isns_all_portals, &null_portals);
for (tig = list_head(&ti->ti_tpgt_list);
tig != NULL;
tig = list_next(&ti->ti_tpgt_list, tig)) {
if (tig->ti_tpgt_tag == ISCSIT_DEFAULT_TPGT) {
if (isnst_add_default_pg(pdu, pdu_size,
&null_portals) != 0) {
rval = 1;
break;
}
} else {
if (isnst_add_tpg_pg(pdu, pdu_size, tig,
&null_portals) != 0) {
rval = 1;
break;
}
}
}
if (rval == 0 &&
isnst_add_null_pg(pdu, pdu_size, &null_portals) != 0) {
rval = 1;
}
isnst_clear_portal_list(&null_portals);
avl_destroy(&null_portals);
return (rval);
}
static int
isnst_add_tpg_pg(isns_pdu_t *pdu, size_t pdu_size,
isns_tpgt_t *tig, avl_tree_t *null_portal_list)
{
isns_tpgt_addr_t *tip;
int rval = 0;
ASSERT(ISNS_GLOBAL_LOCK_HELD());
ASSERT(tig->ti_tpgt_tag != ISCSIT_DEFAULT_TPGT);
if (isnst_add_attr(pdu, pdu_size,
ISNS_PG_TAG_ATTR_ID, 4, 0, tig->ti_tpgt_tag) != 0) {
rval = 1;
goto pg_done;
}
tip = list_head(&tig->ti_portal_list);
ASSERT(tip != NULL);
do {
if (isnst_add_portal_attr(pdu, pdu_size,
ISNS_PG_PORTAL_IP_ADDR_ATTR_ID,
ISNS_PG_PORTAL_PORT_ATTR_ID,
&tip->portal_addr, B_FALSE ) != 0) {
rval = 1;
goto pg_done;
}
isnst_remove_from_portal_list(&tip->portal_addr,
null_portal_list);
tip = list_next(&tig->ti_portal_list, tip);
} while (tip != NULL);
pg_done:
return (rval);
}
static int
isnst_add_default_pg(isns_pdu_t *pdu, size_t pdu_size,
avl_tree_t *null_portal_list)
{
isns_portal_t *iportal;
ASSERT(ISNS_GLOBAL_LOCK_HELD());
if (num_default_portals == 0) {
return (0);
}
if (isnst_add_attr(pdu, pdu_size,
ISNS_PG_TAG_ATTR_ID, 4, 0, ISCSIT_DEFAULT_TPGT) != 0) {
return (1);
}
for (iportal = avl_first(&isns_all_portals);
iportal != NULL;
iportal = AVL_NEXT(&isns_all_portals, iportal)) {
if (iportal->portal_default) {
if (isnst_add_portal_attr(pdu, pdu_size,
ISNS_PG_PORTAL_IP_ADDR_ATTR_ID,
ISNS_PG_PORTAL_PORT_ATTR_ID,
&iportal->portal_addr, B_FALSE) != 0) {
return (1);
}
isnst_remove_from_portal_list(&iportal->portal_addr,
null_portal_list);
}
}
return (0);
}
static int
isnst_add_null_pg(isns_pdu_t *pdu, size_t pdu_size,
avl_tree_t *null_portal_list)
{
isns_portal_t *iportal;
if (avl_numnodes(null_portal_list) == 0) {
return (0);
}
if (isnst_add_attr(pdu, pdu_size,
ISNS_PG_TAG_ATTR_ID, 0, 0, 0) != 0) {
return (1);
}
for (iportal = avl_first(null_portal_list);
iportal != NULL;
iportal = AVL_NEXT(null_portal_list, iportal)) {
if (isnst_add_portal_attr(pdu, pdu_size,
ISNS_PG_PORTAL_IP_ADDR_ATTR_ID,
ISNS_PG_PORTAL_PORT_ATTR_ID,
&iportal->portal_addr, B_FALSE) != 0) {
return (1);
}
}
return (0);
}
static int
isnst_add_portal_attr(isns_pdu_t *pdu, size_t pdu_size,
uint32_t ip_attr_id, uint32_t port_attr_id,
struct sockaddr_storage *ss, boolean_t esi_info)
{
struct sockaddr_in *in;
struct sockaddr_in6 *in6;
uint32_t attr_numeric_data;
void *inaddrp;
in = (struct sockaddr_in *)ss;
in6 = (struct sockaddr_in6 *)ss;
ASSERT((ss->ss_family == AF_INET) || (ss->ss_family == AF_INET6));
if (ss->ss_family == AF_INET) {
attr_numeric_data = sizeof (in_addr_t);
inaddrp = (void *)&in->sin_addr;
} else if (ss->ss_family == AF_INET6) {
attr_numeric_data = sizeof (in6_addr_t);
inaddrp = (void *)&in6->sin6_addr;
}
if (isnst_add_attr(pdu, pdu_size, ip_attr_id,
16, inaddrp, attr_numeric_data) != 0) {
return (1);
}
if (isnst_add_attr(pdu, pdu_size, port_attr_id,
4, 0, ntohs(in->sin_port)) != 0) {
return (1);
}
mutex_enter(&esi.esi_mutex);
if (esi_info && esi.esi_valid) {
if (isnst_add_attr(pdu, pdu_size, ISNS_ESI_INTERVAL_ATTR_ID, 4,
NULL, isns_default_esi_interval) != 0) {
return (1);
}
if (isnst_add_attr(pdu, pdu_size, ISNS_ESI_PORT_ATTR_ID, 4,
NULL, esi.esi_port) != 0) {
return (1);
}
}
mutex_exit(&esi.esi_mutex);
return (0);
}
static int
isnst_deregister(iscsit_isns_svr_t *svr, isns_target_t *itarget)
{
int rc;
isns_pdu_t *pdu, *rsp;
size_t pdu_size, rsp_size;
struct sonode *so;
so = isnst_open_so(&svr->svr_sa);
if (so == NULL) {
return (-1);
}
pdu_size = isnst_make_dereg_pdu(svr, &pdu, itarget);
if (pdu_size == 0) {
isnst_close_so(so);
return (-1);
}
rc = isnst_send_pdu(so, pdu);
if (rc != 0) {
isnst_close_so(so);
kmem_free(pdu, pdu_size);
return (rc);
}
rsp_size = isnst_rcv_pdu(so, &rsp);
if (rsp_size == 0) {
isnst_close_so(so);
kmem_free(pdu, pdu_size);
return (-1);
}
rc = isnst_verify_rsp(svr, pdu, rsp, rsp_size);
isnst_close_so(so);
kmem_free(pdu, pdu_size);
kmem_free(rsp, rsp_size);
return (rc);
}
static int
isnst_keepalive(iscsit_isns_svr_t *svr)
{
int rc;
isns_pdu_t *pdu, *rsp;
size_t pdu_size, rsp_size;
struct sonode *so;
so = isnst_open_so(&svr->svr_sa);
if (so == NULL) {
return (-1);
}
pdu_size = isnst_make_keepalive_pdu(svr, &pdu);
if (pdu_size == 0) {
isnst_close_so(so);
return (-1);
}
rc = isnst_send_pdu(so, pdu);
if (rc != 0) {
isnst_close_so(so);
kmem_free(pdu, pdu_size);
return (rc);
}
rsp_size = isnst_rcv_pdu(so, &rsp);
if (rsp_size == 0) {
isnst_close_so(so);
kmem_free(pdu, pdu_size);
return (-1);
}
rc = isnst_verify_rsp(svr, pdu, rsp, rsp_size);
isnst_close_so(so);
kmem_free(pdu, pdu_size);
kmem_free(rsp, rsp_size);
return (rc);
}
static size_t
isnst_make_dereg_pdu(iscsit_isns_svr_t *svr, isns_pdu_t **pdu,
isns_target_t *itarget)
{
size_t pdu_size;
int len;
isns_target_t *src;
pdu_size = isnst_create_pdu_header(ISNS_DEV_DEREG, pdu, 0);
if (pdu_size == 0) {
return (0);
}
if (svr->svr_registered) {
src = isnst_get_registered_source(svr);
} else if (itarget != NULL) {
src = itarget;
} else {
goto dereg_pdu_error;
}
len = strlen(src->target_info->ti_tgt_name) + 1;
if (isnst_add_attr(*pdu, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
len, src->target_info->ti_tgt_name, 0) != 0) {
goto dereg_pdu_error;
}
if (isnst_add_attr(*pdu, pdu_size, ISNS_DELIMITER_ATTR_ID,
0, 0, 0) != 0) {
goto dereg_pdu_error;
}
if (itarget == NULL) {
len = strlen(isns_eid) + 1;
if (isnst_add_attr(*pdu, pdu_size, ISNS_EID_ATTR_ID,
len, isns_eid, 0) != 0) {
goto dereg_pdu_error;
}
} else {
len = strlen(itarget->target_info->ti_tgt_name) + 1;
if (isnst_add_attr(*pdu, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
len, itarget->target_info->ti_tgt_name, 0) != 0) {
goto dereg_pdu_error;
}
}
return (pdu_size);
dereg_pdu_error:
kmem_free(*pdu, pdu_size);
*pdu = NULL;
return (0);
}
static size_t
isnst_make_keepalive_pdu(iscsit_isns_svr_t *svr, isns_pdu_t **pdu)
{
size_t pdu_size;
int len;
isns_target_t *src;
ASSERT(svr->svr_registered);
pdu_size = isnst_create_pdu_header(ISNS_DEV_ATTR_QRY, pdu, 0);
if (pdu_size == 0) {
return (0);
}
src = isnst_get_registered_source(svr);
len = strlen(src->target_info->ti_tgt_name) + 1;
if (isnst_add_attr(*pdu, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
len, src->target_info->ti_tgt_name, 0) != 0) {
goto keepalive_pdu_error;
}
len = strlen(isns_eid) + 1;
if (isnst_add_attr(*pdu, pdu_size, ISNS_EID_ATTR_ID,
len, isns_eid, 0) != 0) {
goto keepalive_pdu_error;
}
if (isnst_add_attr(*pdu, pdu_size, ISNS_DELIMITER_ATTR_ID,
0, 0, 0) != 0) {
goto keepalive_pdu_error;
}
if (isnst_add_attr(*pdu, pdu_size, ISNS_EID_ATTR_ID,
0, 0, 0) != 0) {
goto keepalive_pdu_error;
}
return (pdu_size);
keepalive_pdu_error:
kmem_free(*pdu, pdu_size);
*pdu = NULL;
return (0);
}
static isns_target_t *
isnst_get_registered_source(iscsit_isns_svr_t *svr)
{
isns_target_t *itarget;
ISNS_GLOBAL_LOCK();
ASSERT(svr->svr_monitor_hold);
itarget = isnst_get_registered_source_locked(svr);
ISNS_GLOBAL_UNLOCK();
return (itarget);
}
static isns_target_t *
isnst_get_registered_source_locked(iscsit_isns_svr_t *svr)
{
isns_target_t *itarget;
ASSERT(ISNS_GLOBAL_LOCK_HELD());
ASSERT(svr->svr_registered);
ASSERT((avl_numnodes(&svr->svr_target_list) != 0));
itarget = avl_first(&svr->svr_target_list);
do {
if (itarget->target_registered == B_TRUE)
break;
itarget = AVL_NEXT(&svr->svr_target_list, itarget);
} while (itarget != NULL);
ASSERT(itarget != NULL);
return (itarget);
}
static int
isnst_verify_rsp(iscsit_isns_svr_t *svr, isns_pdu_t *pdu,
isns_pdu_t *rsp, size_t rsp_size)
{
uint16_t func_id;
int payload_len, rsp_payload_len;
int status;
isns_resp_t *resp;
uint8_t *pp;
isns_tlv_t *attr;
uint32_t attr_len, attr_id, esi_interval;
if (rsp_size < offsetof(isns_pdu_t, payload)) {
ISNST_LOG(CE_WARN, "Invalid iSNS PDU header, %d of %d bytes",
(int)rsp_size, (int)offsetof(isns_pdu_t, payload));
return (-1);
}
payload_len = ntohs(rsp->payload_len);
if (rsp_size < (payload_len + offsetof(isns_pdu_t, payload))) {
ISNST_LOG(CE_WARN, "Invalid iSNS response, %d of %d bytes",
(int)rsp_size,
(int)(payload_len + offsetof(isns_pdu_t, payload)));
return (-1);
}
rsp_payload_len = isnst_pdu_get_op(rsp, &pp);
if (pp == NULL) {
return (-1);
}
if (ntohs(rsp->xid) != ntohs(pdu->xid)) {
return (-1);
}
resp = (isns_resp_t *)((void *)&rsp->payload[0]);
status = ntohl(resp->status);
func_id = ntohs(rsp->func_id);
switch (ntohs(pdu->func_id)) {
case ISNS_DEV_ATTR_REG:
if (func_id != ISNS_DEV_ATTR_REG_RSP) {
return (-1);
}
if (status != 0) {
break;
}
attr = (isns_tlv_t *)((void *)pp);
while (rsp_payload_len >= 8) {
attr_len = ntohl(attr->attr_len);
attr_id = ntohl(attr->attr_id);
if (attr_id == ISNS_ESI_INTERVAL_ATTR_ID) {
if (attr_len != 4 ||
attr_len > rsp_payload_len - 8) {
return (-1);
}
esi_interval =
ntohl(*((uint32_t *)
((void *)(&attr->attr_value))));
ISNS_GLOBAL_LOCK();
ASSERT(svr->svr_monitor_hold);
if (esi_interval > svr->svr_esi_interval)
svr->svr_esi_interval = esi_interval;
ISNS_GLOBAL_UNLOCK();
break;
}
rsp_payload_len -= (8 + attr_len);
attr = (isns_tlv_t *)
((void *)((uint8_t *)attr + attr_len + 8));
}
break;
case ISNS_DEV_DEREG:
if (func_id != ISNS_DEV_DEREG_RSP) {
return (-1);
}
break;
case ISNS_DEV_ATTR_QRY:
if (func_id != ISNS_DEV_ATTR_QRY_RSP) {
return (-1);
}
if (status == 0) {
boolean_t found_eid = B_FALSE;
attr = (isns_tlv_t *)((void *)pp);
while (rsp_payload_len >= 8) {
attr_len = ntohl(attr->attr_len);
attr_id = ntohl(attr->attr_id);
if (attr_id == ISNS_EID_ATTR_ID &&
attr_len > 0 &&
attr_len <= rsp_payload_len - 8) {
found_eid = B_TRUE;
break;
}
rsp_payload_len -= (8 + attr_len);
attr = (isns_tlv_t *)
((void *)((uint8_t *)attr + attr_len + 8));
}
if (! found_eid) {
status = ISNS_RSP_NO_SUCH_ENTRY;
}
}
if (status == ISNS_RSP_NO_SUCH_ENTRY) {
char server_buf[IDM_SA_NTOP_BUFSIZ];
ISNST_LOG(CE_WARN, "iscsit: iSNS server %s"
" forgot about us and has to be reminded.",
idm_sa_ntop(&svr->svr_sa,
server_buf, sizeof (server_buf)));
}
break;
default:
ASSERT(0);
break;
}
if (status == 0) {
ISNS_GLOBAL_LOCK();
ASSERT(svr->svr_monitor_hold);
svr->svr_last_msg = ddi_get_lbolt();
ISNS_GLOBAL_UNLOCK();
}
return (status);
}
static uint16_t
isnst_pdu_get_op(isns_pdu_t *pdu, uint8_t **pp)
{
uint8_t *payload;
uint16_t payload_len;
isns_resp_t *resp;
isns_tlv_t *attr;
uint32_t attr_id;
uint32_t tlv_len;
payload_len = ntohs(pdu->payload_len);
resp = (isns_resp_t *)((void *)&pdu->payload[0]);
if (payload_len < sizeof (resp->status)) {
ISNST_LOG(CE_WARN, "Invalid iSNS response, %d payload bytes",
payload_len);
*pp = NULL;
return (0);
}
payload_len -= sizeof (resp->status);
payload = &resp->data[0];
while (payload_len >= (sizeof (isns_tlv_t) - 1)) {
attr = (isns_tlv_t *)((void *)payload);
tlv_len = offsetof(isns_tlv_t, attr_value) +
ntohl(attr->attr_len);
if (payload_len >= tlv_len) {
payload += tlv_len;
payload_len -= tlv_len;
attr_id = ntohl(attr->attr_id);
if (attr_id == ISNS_DELIMITER_ATTR_ID) {
break;
}
} else {
payload = NULL;
payload_len = 0;
}
}
*pp = payload;
return (payload_len);
}
static size_t
isnst_create_pdu_header(uint16_t func_id, isns_pdu_t **pdu, uint16_t flags)
{
size_t pdu_size = isns_message_buf_size;
*pdu = (isns_pdu_t *)kmem_zalloc(pdu_size, KM_NOSLEEP);
if (*pdu != NULL) {
(*pdu)->version = htons((uint16_t)ISNSP_VERSION);
(*pdu)->func_id = htons((uint16_t)func_id);
(*pdu)->payload_len = htons(0);
(*pdu)->flags = htons(flags);
(*pdu)->xid = htons(GET_XID());
(*pdu)->seq = htons(0);
} else {
pdu_size = 0;
}
return (pdu_size);
}
static int
isnst_add_attr(isns_pdu_t *pdu,
size_t max_pdu_size,
uint32_t attr_id,
uint32_t attr_len,
void *attr_data,
uint32_t attr_numeric_data)
{
isns_tlv_t *attr_tlv;
uint8_t *payload_ptr;
uint16_t payload_len;
uint32_t normalized_attr_len;
uint64_t attr_tlv_len;
normalized_attr_len = (attr_len % 4) == 0 ?
(attr_len) : (attr_len + (4 - (attr_len % 4)));
attr_tlv_len = ISNS_TLV_ATTR_ID_LEN +
ISNS_TLV_ATTR_LEN_LEN + normalized_attr_len;
payload_len = ntohs(pdu->payload_len);
if ((payload_len + attr_tlv_len) > max_pdu_size) {
return (1);
}
attr_tlv = (isns_tlv_t *)kmem_zalloc(attr_tlv_len, KM_SLEEP);
attr_tlv->attr_id = htonl(attr_id);
switch (attr_id) {
case ISNS_DELIMITER_ATTR_ID:
break;
case ISNS_PORTAL_IP_ADDR_ATTR_ID:
case ISNS_PG_PORTAL_IP_ADDR_ATTR_ID:
if (attr_numeric_data == sizeof (in_addr_t)) {
attr_tlv->attr_value[10] = 0xFF;
attr_tlv->attr_value[11] = 0xFF;
bcopy(attr_data, ((attr_tlv->attr_value) + 12),
sizeof (in_addr_t));
} else if (attr_numeric_data == sizeof (in6_addr_t)) {
bcopy(attr_data, attr_tlv->attr_value,
sizeof (in6_addr_t));
} else if (attr_numeric_data == 0) {
} else {
kmem_free(attr_tlv, attr_tlv_len);
attr_tlv = NULL;
return (1);
}
break;
case ISNS_EID_ATTR_ID:
case ISNS_ISCSI_NAME_ATTR_ID:
case ISNS_ISCSI_ALIAS_ATTR_ID:
case ISNS_PG_ISCSI_NAME_ATTR_ID:
if (attr_len && attr_data) {
bcopy((char *)attr_data,
attr_tlv->attr_value, attr_len);
}
break;
default:
if (attr_len == 8) {
*(uint64_t *)((void *)attr_tlv->attr_value) =
BE_64((uint64_t)attr_numeric_data);
} else if (attr_len == 4) {
*(uint32_t *)((void *)attr_tlv->attr_value) =
htonl((uint32_t)attr_numeric_data);
}
break;
}
attr_tlv->attr_len = htonl(normalized_attr_len);
payload_len = ntohs(pdu->payload_len);
payload_ptr = pdu->payload + payload_len;
bcopy(attr_tlv, payload_ptr, attr_tlv_len);
payload_len += attr_tlv_len;
pdu->payload_len = htons(payload_len);
kmem_free(attr_tlv, attr_tlv_len);
attr_tlv = NULL;
return (0);
}
static void
isnst_so_timeout(void *so)
{
idm_soshutdown(so);
}
static int
isnst_send_pdu(void *so, isns_pdu_t *pdu)
{
size_t total_len, payload_len, send_len;
uint8_t *payload;
uint16_t flags, seq;
timeout_id_t send_timer;
iovec_t iov[2];
int rc;
ASSERT(! ISNS_GLOBAL_LOCK_HELD());
flags = ntohs(pdu->flags);
flags |= ISNS_FLAG_CLIENT;
flags |= ISNS_FLAG_FIRST_PDU;
seq = 0;
payload = pdu->payload;
total_len = ntohs(pdu->payload_len);
iov[0].iov_base = (void *)pdu;
iov[0].iov_len = ISNSP_HEADER_SIZE;
do {
if (total_len > ISNSP_MAX_PAYLOAD_SIZE) {
payload_len = ISNSP_MAX_PAYLOAD_SIZE;
} else {
payload_len = total_len;
flags |= ISNS_FLAG_LAST_PDU;
}
pdu->flags = htons(flags);
pdu->seq = htons(seq);
pdu->payload_len = htons(payload_len);
iov[1].iov_base = (void *)payload;
iov[1].iov_len = payload_len;
DTRACE_PROBE3(isnst__pdu__send, uint16_t, ntohs(pdu->func_id),
uint16_t, ntohs(pdu->payload_len), caddr_t, pdu);
send_len = ISNSP_HEADER_SIZE + payload_len;
send_timer = timeout(isnst_so_timeout, so,
drv_usectohz(isns_timeout_usec));
rc = idm_iov_sosend(so, &iov[0], 2, send_len);
(void) untimeout(send_timer);
flags &= ~ISNS_FLAG_FIRST_PDU;
payload += payload_len;
total_len -= payload_len;
seq ++;
} while (rc == 0 && total_len > 0);
return (rc);
}
static size_t
isnst_rcv_pdu(void *so, isns_pdu_t **pdu)
{
size_t total_pdu_len;
size_t total_payload_len;
size_t payload_len;
size_t combined_len;
isns_pdu_t tmp_pdu_hdr;
isns_pdu_t *combined_pdu;
uint8_t *payload;
uint8_t *combined_payload;
timeout_id_t rcv_timer;
uint16_t flags;
uint16_t seq;
ASSERT(! ISNS_GLOBAL_LOCK_HELD());
*pdu = NULL;
total_pdu_len = total_payload_len = 0;
payload = NULL;
seq = 0;
do {
rcv_timer = timeout(isnst_so_timeout, so,
drv_usectohz(isns_timeout_usec));
if (idm_sorecv(so, &tmp_pdu_hdr, ISNSP_HEADER_SIZE) != 0 ||
ntohs(tmp_pdu_hdr.seq) != seq) {
(void) untimeout(rcv_timer);
goto rcv_error;
}
(void) untimeout(rcv_timer);
payload_len = ntohs(tmp_pdu_hdr.payload_len);
if (payload_len > ISNST_MAX_MSG_SIZE) {
goto rcv_error;
}
payload = kmem_alloc(payload_len, KM_NOSLEEP);
if (payload == NULL) {
goto rcv_error;
}
rcv_timer = timeout(isnst_so_timeout, so,
drv_usectohz(ISNS_RCV_TIMER_SECONDS * 1000000));
if (idm_sorecv(so, payload, payload_len) != 0) {
(void) untimeout(rcv_timer);
goto rcv_error;
}
(void) untimeout(rcv_timer);
if (total_pdu_len > 0) {
combined_len = total_pdu_len + payload_len;
combined_pdu = kmem_alloc(combined_len, KM_SLEEP);
if (combined_pdu == NULL) {
goto rcv_error;
}
bcopy(*pdu, combined_pdu, total_pdu_len);
combined_payload =
&combined_pdu->payload[total_payload_len];
bcopy(payload, combined_payload, payload_len);
kmem_free(*pdu, total_pdu_len);
kmem_free(payload, payload_len);
*pdu = combined_pdu;
total_payload_len += payload_len;
total_pdu_len += payload_len;
(*pdu)->payload_len = htons(total_payload_len);
} else {
total_payload_len = payload_len;
total_pdu_len = ISNSP_HEADER_SIZE + payload_len;
*pdu = kmem_alloc(total_pdu_len, KM_NOSLEEP);
if (*pdu == NULL) {
goto rcv_error;
}
bcopy(&tmp_pdu_hdr, *pdu, ISNSP_HEADER_SIZE);
bcopy(payload, &(*pdu)->payload[0], payload_len);
kmem_free(payload, payload_len);
}
payload = NULL;
flags = ntohs(tmp_pdu_hdr.flags);
seq ++;
} while ((flags & ISNS_FLAG_LAST_PDU) == 0);
DTRACE_PROBE3(isnst__pdu__recv, uint16_t, ntohs((*pdu)->func_id),
size_t, total_payload_len, caddr_t, *pdu);
return (total_pdu_len);
rcv_error:
if (*pdu != NULL) {
kmem_free(*pdu, total_pdu_len);
*pdu = NULL;
}
if (payload != NULL) {
kmem_free(payload, payload_len);
}
return (0);
}
static void *
isnst_open_so(struct sockaddr_storage *sa)
{
int sa_sz;
ksocket_t so;
ASSERT(! ISNS_GLOBAL_LOCK_HELD());
if (sa->ss_family == AF_INET) {
sa_sz = sizeof (struct sockaddr_in);
so = idm_socreate(AF_INET, SOCK_STREAM, 0);
} else {
sa_sz = sizeof (struct sockaddr_in6);
so = idm_socreate(AF_INET6, SOCK_STREAM, 0);
}
if (so != NULL) {
if (idm_so_timed_socket_connect(so, sa, sa_sz,
isns_timeout_usec) != 0) {
idm_soshutdown(so);
idm_sodestroy(so);
so = NULL;
}
}
if (so == NULL) {
char server_buf[IDM_SA_NTOP_BUFSIZ];
ISNST_LOG(CE_WARN, "open iSNS Server %s failed",
idm_sa_ntop(sa, server_buf,
sizeof (server_buf)));
DTRACE_PROBE1(isnst__connect__fail,
struct sockaddr_storage *, sa);
}
return (so);
}
static void
isnst_close_so(void *so)
{
idm_soshutdown(so);
idm_sodestroy(so);
}
static void
isnst_esi_start(void)
{
if (isns_use_esi == B_FALSE) {
ISNST_LOG(CE_NOTE, "ESI disabled by isns_use_esi=FALSE");
return;
}
ISNST_LOG(CE_NOTE, "isnst_esi_start");
mutex_enter(&esi.esi_mutex);
ASSERT(esi.esi_enabled == B_FALSE);
ASSERT(esi.esi_thread_running == B_FALSE);
esi.esi_enabled = B_TRUE;
esi.esi_valid = B_FALSE;
esi.esi_thread = thread_create(NULL, 0, isnst_esi_thread,
(void *)&esi, 0, &p0, TS_RUN, minclsyspri);
while (!esi.esi_thread_running) {
cv_wait(&esi.esi_cv, &esi.esi_mutex);
}
mutex_exit(&esi.esi_mutex);
}
static void
isnst_esi_stop()
{
boolean_t need_offline = B_FALSE;
ISNST_LOG(CE_NOTE, "isnst_esi_stop");
mutex_enter(&esi.esi_mutex);
if (esi.esi_enabled) {
esi.esi_enabled = B_FALSE;
if (esi.esi_valid) {
need_offline = B_TRUE;
}
mutex_exit(&esi.esi_mutex);
if (need_offline) {
idm_soshutdown(esi.esi_so);
idm_sodestroy(esi.esi_so);
}
thread_join(esi.esi_thread_did);
} else {
mutex_exit(&esi.esi_mutex);
}
}
static void
isnst_esi_thread(void *arg)
{
ksocket_t newso;
struct sockaddr_in6 sin6;
socklen_t sin_addrlen;
uint32_t on = 1;
int rc;
isns_pdu_t *pdu;
size_t pl_size;
bzero(&sin6, sizeof (struct sockaddr_in6));
sin_addrlen = sizeof (struct sockaddr_in6);
esi.esi_thread_did = curthread->t_did;
mutex_enter(&esi.esi_mutex);
esi.esi_thread_running = B_TRUE;
cv_signal(&esi.esi_cv);
while (esi.esi_enabled) {
if ((esi.esi_so = idm_socreate(PF_INET6, SOCK_STREAM, 0)) ==
NULL) {
ISNST_LOG(CE_WARN,
"isnst_esi_thread: Unable to create socket");
mutex_exit(&esi.esi_mutex);
delay(drv_usectohz(1000000));
mutex_enter(&esi.esi_mutex);
continue;
}
bzero(&sin6, sizeof (sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_port = htons(0);
sin6.sin6_addr = in6addr_any;
(void) ksocket_setsockopt(esi.esi_so, SOL_SOCKET,
SO_REUSEADDR, (char *)&on, sizeof (on), CRED());
if (ksocket_bind(esi.esi_so, (struct sockaddr *)&sin6,
sizeof (sin6), CRED()) != 0) {
ISNST_LOG(CE_WARN, "Unable to bind socket for ESI");
idm_sodestroy(esi.esi_so);
mutex_exit(&esi.esi_mutex);
delay(drv_usectohz(1000000));
mutex_enter(&esi.esi_mutex);
continue;
}
(void) ksocket_getsockname(esi.esi_so,
(struct sockaddr *)(&sin6), &sin_addrlen, CRED());
esi.esi_port =
ntohs(((struct sockaddr_in6 *)(&sin6))->sin6_port);
if ((rc = ksocket_listen(esi.esi_so, 5, CRED())) != 0) {
ISNST_LOG(CE_WARN, "isnst_esi_thread: listen "
"failure 0x%x", rc);
idm_sodestroy(esi.esi_so);
mutex_exit(&esi.esi_mutex);
delay(drv_usectohz(1000000));
mutex_enter(&esi.esi_mutex);
continue;
}
ksocket_hold(esi.esi_so);
esi.esi_valid = B_TRUE;
while (esi.esi_enabled) {
mutex_exit(&esi.esi_mutex);
DTRACE_PROBE3(iscsit__isns__esi__accept__wait,
boolean_t, esi.esi_enabled,
ksocket_t, esi.esi_so,
struct sockaddr_in6, &sin6);
if ((rc = ksocket_accept(esi.esi_so, NULL, NULL,
&newso, CRED())) != 0) {
mutex_enter(&esi.esi_mutex);
DTRACE_PROBE2(iscsit__isns__esi__accept__fail,
int, rc, boolean_t, esi.esi_enabled);
ISNST_LOG(CE_WARN, "isnst_esi_thread: "
"accept failure (0x%x)", rc);
if (rc == EINTR) {
continue;
} else {
break;
}
}
DTRACE_PROBE2(iscsit__isns__esi__accept,
boolean_t, esi.esi_enabled,
ksocket_t, newso);
pl_size = isnst_rcv_pdu(newso, &pdu);
if (pl_size == 0) {
ISNST_LOG(CE_WARN, "isnst_esi_thread: "
"rcv_pdu failure");
idm_soshutdown(newso);
idm_sodestroy(newso);
mutex_enter(&esi.esi_mutex);
continue;
}
isnst_handle_esi_req(newso, pdu, pl_size);
idm_soshutdown(newso);
idm_sodestroy(newso);
mutex_enter(&esi.esi_mutex);
}
idm_soshutdown(esi.esi_so);
ksocket_rele(esi.esi_so);
esi.esi_valid = B_FALSE;
if (esi.esi_enabled)
idm_sodestroy(esi.esi_so);
}
esi.esi_thread_running = B_FALSE;
cv_signal(&esi.esi_cv);
mutex_exit(&esi.esi_mutex);
thread_exit();
}
static void
isnst_handle_esi_req(ksocket_t ks, isns_pdu_t *pdu, size_t pdu_size)
{
isns_pdu_t *rsp_pdu;
isns_resp_t *rsp;
isns_tlv_t *attr;
uint32_t attr_len, attr_id;
size_t req_pl_len, rsp_size, tlv_len;
struct sockaddr_storage portal_ss;
struct sockaddr_storage server_ss;
struct sockaddr_in6 *portal_addr6;
boolean_t portal_addr_valid = B_FALSE;
boolean_t portal_port_valid = B_FALSE;
uint32_t esi_response = ISNS_RSP_SUCCESSFUL;
isns_portal_t *iportal;
socklen_t sa_len;
if (ntohs(pdu->func_id) != ISNS_ESI) {
ISNST_LOG(CE_WARN, "isnst_handle_esi_req: Unexpected func 0x%x",
pdu->func_id);
kmem_free(pdu, pdu_size);
return;
}
req_pl_len = ntohs(pdu->payload_len);
if (req_pl_len + offsetof(isns_pdu_t, payload) > pdu_size) {
ISNST_LOG(CE_WARN, "isnst_handle_esi_req: "
"payload exceeds PDU size (%d > %d)",
(int)(req_pl_len + offsetof(isns_pdu_t, payload)),
(int)pdu_size);
kmem_free(pdu, pdu_size);
return;
}
if (req_pl_len + sizeof (uint32_t) > ISNSP_MAX_PAYLOAD_SIZE) {
ISNST_LOG(CE_WARN,
"isnst_handle_esi_req: PDU payload exceeds max (%ld bytes)",
req_pl_len + sizeof (uint32_t));
kmem_free(pdu, pdu_size);
return;
}
bzero(&portal_ss, sizeof (struct sockaddr_storage));
portal_ss.ss_family = AF_INET6;
portal_addr6 = (struct sockaddr_in6 *)&portal_ss;
attr = (isns_tlv_t *)((void *)&pdu->payload);
attr_len = ntohl(attr->attr_len);
attr_id = ntohl(attr->attr_id);
tlv_len = attr_len + offsetof(isns_tlv_t, attr_value);
while (tlv_len <= req_pl_len) {
switch (attr_id) {
case ISNS_TIMESTAMP_ATTR_ID:
break;
case ISNS_EID_ATTR_ID:
break;
case ISNS_PORTAL_IP_ADDR_ATTR_ID:
if (attr_len > sizeof (struct in6_addr)) {
esi_response = ISNS_RSP_MSG_FORMAT_ERROR;
} else {
portal_addr6->sin6_family = AF_INET6;
attr_len = min(attr_len,
sizeof (portal_addr6->sin6_addr));
bcopy(attr->attr_value,
portal_addr6->sin6_addr.s6_addr, attr_len);
portal_addr_valid = B_TRUE;
}
break;
case ISNS_PORTAL_PORT_ATTR_ID:
if (attr_len > sizeof (uint32_t)) {
esi_response = ISNS_RSP_MSG_FORMAT_ERROR;
} else {
portal_addr6->sin6_port =
htons((uint16_t)BE_IN32(attr->attr_value));
portal_port_valid = B_TRUE;
}
break;
default:
esi_response = ISNS_RSP_MSG_FORMAT_ERROR;
break;
}
if (esi_response != ISNS_RSP_SUCCESSFUL) {
break;
}
req_pl_len -= tlv_len;
attr = (isns_tlv_t *)((void *)((uint8_t *)attr + tlv_len));
attr_len = ntohl(attr->attr_len);
attr_id = ntohl(attr->attr_id);
tlv_len = attr_len + offsetof(isns_tlv_t, attr_value);
}
if (!portal_port_valid)
portal_addr6->sin6_port = htons(ISCSI_LISTEN_PORT);
if (!portal_addr_valid) {
esi_response = ISNS_RSP_MSG_FORMAT_ERROR;
}
if (esi_response != ISNS_RSP_SUCCESSFUL) {
kmem_free(pdu, pdu_size);
return;
}
bzero(&server_ss, sizeof (server_ss));
sa_len = sizeof (server_ss);
if (ksocket_getpeername(ks, (struct sockaddr *)&server_ss, &sa_len,
CRED())) {
return;
}
if (iscsit_isns_logging) {
char server_buf[IDM_SA_NTOP_BUFSIZ];
char portal_buf[IDM_SA_NTOP_BUFSIZ];
ISNST_LOG(CE_NOTE, "ESI: svr %s -> portal %s",
idm_sa_ntop(&server_ss, server_buf,
sizeof (server_buf)),
idm_sa_ntop(&portal_ss, portal_buf,
sizeof (portal_buf)));
}
ISNS_GLOBAL_LOCK();
if (isnst_lookup_portal(&portal_ss) == NULL) {
ISNST_LOG(CE_WARN, "ESI req to non-active portal");
ISNS_GLOBAL_UNLOCK();
kmem_free(pdu, pdu_size);
return;
}
if (! isnst_update_server_timestamp(&server_ss)) {
ISNST_LOG(CE_WARN, "ESI req from unknown server");
kmem_free(pdu, pdu_size);
ISNS_GLOBAL_UNLOCK();
return;
}
for (iportal = avl_first(&isns_all_portals);
iportal != NULL;
iportal = AVL_NEXT(&isns_all_portals, iportal)) {
if (idm_ss_compare(&iportal->portal_addr, &portal_ss,
B_TRUE, B_FALSE)) {
gethrestime(&iportal->portal_esi_timestamp);
}
}
ISNS_GLOBAL_UNLOCK();
rsp_size = isnst_create_pdu_header(ISNS_ESI_RSP, &rsp_pdu, 0);
if (rsp_size == 0) {
ISNST_LOG(CE_WARN, "isnst_handle_esi_req: Can't get rsp pdu");
kmem_free(pdu, pdu_size);
return;
}
rsp = (isns_resp_t *)((void *)(&rsp_pdu->payload[0]));
rsp_pdu->xid = pdu->xid;
rsp->status = htonl(ISNS_RSP_SUCCESSFUL);
req_pl_len = ntohs(pdu->payload_len);
bcopy(pdu->payload, rsp->data, req_pl_len);
rsp_pdu->payload_len = htons(req_pl_len + sizeof (uint32_t));
if (isnst_send_pdu(ks, rsp_pdu) != 0) {
ISNST_LOG(CE_WARN,
"isnst_handle_esi_req: Send response failed");
}
kmem_free(rsp_pdu, rsp_size);
kmem_free(pdu, pdu_size);
}
static int
isnst_tgt_avl_compare(const void *t1, const void *t2)
{
const isns_target_t *tgt1 = t1;
const isns_target_t *tgt2 = t2;
if (tgt1->target < tgt2->target) {
return (-1);
} else if (tgt1->target > tgt2->target) {
return (1);
}
return (0);
}
static void
isnst_set_server_status(iscsit_isns_svr_t *svr, boolean_t registered)
{
isns_target_t *itarget;
ASSERT(ISNS_GLOBAL_LOCK_HELD());
svr->svr_reset_needed = B_FALSE;
if (registered == B_TRUE) {
svr->svr_registered = B_TRUE;
svr->svr_last_msg = ddi_get_lbolt();
itarget = avl_first(&svr->svr_target_list);
while (itarget) {
isns_target_t *next_target;
next_target = AVL_NEXT(&svr->svr_target_list, itarget);
if (itarget->target_delete_needed) {
isnst_clear_from_target_list(itarget,
&svr->svr_target_list);
} else {
itarget->target_registered = B_TRUE;
itarget->target_update_needed = B_FALSE;
}
itarget = next_target;
}
ASSERT(avl_numnodes(&svr->svr_target_list) > 0);
} else {
svr->svr_registered = B_FALSE;
isnst_clear_target_list(svr);
}
}
static void
isnst_monitor_default_portal_list(void)
{
idm_addr_list_t *new_portal_list = NULL;
uint32_t new_portal_list_size = 0;
ASSERT(ISNS_GLOBAL_LOCK_HELD());
ASSERT(mutex_owned(&iscsit_isns_mutex));
if (default_portal_online) {
new_portal_list_size = idm_get_ipaddr(&new_portal_list);
}
if (isns_portals_changed ||
((new_portal_list_size != 0) &&
(isnst_find_default_portals(new_portal_list) !=
num_default_portals)) ||
((new_portal_list_size == 0) && (num_default_portals > 0))) {
isnst_clear_default_portals();
isnst_copy_portal_list(&isns_tpg_portals,
&isns_all_portals);
num_tpg_portals = avl_numnodes(&isns_all_portals);
if (new_portal_list_size != 0) {
num_default_portals =
isnst_add_default_portals(new_portal_list);
}
}
ASSERT(num_tpg_portals == avl_numnodes(&isns_tpg_portals));
if (new_portal_list != NULL) {
kmem_free(new_portal_list, new_portal_list_size);
}
}
static int
isnst_find_default_portals(idm_addr_list_t *alist)
{
idm_addr_t *dportal;
isns_portal_t *iportal;
struct sockaddr_storage sa;
int aidx;
int num_portals_found = 0;
for (aidx = 0; aidx < alist->al_out_cnt; aidx++) {
dportal = &alist->al_addrs[aidx];
dportal->a_port = ISCSI_LISTEN_PORT;
idm_addr_to_sa(dportal, &sa);
iportal = isnst_lookup_portal(&sa);
if (iportal == NULL) {
return (-1);
}
if (iportal->portal_default) {
num_portals_found++;
}
}
return (num_portals_found);
}
static void
isnst_clear_default_portals(void)
{
ASSERT(ISNS_GLOBAL_LOCK_HELD());
isnst_clear_portal_list(&isns_all_portals);
num_tpg_portals = 0;
num_default_portals = 0;
}
static int
isnst_add_default_portals(idm_addr_list_t *alist)
{
idm_addr_t *dportal;
isns_portal_t *iportal;
struct sockaddr_storage sa;
int aidx;
for (aidx = 0; aidx < alist->al_out_cnt; aidx++) {
dportal = &alist->al_addrs[aidx];
dportal->a_port = ISCSI_LISTEN_PORT;
idm_addr_to_sa(dportal, &sa);
iportal = isnst_add_to_portal_list(&sa, &isns_all_portals);
iportal->portal_default = B_TRUE;
}
return (alist->al_out_cnt);
}
static int
isnst_portal_avl_compare(const void *p1, const void *p2)
{
const isns_portal_t *portal1 = p1;
const isns_portal_t *portal2 = p2;
return (idm_ss_compare(&portal1->portal_addr, &portal2->portal_addr,
B_TRUE , B_TRUE ));
}
static void
isnst_clear_portal_list(avl_tree_t *portal_list)
{
isns_portal_t *iportal;
void *cookie = NULL;
while ((iportal = avl_destroy_nodes(portal_list, &cookie)) != NULL) {
kmem_free(iportal, sizeof (isns_portal_t));
}
}
static void
isnst_copy_portal_list(avl_tree_t *t1, avl_tree_t *t2)
{
isns_portal_t *iportal, *jportal;
iportal = (isns_portal_t *)avl_first(t1);
while (iportal) {
jportal = isnst_add_to_portal_list(&iportal->portal_addr, t2);
jportal->portal_iscsit = iportal->portal_iscsit;
iportal = AVL_NEXT(t1, iportal);
}
}
static isns_portal_t *
isnst_lookup_portal(struct sockaddr_storage *sa)
{
isns_portal_t *iportal, tmp_portal;
ASSERT(ISNS_GLOBAL_LOCK_HELD());
bcopy(sa, &tmp_portal.portal_addr, sizeof (*sa));
iportal = avl_find(&isns_all_portals, &tmp_portal, NULL);
return (iportal);
}
static isns_portal_t *
isnst_add_to_portal_list(struct sockaddr_storage *sa, avl_tree_t *portal_list)
{
isns_portal_t *iportal, tmp_portal;
avl_index_t where;
bcopy(sa, &tmp_portal.portal_addr, sizeof (*sa));
if ((iportal = (isns_portal_t *)avl_find(portal_list,
&tmp_portal, &where)) == NULL) {
iportal = kmem_zalloc(sizeof (isns_portal_t), KM_SLEEP);
bcopy(sa, &iportal->portal_addr, sizeof (*sa));
avl_insert(portal_list, (void *)iportal, where);
}
return (iportal);
}
static void
isnst_remove_from_portal_list(struct sockaddr_storage *sa,
avl_tree_t *portal_list)
{
isns_portal_t *iportal, tmp_portal;
bcopy(sa, &tmp_portal.portal_addr, sizeof (*sa));
if ((iportal = avl_find(portal_list, &tmp_portal, NULL))
!= NULL) {
avl_remove(portal_list, iportal);
kmem_free(iportal, sizeof (isns_portal_t));
}
}
void
iscsit_isns_portal_online(iscsit_portal_t *portal)
{
isns_portal_t *iportal;
mutex_enter(&iscsit_isns_mutex);
if (portal->portal_default) {
ASSERT(default_portal_online == B_FALSE);
default_portal_online = B_TRUE;
} else {
iportal = isnst_add_to_portal_list(
&portal->portal_addr, &isns_tpg_portals);
iportal->portal_iscsit = portal;
}
isns_portals_changed = B_TRUE;
mutex_exit(&iscsit_isns_mutex);
isnst_monitor_awaken();
}
void
iscsit_isns_portal_offline(iscsit_portal_t *portal)
{
mutex_enter(&iscsit_isns_mutex);
if (portal->portal_default) {
ASSERT(default_portal_online == B_TRUE);
default_portal_online = B_FALSE;
} else {
isnst_remove_from_portal_list(&portal->portal_addr,
&isns_tpg_portals);
}
isns_portals_changed = B_TRUE;
mutex_exit(&iscsit_isns_mutex);
isnst_monitor_awaken();
}