#include "iscsi.h"
#include "isns_protocol.h"
#include "isns_client.h"
#include "persistent.h"
#ifdef _KERNEL
#include <sys/sunddi.h>
#else
#include <stdlib.h>
#endif
#include <netinet/tcp.h>
#include <sys/types.h>
#define ISNS_MAX_IOVEC 5
#define MAX_XID (2^16)
#define MAX_RCV_RSP_COUNT 10
#define ISNS_RCV_TIMEOUT 5
#define ISNS_RCV_RETRY_MAX 2
#define IPV4_RSVD_BYTES 10
typedef struct isns_reg_arg {
iscsi_addr_t *isns_server_addr;
uint8_t *node_name;
size_t node_name_len;
uint8_t *node_alias;
size_t node_alias_len;
uint32_t node_type;
uint8_t *lhba_handle;
} isns_reg_arg_t;
typedef struct isns_async_thread_arg {
uint8_t *lhba_handle;
void *listening_so;
} isns_async_thread_arg_t;
static ddi_taskq_t *reg_query_taskq;
static kmutex_t reg_query_taskq_mutex;
static ddi_taskq_t *scn_taskq;
static kmutex_t scn_taskq_mutex;
static uint16_t xid = 0;
void (*scn_callback_p)(void *);
static boolean_t esi_scn_thr_to_shutdown = B_FALSE;
static iscsi_thread_t *esi_scn_thr_id = NULL;
static void *instance_listening_so = NULL;
static kmutex_t esi_scn_thr_mutex;
#define ISNS_OK 0
#define ISNS_BAD_SVR_ADDR 1
#define ISNS_INTERNAL_ERR 2
#define ISNS_CANNOT_FIND_LOCAL_ADDR 3
static int discover_isns_server(uint8_t *lhba_handle,
iscsi_addr_list_t **isns_server_addrs);
static int create_esi_scn_thr(uint8_t *lhba_handle,
iscsi_addr_t *isns_server_addr);
static void esi_scn_thr_cleanup(void);
static void register_isns_client(void *arg);
static isns_status_t do_isns_dev_attr_reg(iscsi_addr_t *isns_server_addr,
uint8_t *node_name, uint8_t *node_alias, uint32_t node_type);
static isns_status_t do_isns_dev_dereg(iscsi_addr_t *isns_server_addr,
uint8_t *node_name);
static isns_status_t do_isns_query(boolean_t is_query_all_nodes_b,
uint8_t *lhba_handle, uint8_t *target_node_name,
uint8_t *source_node_name, uint8_t *source_node_alias,
uint32_t source_node_type, isns_portal_group_list_t **pg_list);
static isns_status_t do_isns_dev_attr_query_all_nodes(
iscsi_addr_t *isns_server_addr, uint8_t *node_name,
uint8_t *node_alias, isns_portal_group_list_t **pg_list);
static isns_status_t do_isns_dev_attr_query_one_node(
iscsi_addr_t *isns_server_addr, uint8_t *target_node_name,
uint8_t *source_node_name, uint8_t *source_node_alias,
uint32_t source_node_type, isns_portal_group_list_t **pg_list);
static void isns_service_esi_scn(iscsi_thread_t *thread, void* arg);
static void (*scn_callback_lookup(uint8_t *lhba_handle))(void *);
static void *isns_open(iscsi_addr_t *isns_server_addr);
static ssize_t isns_send_pdu(void *socket, isns_pdu_t *pdu);
static size_t isns_rcv_pdu(void *so, isns_pdu_t **pdu, size_t *pdu_size);
static boolean_t find_listening_addr(iscsi_addr_t *local_addr,
void *listening_so);
static boolean_t find_local_portal(iscsi_addr_t *isns_server_addr,
iscsi_addr_t **local_addr, void **listening_so);
static size_t isns_create_pdu_header(uint16_t func_id,
uint16_t flags, isns_pdu_t **pdu);
static int isns_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 uint16_t create_xid(void);
static size_t isns_create_dev_attr_reg_pdu(
uint8_t *node_name, uint8_t *node_alias, uint32_t node_type,
uint16_t *xid, isns_pdu_t **out_pdu);
static size_t isns_create_dev_dereg_pdu(uint8_t *node_name,
uint16_t *xid_p, isns_pdu_t **out_pdu);
static size_t isns_create_dev_attr_qry_target_nodes_pdu(
uint8_t *node_name, uint8_t *node_alias, uint16_t *xid,
isns_pdu_t **out_pdu);
static size_t isns_create_dev_attr_qry_one_pg_pdu(
uint8_t *target_node_name, uint8_t *source_node_name,
uint16_t *xid, isns_pdu_t **out_pdu);
static size_t isns_create_esi_rsp_pdu(uint32_t rsp_status_code,
isns_pdu_t *pdu, uint16_t *xid, isns_pdu_t **out_pdu);
static size_t isns_create_scn_reg_pdu(uint8_t *node_name,
uint8_t *node_alias, uint16_t *xid, isns_pdu_t **out_pdu);
static size_t isns_create_scn_dereg_pdu(uint8_t *node_name,
uint16_t *xid_p, isns_pdu_t **out_pdu);
static size_t isns_create_scn_rsp_pdu(uint32_t rsp_status_code,
isns_pdu_t *pdu, uint16_t *xid, isns_pdu_t **out_pdu);
static uint32_t isns_process_dev_attr_reg_rsp(isns_pdu_t *resp_pdu_p);
static uint32_t isns_process_dev_attr_dereg_rsp(isns_pdu_t *resp_pdu_p);
static uint32_t isns_process_dev_attr_qry_target_nodes_pdu(
iscsi_addr_t *isns_server_addr, uint16_t payload_funcId,
isns_resp_t *resp_p, size_t resp_len,
isns_portal_group_list_t **pg_list);
static uint32_t isns_process_scn_reg_rsp(isns_pdu_t *resp_pdu_p);
static uint32_t isns_process_scn_dereg_rsp(isns_pdu_t *resp_pdu_p);
static uint32_t isns_process_esi(isns_pdu_t *esi_pdu_p);
static uint32_t isns_process_scn(isns_pdu_t *scn_pdu_p, uint8_t *lhba_handle);
void
isns_client_init()
{
mutex_init(®_query_taskq_mutex, NULL, MUTEX_DRIVER, NULL);
mutex_enter(®_query_taskq_mutex);
reg_query_taskq = ddi_taskq_create(NULL, "isns_reg_query_taskq",
1, TASKQ_DEFAULTPRI, 0);
mutex_exit(®_query_taskq_mutex);
mutex_init(&scn_taskq_mutex, NULL, MUTEX_DRIVER, NULL);
mutex_enter(&scn_taskq_mutex);
scn_taskq = ddi_taskq_create(NULL, "isns_scn_taskq",
1, TASKQ_DEFAULTPRI, 0);
mutex_exit(&scn_taskq_mutex);
mutex_init(&esi_scn_thr_mutex, NULL, MUTEX_DRIVER, NULL);
scn_callback_p = NULL;
esi_scn_thr_id = NULL;
instance_listening_so = NULL;
esi_scn_thr_to_shutdown = B_FALSE;
xid = 0;
}
void
isns_client_cleanup()
{
ddi_taskq_t *tmp_taskq_p;
mutex_enter(&scn_taskq_mutex);
tmp_taskq_p = scn_taskq;
scn_taskq = NULL;
mutex_exit(&scn_taskq_mutex);
ddi_taskq_destroy(tmp_taskq_p);
mutex_enter(®_query_taskq_mutex);
tmp_taskq_p = reg_query_taskq;
reg_query_taskq = NULL;
mutex_exit(®_query_taskq_mutex);
ddi_taskq_destroy(tmp_taskq_p);
mutex_destroy(®_query_taskq_mutex);
mutex_destroy(&scn_taskq_mutex);
esi_scn_thr_cleanup();
mutex_destroy(&esi_scn_thr_mutex);
}
isns_status_t
isns_reg(uint8_t *lhba_handle,
uint8_t *node_name,
size_t node_name_len,
uint8_t *node_alias,
size_t node_alias_len,
uint32_t node_type,
void (*scn_callback)(void *))
{
int i;
int list_space;
iscsi_addr_list_t *isns_server_addr_list;
isns_reg_arg_t *reg_args_p;
if (discover_isns_server(lhba_handle, &isns_server_addr_list) !=
ISNS_OK) {
return (isns_no_svr_found);
}
if (isns_server_addr_list->al_out_cnt == 0) {
list_space = sizeof (iscsi_addr_list_t);
kmem_free(isns_server_addr_list, list_space);
isns_server_addr_list = NULL;
return (isns_no_svr_found);
}
for (i = 0; i < isns_server_addr_list->al_out_cnt; i++) {
if (create_esi_scn_thr(lhba_handle,
&(isns_server_addr_list->al_addrs[i])) == ISNS_OK) {
break;
}
}
if (i == isns_server_addr_list->al_out_cnt) {
list_space = sizeof (iscsi_addr_list_t);
if (isns_server_addr_list->al_out_cnt > 0) {
list_space += (sizeof (iscsi_addr_t) *
(isns_server_addr_list->al_out_cnt - 1));
}
kmem_free(isns_server_addr_list, list_space);
isns_server_addr_list = NULL;
return (isns_internal_err);
}
for (i = 0; i < isns_server_addr_list->al_out_cnt; i++) {
reg_args_p = kmem_zalloc(sizeof (isns_reg_arg_t), KM_SLEEP);
reg_args_p->isns_server_addr =
kmem_zalloc(sizeof (iscsi_addr_t), KM_SLEEP);
bcopy(&isns_server_addr_list->al_addrs[i],
reg_args_p->isns_server_addr, sizeof (iscsi_addr_t));
reg_args_p->node_name = kmem_zalloc(node_name_len, KM_SLEEP);
bcopy(node_name, reg_args_p->node_name, node_name_len);
reg_args_p->node_name_len = node_name_len;
reg_args_p->node_alias = kmem_zalloc(node_alias_len, KM_SLEEP);
bcopy(node_alias, reg_args_p->node_alias, node_alias_len);
reg_args_p->node_alias_len = node_alias_len;
reg_args_p->node_type = node_type;
register_isns_client(reg_args_p);
}
list_space = sizeof (iscsi_addr_list_t);
if (isns_server_addr_list->al_out_cnt > 0) {
list_space += (sizeof (iscsi_addr_t) *
(isns_server_addr_list->al_out_cnt - 1));
}
kmem_free(isns_server_addr_list, list_space);
isns_server_addr_list = NULL;
scn_callback_p = scn_callback;
return (isns_ok);
}
isns_status_t
isns_reg_one_server(entry_t *isns_server,
uint8_t *lhba_handle,
uint8_t *node_name,
size_t node_name_len,
uint8_t *node_alias,
size_t node_alias_len,
uint32_t node_type,
void (*scn_callback)(void *))
{
int status;
iscsi_addr_t *ap;
isns_reg_arg_t *reg_args_p;
ap = (iscsi_addr_t *)kmem_zalloc(sizeof (iscsi_addr_t), KM_SLEEP);
ap->a_port = isns_server->e_port;
ap->a_addr.i_insize = isns_server->e_insize;
if (isns_server->e_insize == sizeof (struct in_addr)) {
ap->a_addr.i_addr.in4.s_addr = (isns_server->e_u.u_in4.s_addr);
} else if (isns_server->e_insize == sizeof (struct in6_addr)) {
bcopy(&(isns_server->e_u.u_in6.s6_addr),
ap->a_addr.i_addr.in6.s6_addr,
sizeof (struct in6_addr));
} else {
kmem_free(ap, sizeof (iscsi_addr_t));
return (isns_op_failed);
}
if ((status = create_esi_scn_thr(lhba_handle, ap))
!= ISNS_OK) {
DTRACE_PROBE1(isns_reg_one_server_create_esi_scn_thr,
int, status);
kmem_free(ap, sizeof (iscsi_addr_t));
return (isns_internal_err);
}
reg_args_p = kmem_zalloc(sizeof (isns_reg_arg_t), KM_SLEEP);
reg_args_p->isns_server_addr =
kmem_zalloc(sizeof (iscsi_addr_t), KM_SLEEP);
bcopy(ap, reg_args_p->isns_server_addr, sizeof (iscsi_addr_t));
reg_args_p->node_name = kmem_zalloc(node_name_len, KM_SLEEP);
bcopy(node_name, reg_args_p->node_name, node_name_len);
reg_args_p->node_name_len = node_name_len;
reg_args_p->node_alias = kmem_zalloc(node_alias_len, KM_SLEEP);
bcopy(node_alias, reg_args_p->node_alias, node_alias_len);
reg_args_p->node_alias_len = node_alias_len;
reg_args_p->node_type = node_type;
register_isns_client(reg_args_p);
scn_callback_p = scn_callback;
kmem_free(ap, sizeof (iscsi_addr_t));
return (isns_ok);
}
isns_status_t
isns_dereg(uint8_t *lhba_handle,
uint8_t *node_name)
{
int i;
int isns_svr_lst_sz;
int list_space;
iscsi_addr_list_t *isns_server_addr_list = NULL;
isns_status_t dereg_stat, combined_dereg_stat;
if (discover_isns_server(lhba_handle, &isns_server_addr_list) !=
ISNS_OK) {
return (isns_no_svr_found);
}
ASSERT(isns_server_addr_list != NULL);
if (isns_server_addr_list->al_out_cnt == 0) {
isns_svr_lst_sz = sizeof (iscsi_addr_list_t);
kmem_free(isns_server_addr_list, isns_svr_lst_sz);
isns_server_addr_list = NULL;
return (isns_no_svr_found);
}
combined_dereg_stat = isns_ok;
for (i = 0; i < isns_server_addr_list->al_out_cnt; i++) {
dereg_stat = do_isns_dev_dereg(
&isns_server_addr_list->al_addrs[i],
node_name);
if (dereg_stat == isns_ok) {
if (combined_dereg_stat != isns_ok) {
combined_dereg_stat = isns_op_partially_failed;
}
} else {
if (combined_dereg_stat == isns_ok) {
combined_dereg_stat = isns_op_partially_failed;
}
}
}
list_space = sizeof (iscsi_addr_list_t);
if (isns_server_addr_list->al_out_cnt > 0) {
list_space += (sizeof (iscsi_addr_t) *
(isns_server_addr_list->al_out_cnt - 1));
}
kmem_free(isns_server_addr_list, list_space);
isns_server_addr_list = NULL;
esi_scn_thr_cleanup();
return (combined_dereg_stat);
}
isns_status_t
isns_dereg_one_server(entry_t *isns_server,
uint8_t *node_name,
boolean_t is_last_isns_server_b)
{
iscsi_addr_t *ap;
isns_status_t dereg_stat;
ap = (iscsi_addr_t *)kmem_zalloc(sizeof (iscsi_addr_t), KM_SLEEP);
ap->a_port = isns_server->e_port;
ap->a_addr.i_insize = isns_server->e_insize;
if (isns_server->e_insize == sizeof (struct in_addr)) {
ap->a_addr.i_addr.in4.s_addr = (isns_server->e_u.u_in4.s_addr);
} else if (isns_server->e_insize == sizeof (struct in6_addr)) {
bcopy(&(isns_server->e_u.u_in6.s6_addr),
ap->a_addr.i_addr.in6.s6_addr,
sizeof (struct in6_addr));
} else {
kmem_free(ap, sizeof (iscsi_addr_t));
return (isns_op_failed);
}
dereg_stat = do_isns_dev_dereg(ap, node_name);
kmem_free(ap, sizeof (iscsi_addr_t));
if (is_last_isns_server_b == B_TRUE) {
esi_scn_thr_cleanup();
}
return (dereg_stat);
}
isns_status_t
isns_query(uint8_t *lhba_handle,
uint8_t *node_name,
uint8_t *node_alias,
uint32_t node_type,
isns_portal_group_list_t **pg_list)
{
return (do_isns_query(B_TRUE,
lhba_handle,
(uint8_t *)"",
node_name,
node_alias,
node_type,
pg_list));
}
isns_status_t
isns_query_one_server(iscsi_addr_t *isns_server_addr,
uint8_t *lhba_handle,
uint8_t *node_name,
uint8_t *node_alias,
uint32_t node_type,
isns_portal_group_list_t **pg_list)
{
return (do_isns_dev_attr_query_all_nodes(isns_server_addr,
node_name,
node_alias,
pg_list));
}
isns_status_t
isns_query_one_node(uint8_t *target_node_name,
uint8_t *lhba_handle,
uint8_t *source_node_name,
uint8_t *source_node_alias,
uint32_t source_node_type,
isns_portal_group_list_t **pg_list)
{
return (do_isns_query(B_FALSE,
lhba_handle,
target_node_name,
source_node_name,
source_node_alias,
source_node_type,
pg_list));
}
isns_status_t
isns_query_one_server_one_node(iscsi_addr_t *isns_server_addr,
uint8_t *target_node_name,
uint8_t *lhba_handle,
uint8_t *source_node_name,
uint8_t *source_node_alias,
uint32_t source_node_type,
isns_portal_group_list_t **pg_list) {
*pg_list = NULL;
return (isns_op_failed);
}
static
int
discover_isns_server(uint8_t *lhba_handle,
iscsi_addr_list_t **isns_server_addrs)
{
entry_t e;
int i;
int isns_server_count = 1;
int list_space;
void *void_p;
isns_server_count = 0;
void_p = NULL;
persistent_isns_addr_lock();
while (persistent_isns_addr_next(&void_p, &e) == B_TRUE) {
isns_server_count++;
}
persistent_isns_addr_unlock();
list_space = sizeof (iscsi_addr_list_t);
if (isns_server_count > 0) {
list_space += (sizeof (iscsi_addr_t) * (isns_server_count - 1));
}
*isns_server_addrs = (iscsi_addr_list_t *)kmem_zalloc(list_space,
KM_SLEEP);
(*isns_server_addrs)->al_out_cnt = isns_server_count;
persistent_isns_addr_lock();
i = 0;
void_p = NULL;
while (persistent_isns_addr_next(&void_p, &e) == B_TRUE) {
iscsi_addr_t *ap;
ap = &((*isns_server_addrs)->al_addrs[i]);
ap->a_port = e.e_port;
ap->a_addr.i_insize = e.e_insize;
if (e.e_insize == sizeof (struct in_addr)) {
ap->a_addr.i_addr.in4.s_addr = (e.e_u.u_in4.s_addr);
} else if (e.e_insize == sizeof (struct in6_addr)) {
bcopy(&e.e_u.u_in6.s6_addr,
ap->a_addr.i_addr.in6.s6_addr,
sizeof (struct in6_addr));
} else {
kmem_free(*isns_server_addrs, list_space);
*isns_server_addrs = NULL;
return (ISNS_BAD_SVR_ADDR);
}
i++;
}
persistent_isns_addr_unlock();
return (ISNS_OK);
}
static
int
create_esi_scn_thr(uint8_t *lhba_handle, iscsi_addr_t *isns_server_address)
{
void *listening_so = NULL;
boolean_t found = B_FALSE;
ASSERT(lhba_handle != NULL);
ASSERT(isns_server_address != NULL);
mutex_enter(&esi_scn_thr_mutex);
found = find_local_portal(isns_server_address,
NULL, &listening_so);
if (found == B_FALSE) {
if (listening_so != NULL) {
iscsi_net->close(listening_so);
}
mutex_exit(&esi_scn_thr_mutex);
return (ISNS_CANNOT_FIND_LOCAL_ADDR);
}
if (esi_scn_thr_id == NULL) {
char thr_name[ISCSI_TH_MAX_NAME_LEN];
int rval;
isns_async_thread_arg_t *larg;
if (snprintf(thr_name, sizeof (thr_name) - 1,
"isns_client_esi_%x%x%x%x",
lhba_handle[0],
lhba_handle[1],
lhba_handle[2],
lhba_handle[3]) >=
sizeof (thr_name)) {
esi_scn_thr_id = NULL;
if (listening_so != NULL) {
iscsi_net->close(listening_so);
listening_so = NULL;
}
mutex_exit(&esi_scn_thr_mutex);
return (ISNS_INTERNAL_ERR);
}
larg = kmem_zalloc(sizeof (isns_async_thread_arg_t), KM_SLEEP);
larg->lhba_handle = lhba_handle;
larg->listening_so = listening_so;
instance_listening_so = listening_so;
esi_scn_thr_to_shutdown = B_FALSE;
esi_scn_thr_id = iscsi_thread_create(NULL,
thr_name, isns_service_esi_scn, (void *)larg);
if (esi_scn_thr_id == NULL) {
if (listening_so != NULL) {
iscsi_net->close(listening_so);
listening_so = NULL;
instance_listening_so = NULL;
}
mutex_exit(&esi_scn_thr_mutex);
return (ISNS_INTERNAL_ERR);
}
rval = iscsi_thread_start(esi_scn_thr_id);
if (rval == B_FALSE) {
iscsi_thread_destroy(esi_scn_thr_id);
esi_scn_thr_id = NULL;
if (listening_so != NULL) {
iscsi_net->close(listening_so);
listening_so = NULL;
instance_listening_so = NULL;
}
mutex_exit(&esi_scn_thr_mutex);
return (ISNS_INTERNAL_ERR);
}
(void) iscsi_thread_send_wakeup(esi_scn_thr_id);
}
mutex_exit(&esi_scn_thr_mutex);
return (ISNS_OK);
}
static
void
register_isns_client(void *arg)
{
isns_reg_arg_t *reg_args;
isns_status_t status;
reg_args = (isns_reg_arg_t *)arg;
status = do_isns_dev_dereg(reg_args->isns_server_addr,
reg_args->node_name);
if (status == isns_open_conn_err) {
kmem_free(reg_args->isns_server_addr, sizeof (iscsi_addr_t));
reg_args->isns_server_addr = NULL;
kmem_free(reg_args->node_name, reg_args->node_name_len);
reg_args->node_name = NULL;
kmem_free(reg_args->node_alias, reg_args->node_alias_len);
reg_args->node_alias = NULL;
kmem_free(reg_args, sizeof (isns_reg_arg_t));
return;
}
DTRACE_PROBE1(register_isns_client_dereg, isns_status_t, status);
status = do_isns_dev_attr_reg(reg_args->isns_server_addr,
reg_args->node_name, reg_args->node_alias, reg_args->node_type);
DTRACE_PROBE1(register_isns_client_reg, isns_status_t, status);
kmem_free(reg_args->isns_server_addr, sizeof (iscsi_addr_t));
reg_args->isns_server_addr = NULL;
kmem_free(reg_args->node_name, reg_args->node_name_len);
reg_args->node_name = NULL;
kmem_free(reg_args->node_alias, reg_args->node_alias_len);
reg_args->node_alias = NULL;
kmem_free(reg_args, sizeof (isns_reg_arg_t));
}
static
isns_status_t
do_isns_dev_attr_reg(iscsi_addr_t *isns_server_addr,
uint8_t *node_name, uint8_t *node_alias, uint32_t node_type)
{
int rcv_rsp_cnt = 0;
int rsp_status;
isns_pdu_t *in_pdu, *out_pdu;
isns_status_t rval;
size_t bytes_received, in_pdu_size = 0, out_pdu_size = 0;
uint16_t xid;
void *so = NULL;
out_pdu_size = isns_create_dev_attr_reg_pdu(
node_name,
node_alias,
node_type,
&xid, &out_pdu);
if (out_pdu_size == 0) {
return (isns_create_msg_err);
}
ASSERT(out_pdu != NULL);
ASSERT(out_pdu_size > 0);
so = isns_open(isns_server_addr);
if (so == NULL) {
kmem_free(out_pdu, out_pdu_size);
out_pdu = NULL;
return (isns_open_conn_err);
}
if (isns_send_pdu(so, out_pdu) != 0) {
iscsi_net->close(so);
kmem_free(out_pdu, out_pdu_size);
out_pdu = NULL;
return (isns_send_msg_err);
}
kmem_free(out_pdu, out_pdu_size);
out_pdu = NULL;
rcv_rsp_cnt = 0;
rval = isns_ok;
for (;;) {
bytes_received = isns_rcv_pdu(so, &in_pdu, &in_pdu_size);
ASSERT(bytes_received >= (size_t)0);
if (bytes_received == 0) {
ASSERT(in_pdu == NULL);
ASSERT(in_pdu_size == 0);
rval = isns_rcv_msg_err;
break;
}
ASSERT(in_pdu != NULL);
ASSERT(in_pdu_size > 0);
if (ntohs(in_pdu->xid) != xid) {
rcv_rsp_cnt++;
if (rcv_rsp_cnt < MAX_RCV_RSP_COUNT) {
continue;
} else {
kmem_free(in_pdu, in_pdu_size);
in_pdu = NULL;
rval = isns_no_rsp_rcvd;
break;
}
}
rsp_status = isns_process_dev_attr_reg_rsp(in_pdu);
if (rsp_status != ISNS_RSP_SUCCESSFUL) {
if (rsp_status == ISNS_RSP_SRC_UNAUTHORIZED) {
rval = isns_op_partially_failed;
} else {
rval = isns_op_failed;
}
}
kmem_free(in_pdu, in_pdu_size);
in_pdu = NULL;
break;
}
if (rval != isns_ok) {
iscsi_net->close(so);
return (rval);
}
out_pdu_size = isns_create_scn_reg_pdu(
node_name, node_alias,
&xid, &out_pdu);
if (out_pdu_size == 0) {
iscsi_net->close(so);
return (isns_create_msg_err);
}
ASSERT(out_pdu != NULL);
ASSERT(out_pdu_size > 0);
if (isns_send_pdu(so, out_pdu) != 0) {
iscsi_net->close(so);
kmem_free(out_pdu, out_pdu_size);
out_pdu = NULL;
return (isns_send_msg_err);
}
kmem_free(out_pdu, out_pdu_size);
out_pdu = NULL;
rcv_rsp_cnt = 0;
for (;;) {
bytes_received = isns_rcv_pdu(so, &in_pdu, &in_pdu_size);
ASSERT(bytes_received >= (size_t)0);
if (bytes_received == 0) {
ASSERT(in_pdu == NULL);
ASSERT(in_pdu_size == 0);
rval = isns_rcv_msg_err;
break;
}
ASSERT(in_pdu != NULL);
ASSERT(in_pdu_size > 0);
if (ntohs(in_pdu->xid) != xid) {
rcv_rsp_cnt++;
if (rcv_rsp_cnt < MAX_RCV_RSP_COUNT) {
continue;
} else {
kmem_free(in_pdu, in_pdu_size);
in_pdu = NULL;
rval = isns_no_rsp_rcvd;
break;
}
}
rsp_status = isns_process_scn_reg_rsp(in_pdu);
if (rsp_status != ISNS_RSP_SUCCESSFUL) {
rval = isns_op_failed;
}
kmem_free(in_pdu, in_pdu_size);
in_pdu = NULL;
break;
}
iscsi_net->close(so);
return (rval);
}
static
isns_status_t
do_isns_dev_dereg(iscsi_addr_t *isns_server_addr,
uint8_t *node_name)
{
int rcv_rsp_cnt = 0;
int rsp_status;
isns_pdu_t *in_pdu, *out_pdu;
isns_status_t rval;
size_t bytes_received, in_pdu_size = 0, out_pdu_size = 0;
uint16_t xid;
void *so = NULL;
out_pdu_size = isns_create_dev_dereg_pdu(
node_name,
&xid, &out_pdu);
if (out_pdu_size == 0) {
return (isns_create_msg_err);
}
ASSERT(out_pdu != NULL);
ASSERT(out_pdu_size > 0);
so = isns_open(isns_server_addr);
if (so == NULL) {
kmem_free(out_pdu, out_pdu_size);
out_pdu = NULL;
return (isns_open_conn_err);
}
if (isns_send_pdu(so, out_pdu) != 0) {
iscsi_net->close(so);
kmem_free(out_pdu, out_pdu_size);
out_pdu = NULL;
return (isns_send_msg_err);
}
kmem_free(out_pdu, out_pdu_size);
out_pdu = NULL;
rcv_rsp_cnt = 0;
rval = isns_ok;
for (;;) {
bytes_received = isns_rcv_pdu(so, &in_pdu, &in_pdu_size);
ASSERT(bytes_received >= (size_t)0);
if (bytes_received == 0) {
ASSERT(in_pdu == NULL);
ASSERT(in_pdu_size == 0);
rval = isns_rcv_msg_err;
break;
}
ASSERT(in_pdu != NULL);
ASSERT(in_pdu_size > 0);
if (ntohs(in_pdu->xid) != xid) {
rcv_rsp_cnt++;
if (rcv_rsp_cnt < MAX_RCV_RSP_COUNT) {
continue;
} else {
kmem_free(in_pdu, in_pdu_size);
in_pdu = NULL;
rval = isns_no_rsp_rcvd;
break;
}
}
rsp_status = isns_process_dev_attr_dereg_rsp(in_pdu);
if (rsp_status != ISNS_RSP_SUCCESSFUL) {
rval = isns_op_failed;
}
kmem_free(in_pdu, in_pdu_size);
in_pdu = NULL;
break;
}
if (rval != isns_ok) {
iscsi_net->close(so);
return (rval);
}
out_pdu_size = isns_create_scn_dereg_pdu(
node_name,
&xid, &out_pdu);
if (out_pdu_size == 0) {
iscsi_net->close(so);
return (isns_create_msg_err);
}
ASSERT(out_pdu != NULL);
ASSERT(out_pdu_size > 0);
if (isns_send_pdu(so, out_pdu) != 0) {
iscsi_net->close(so);
kmem_free(out_pdu, out_pdu_size);
out_pdu = NULL;
return (isns_send_msg_err);
}
kmem_free(out_pdu, out_pdu_size);
out_pdu = NULL;
rcv_rsp_cnt = 0;
for (;;) {
bytes_received = isns_rcv_pdu(so, &in_pdu, &in_pdu_size);
ASSERT(bytes_received >= (size_t)0);
if (bytes_received == 0) {
ASSERT(in_pdu == NULL);
ASSERT(in_pdu_size == 0);
rval = isns_rcv_msg_err;
break;
}
ASSERT(in_pdu != NULL);
ASSERT(in_pdu_size > 0);
if (ntohs(in_pdu->xid) != xid) {
rcv_rsp_cnt++;
if (rcv_rsp_cnt < MAX_RCV_RSP_COUNT) {
continue;
} else {
kmem_free(in_pdu, in_pdu_size);
in_pdu = NULL;
rval = isns_no_rsp_rcvd;
break;
}
}
rsp_status = isns_process_scn_dereg_rsp(in_pdu);
if (rsp_status != ISNS_RSP_SUCCESSFUL) {
rval = isns_op_failed;
}
kmem_free(in_pdu, in_pdu_size);
in_pdu = NULL;
break;
}
iscsi_net->close(so);
return (rval);
}
static
isns_status_t
do_isns_query(boolean_t is_query_all_nodes_b,
uint8_t *lhba_handle,
uint8_t *target_node_name,
uint8_t *source_node_name,
uint8_t *source_node_alias,
uint32_t source_node_type,
isns_portal_group_list_t **pg_list)
{
int i, j, k;
int combined_num_of_pgs, combined_pg_lst_sz,
isns_svr_lst_sz,
tmp_pg_list_sz,
tmp_pg_lists_sz;
iscsi_addr_list_t *isns_server_addr_list = NULL;
isns_portal_group_t *pg;
isns_portal_group_list_t *combined_pg_list,
*tmp_pg_list, **tmp_pg_lists;
isns_status_t qry_stat, combined_qry_stat;
if (discover_isns_server(lhba_handle, &isns_server_addr_list) !=
ISNS_OK) {
*pg_list = NULL;
return (isns_no_svr_found);
}
if (isns_server_addr_list->al_out_cnt == 0) {
isns_svr_lst_sz = sizeof (iscsi_addr_list_t);
kmem_free(isns_server_addr_list, isns_svr_lst_sz);
isns_server_addr_list = NULL;
*pg_list = NULL;
return (isns_no_svr_found);
}
tmp_pg_lists_sz = isns_server_addr_list->al_out_cnt *
sizeof (isns_portal_group_list_t *);
tmp_pg_lists = (isns_portal_group_list_t **)kmem_zalloc(
tmp_pg_lists_sz, KM_SLEEP);
combined_num_of_pgs = 0;
combined_qry_stat = isns_ok;
for (i = 0; i < isns_server_addr_list->al_out_cnt; i++) {
if (is_query_all_nodes_b) {
qry_stat = do_isns_dev_attr_query_all_nodes(
&isns_server_addr_list->al_addrs[i],
source_node_name,
source_node_alias,
&tmp_pg_list);
} else {
qry_stat = do_isns_dev_attr_query_one_node(
&isns_server_addr_list->al_addrs[i],
target_node_name,
source_node_name,
source_node_alias,
source_node_type,
&tmp_pg_list);
}
tmp_pg_lists[i] = tmp_pg_list;
if (tmp_pg_list != NULL) {
combined_num_of_pgs += tmp_pg_list->pg_out_cnt;
}
if (qry_stat == isns_ok) {
if (combined_qry_stat != isns_ok) {
combined_qry_stat = isns_op_partially_failed;
}
} else {
if (combined_qry_stat != isns_op_partially_failed) {
if (combined_qry_stat == isns_ok && i > 0) {
combined_qry_stat =
isns_op_partially_failed;
} else {
combined_qry_stat = qry_stat;
}
}
}
if (is_query_all_nodes_b == B_FALSE) {
if (qry_stat == isns_ok) {
break;
}
}
}
combined_pg_lst_sz = sizeof (isns_portal_group_list_t);
if (combined_num_of_pgs > 0) {
combined_pg_lst_sz += (combined_num_of_pgs - 1) *
sizeof (isns_portal_group_t);
}
combined_pg_list = (isns_portal_group_list_t *)kmem_zalloc(
combined_pg_lst_sz, KM_SLEEP);
combined_pg_list->pg_out_cnt = combined_num_of_pgs;
k = 0;
for (i = 0; i < isns_server_addr_list->al_out_cnt; i++) {
if (tmp_pg_lists[i] == NULL) {
continue;
}
for (j = 0; j < tmp_pg_lists[i]->pg_out_cnt; j++) {
pg = &(combined_pg_list->pg_list[k]);
bcopy(&(tmp_pg_lists[i]->pg_list[j]),
pg, sizeof (isns_portal_group_t));
k++;
}
tmp_pg_list_sz = sizeof (isns_portal_group_list_t);
if (tmp_pg_lists[i]->pg_out_cnt > 0) {
tmp_pg_list_sz += (tmp_pg_lists[i]->pg_out_cnt - 1) *
sizeof (isns_portal_group_t);
}
kmem_free(tmp_pg_lists[i], tmp_pg_list_sz);
tmp_pg_lists[i] = NULL;
}
kmem_free(tmp_pg_lists, tmp_pg_lists_sz);
tmp_pg_lists = NULL;
isns_svr_lst_sz = sizeof (iscsi_addr_list_t);
if (isns_server_addr_list->al_out_cnt > 0) {
isns_svr_lst_sz += (sizeof (iscsi_addr_t) *
(isns_server_addr_list->al_out_cnt - 1));
}
kmem_free(isns_server_addr_list, isns_svr_lst_sz);
isns_server_addr_list = NULL;
DTRACE_PROBE1(list, isns_portal_group_list_t *, combined_pg_list);
*pg_list = combined_pg_list;
return (combined_qry_stat);
}
static
isns_status_t
do_isns_dev_attr_query_all_nodes(iscsi_addr_t *isns_server_addr,
uint8_t *node_name,
uint8_t *node_alias,
isns_portal_group_list_t **pg_list)
{
int bytes_received;
int rcv_rsp_cnt = 0;
int rsp_status;
uint16_t xid, seq_id = 0, func_id;
isns_pdu_t *in_pdu, *out_pdu;
isns_pdu_mult_payload_t *combined_pdu = NULL, *old_combined_pdu = NULL;
isns_status_t qry_stat;
size_t out_pdu_size = 0, in_pdu_size = 0;
size_t old_combined_pdu_size = 0, combined_pdu_size = 0;
void *so = NULL;
uint8_t *payload_ptr;
*pg_list = NULL;
so = isns_open(isns_server_addr);
if (so == NULL) {
return (isns_open_conn_err);
}
out_pdu_size = isns_create_dev_attr_qry_target_nodes_pdu(
node_name, node_alias, &xid, &out_pdu);
if (out_pdu_size == 0) {
iscsi_net->close(so);
return (isns_create_msg_err);
}
ASSERT(out_pdu != NULL);
ASSERT(out_pdu_size > 0);
if (isns_send_pdu(so, out_pdu) != 0) {
iscsi_net->close(so);
kmem_free(out_pdu, out_pdu_size);
out_pdu = NULL;
return (isns_send_msg_err);
}
kmem_free(out_pdu, out_pdu_size);
out_pdu = NULL;
rcv_rsp_cnt = 0;
qry_stat = isns_ok;
for (;;) {
uint16_t flags;
bytes_received = isns_rcv_pdu(so, &in_pdu, &in_pdu_size);
ASSERT(bytes_received >= 0);
if (bytes_received == 0) {
ASSERT(in_pdu == NULL);
ASSERT(in_pdu_size == 0);
qry_stat = isns_rcv_msg_err;
break;
}
ASSERT(in_pdu != NULL);
ASSERT(in_pdu_size > 0);
if (ntohs(in_pdu->xid) != xid) {
rcv_rsp_cnt++;
if (rcv_rsp_cnt < MAX_RCV_RSP_COUNT) {
kmem_free(in_pdu, in_pdu_size);
in_pdu = NULL;
continue;
} else {
kmem_free(in_pdu, in_pdu_size);
in_pdu = NULL;
qry_stat = isns_no_rsp_rcvd;
break;
}
}
flags = in_pdu->flags;
if (((flags & ISNS_FLAG_FIRST_PDU) == ISNS_FLAG_FIRST_PDU) &&
((flags & ISNS_FLAG_LAST_PDU) == ISNS_FLAG_LAST_PDU)) {
rsp_status =
isns_process_dev_attr_qry_target_nodes_pdu(
isns_server_addr,
in_pdu->func_id,
(isns_resp_t *)in_pdu->payload,
(size_t)in_pdu->payload_len,
pg_list);
kmem_free(in_pdu, in_pdu_size);
in_pdu = NULL;
break;
}
if ((flags & ISNS_FLAG_FIRST_PDU) == ISNS_FLAG_FIRST_PDU) {
if (in_pdu->seq != 0) {
cmn_err(CE_NOTE, "isns query response invalid: "
"first pdu is not sequence ID 0");
kmem_free(in_pdu, in_pdu_size);
in_pdu = NULL;
return (isns_op_failed);
}
seq_id = 0;
combined_pdu_size = ISNSP_MULT_PAYLOAD_HEADER_SIZE +
in_pdu->payload_len;
combined_pdu = (isns_pdu_mult_payload_t *)kmem_zalloc(
combined_pdu_size, KM_SLEEP);
func_id = in_pdu->func_id;
combined_pdu->payload_len = in_pdu->payload_len;
bcopy(in_pdu->payload, combined_pdu->payload,
in_pdu->payload_len);
kmem_free(in_pdu, in_pdu_size);
in_pdu = NULL;
} else {
seq_id++;
if (in_pdu->seq != seq_id) {
cmn_err(CE_NOTE, "isns query response invalid: "
"Missing sequence ID %d from isns query "
"response.", seq_id);
kmem_free(in_pdu, in_pdu_size);
in_pdu = NULL;
if (combined_pdu != NULL) {
kmem_free(combined_pdu,
combined_pdu_size);
combined_pdu = NULL;
}
return (isns_op_failed);
}
if (combined_pdu_size == 0) {
cmn_err(CE_NOTE, "isns query response invalid: "
"Did not receive first pdu.\n");
kmem_free(in_pdu, in_pdu_size);
in_pdu = NULL;
return (isns_op_failed);
}
old_combined_pdu_size = combined_pdu_size;
old_combined_pdu = combined_pdu;
combined_pdu_size += in_pdu->payload_len;
combined_pdu = (isns_pdu_mult_payload_t *)kmem_zalloc(
combined_pdu_size, KM_SLEEP);
bcopy(old_combined_pdu, combined_pdu,
old_combined_pdu_size);
payload_ptr = combined_pdu->payload +
combined_pdu->payload_len;
combined_pdu->payload_len += in_pdu->payload_len;
bcopy(in_pdu->payload, payload_ptr,
in_pdu->payload_len);
kmem_free(in_pdu, in_pdu_size);
kmem_free(old_combined_pdu, old_combined_pdu_size);
in_pdu = NULL;
old_combined_pdu = NULL;
}
if ((flags & ISNS_FLAG_LAST_PDU) == ISNS_FLAG_LAST_PDU) {
rsp_status =
isns_process_dev_attr_qry_target_nodes_pdu(
isns_server_addr,
func_id,
(isns_resp_t *)combined_pdu->payload,
combined_pdu->payload_len,
pg_list);
kmem_free(combined_pdu, combined_pdu_size);
combined_pdu = NULL;
break;
}
}
if (rsp_status != ISNS_RSP_SUCCESSFUL) {
qry_stat = isns_op_failed;
}
iscsi_net->close(so);
return (qry_stat);
}
static
isns_status_t
do_isns_dev_attr_query_one_node(iscsi_addr_t *isns_server_addr,
uint8_t *target_node_name,
uint8_t *source_node_name,
uint8_t *source_node_alias,
uint32_t source_node_type,
isns_portal_group_list_t **pg_list)
{
int bytes_received;
int rcv_rsp_cnt;
int rsp_status;
isns_pdu_t *in_pdu, *out_pdu;
isns_status_t rval;
size_t out_pdu_size = 0, in_pdu_size = 0;
uint16_t xid;
void *so = NULL;
out_pdu_size = isns_create_dev_attr_qry_one_pg_pdu(
target_node_name, source_node_name, &xid, &out_pdu);
if (out_pdu_size == 0) {
return (isns_create_msg_err);
}
ASSERT(out_pdu != NULL);
ASSERT(out_pdu_size > 0);
so = isns_open(isns_server_addr);
if (so == NULL) {
kmem_free(out_pdu, out_pdu_size);
out_pdu = NULL;
return (isns_open_conn_err);
}
if (isns_send_pdu(so, out_pdu) != 0) {
iscsi_net->close(so);
kmem_free(out_pdu, out_pdu_size);
out_pdu = NULL;
return (isns_send_msg_err);
}
kmem_free(out_pdu, out_pdu_size);
out_pdu = NULL;
rcv_rsp_cnt = 0;
rval = isns_ok;
for (;;) {
bytes_received = isns_rcv_pdu(so, &in_pdu, &in_pdu_size);
ASSERT(bytes_received >= 0);
if (bytes_received == 0) {
ASSERT(in_pdu == NULL);
ASSERT(in_pdu_size == 0);
rval = isns_rcv_msg_err;
break;
}
ASSERT(in_pdu != NULL);
ASSERT(in_pdu_size > 0);
if (ntohs(in_pdu->xid) != xid) {
rcv_rsp_cnt++;
if (rcv_rsp_cnt < MAX_RCV_RSP_COUNT) {
continue;
} else {
kmem_free(in_pdu, in_pdu_size);
in_pdu = NULL;
rval = isns_no_rsp_rcvd;
break;
}
}
rsp_status = isns_process_dev_attr_qry_target_nodes_pdu(
isns_server_addr, in_pdu->func_id,
(isns_resp_t *)in_pdu->payload, (size_t)in_pdu->payload_len,
pg_list);
if (rsp_status != ISNS_RSP_SUCCESSFUL) {
rval = isns_op_failed;
}
kmem_free(in_pdu, in_pdu_size);
in_pdu = NULL;
break;
}
iscsi_net->close(so);
return (rval);
}
static
void
*isns_open(iscsi_addr_t *isns_server_addr)
{
int rval = 0;
union {
struct sockaddr sin;
struct sockaddr_in s_in4;
struct sockaddr_in6 s_in6;
} sa_rsvr = { 0 };
void *so;
struct sockaddr_in6 t_addr;
socklen_t t_addrlen;
bzero(&t_addr, sizeof (struct sockaddr_in6));
t_addrlen = sizeof (struct sockaddr_in6);
if (isns_server_addr->a_addr.i_insize == sizeof (struct in_addr)) {
sa_rsvr.s_in4.sin_family = AF_INET;
sa_rsvr.s_in4.sin_port = htons(isns_server_addr->a_port);
sa_rsvr.s_in4.sin_addr.s_addr =
isns_server_addr->a_addr.i_addr.in4.s_addr;
so = iscsi_net->socket(AF_INET, SOCK_STREAM, 0);
} else {
sa_rsvr.s_in6.sin6_family = AF_INET6;
bcopy(&(isns_server_addr->a_addr.i_addr.in6),
sa_rsvr.s_in6.sin6_addr.s6_addr,
sizeof (struct in6_addr));
sa_rsvr.s_in6.sin6_port = htons(isns_server_addr->a_port);
so = iscsi_net->socket(AF_INET6, SOCK_STREAM, 0);
}
if (so == NULL) {
return (NULL);
}
rval = iscsi_net->connect(so, &sa_rsvr.sin,
(isns_server_addr->a_addr.i_insize == sizeof (struct in_addr)) ?
sizeof (struct sockaddr_in) :
sizeof (struct sockaddr_in6), 0, 0);
if (rval != 0) {
iscsi_net->shutdown(so, 2);
iscsi_net->close(so);
return (NULL);
}
(void) iscsi_net->getsockname(so, (struct sockaddr *)&t_addr,
&t_addrlen);
return (so);
}
static ssize_t
isns_send_pdu(void *socket, isns_pdu_t *pdu)
{
int iovlen = 0;
iovec_t iovec[ISNS_MAX_IOVEC];
struct msghdr msg;
size_t send_len;
size_t total_len = 0;
ASSERT(iovlen < ISNS_MAX_IOVEC);
iovec[iovlen].iov_base = (void *)pdu;
iovec[iovlen].iov_len = (ISNSP_HEADER_SIZE);
total_len += (ISNSP_HEADER_SIZE);
iovlen++;
ASSERT(iovlen < ISNS_MAX_IOVEC);
iovec[iovlen].iov_base = (void *)pdu->payload;
iovec[iovlen].iov_len = ntohs(pdu->payload_len);
total_len += ntohs(pdu->payload_len);
iovlen++;
bzero(&msg, sizeof (msg));
msg.msg_iov = &iovec[0];
msg.msg_flags = MSG_WAITALL;
msg.msg_iovlen = iovlen;
send_len = iscsi_net->sendmsg(socket, &msg);
return (send_len == total_len ? 0 : -1);
}
static
size_t
isns_rcv_pdu(void *socket, isns_pdu_t **pdu, size_t *pdu_size)
{
int poll_cnt;
iovec_t iovec[ISNS_MAX_IOVEC];
isns_pdu_t *tmp_pdu_hdr;
size_t bytes_received, total_bytes_received = 0, payload_len = 0;
struct msghdr msg;
uint8_t *tmp_pdu_data;
tmp_pdu_hdr = (isns_pdu_t *)kmem_zalloc(ISNSP_HEADER_SIZE, KM_SLEEP);
(void) memset((char *)&iovec[0], 0, sizeof (iovec_t));
iovec[0].iov_base = (void *)tmp_pdu_hdr;
iovec[0].iov_len = ISNSP_HEADER_SIZE;
bzero(&msg, sizeof (msg));
msg.msg_iov = &iovec[0];
msg.msg_flags = MSG_WAITALL;
msg.msg_iovlen = 1;
poll_cnt = 0;
do {
bytes_received = iscsi_net->recvmsg(socket, &msg,
ISNS_RCV_TIMEOUT);
if (bytes_received == 0) {
poll_cnt++;
continue;
} else {
break;
}
} while (poll_cnt < ISNS_RCV_RETRY_MAX);
DTRACE_PROBE2(isns_rcv_pdu_hdr_summary,
int, poll_cnt, int, bytes_received);
if (poll_cnt >= ISNS_RCV_RETRY_MAX) {
kmem_free(tmp_pdu_hdr, ISNSP_HEADER_SIZE);
*pdu = NULL;
*pdu_size = 0;
return (0);
}
if (bytes_received == 0 || bytes_received != ISNSP_HEADER_SIZE) {
kmem_free(tmp_pdu_hdr, ISNSP_HEADER_SIZE);
*pdu = NULL;
*pdu_size = 0;
return (0);
}
total_bytes_received += bytes_received;
payload_len = ntohs(tmp_pdu_hdr->payload_len);
DTRACE_PROBE1(isns_rcv_pdu_probe1, int, payload_len);
if (payload_len > ISNSP_MAX_PAYLOAD_SIZE) {
kmem_free(tmp_pdu_hdr, ISNSP_HEADER_SIZE);
*pdu = NULL;
*pdu_size = 0;
return (0);
}
tmp_pdu_data = kmem_zalloc(payload_len, KM_SLEEP);
(void) memset((char *)&iovec[0], 0, sizeof (iovec_t));
iovec[0].iov_base = (void *)tmp_pdu_data;
iovec[0].iov_len = payload_len;
bzero(&msg, sizeof (msg));
msg.msg_iov = &iovec[0];
msg.msg_flags = MSG_WAITALL;
msg.msg_iovlen = 1;
poll_cnt = 0;
do {
bytes_received = iscsi_net->recvmsg(socket, &msg,
ISNS_RCV_TIMEOUT);
if (bytes_received == 0) {
poll_cnt++;
continue;
} else {
break;
}
} while (poll_cnt < ISNS_RCV_RETRY_MAX);
DTRACE_PROBE2(isns_rcv_pdu_data_summary,
int, poll_cnt, int, bytes_received);
if (poll_cnt >= ISNS_RCV_RETRY_MAX) {
kmem_free(tmp_pdu_data, payload_len);
kmem_free(tmp_pdu_hdr, ISNSP_HEADER_SIZE);
*pdu = NULL;
*pdu_size = 0;
return (0);
}
if (bytes_received == 0 || bytes_received != payload_len) {
kmem_free(tmp_pdu_data, payload_len);
kmem_free(tmp_pdu_hdr, ISNSP_HEADER_SIZE);
*pdu = NULL;
*pdu_size = 0;
return (0);
}
total_bytes_received += bytes_received;
*pdu_size = ISNSP_HEADER_SIZE + payload_len;
(*pdu) = (isns_pdu_t *)kmem_zalloc((*pdu_size), KM_SLEEP);
(*pdu)->version = ntohs(tmp_pdu_hdr->version);
(*pdu)->func_id = ntohs(tmp_pdu_hdr->func_id);
(*pdu)->payload_len = payload_len;
(*pdu)->flags = ntohs(tmp_pdu_hdr->flags);
(*pdu)->xid = ntohs(tmp_pdu_hdr->xid);
(*pdu)->seq = ntohs(tmp_pdu_hdr->seq);
bcopy(tmp_pdu_data, &((*pdu)->payload), payload_len);
kmem_free(tmp_pdu_data, payload_len);
tmp_pdu_data = NULL;
kmem_free(tmp_pdu_hdr, ISNSP_HEADER_SIZE);
tmp_pdu_hdr = NULL;
return (total_bytes_received);
}
static size_t
isns_create_dev_attr_reg_pdu(
uint8_t *node_name,
uint8_t *node_alias,
uint32_t node_type,
uint16_t *xid_p,
isns_pdu_t **out_pdu)
{
in_port_t local_port;
isns_pdu_t *pdu;
size_t pdu_size, node_name_len, node_alias_len;
uint16_t flags;
boolean_t rval = B_FALSE;
iscsi_addr_t local_addr;
ASSERT(node_name != NULL);
ASSERT(node_alias != NULL);
node_name_len = strlen((char *)node_name) + 1;
node_alias_len = strlen((char *)node_alias) + 1;
if (node_name_len == 1) {
*out_pdu = NULL;
return (0);
}
flags = ISNS_FLAG_FIRST_PDU |
ISNS_FLAG_LAST_PDU |
ISNS_FLAG_REPLACE_REG;
pdu_size = isns_create_pdu_header(ISNS_DEV_ATTR_REG, flags, &pdu);
*xid_p = pdu->xid;
if (isns_add_attr(pdu, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
node_name_len, node_name, 0) != 0) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu, pdu_size, ISNS_EID_ATTR_ID,
node_name_len, node_name, 0) != 0) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu, pdu_size, ISNS_DELIMITER_ATTR_ID, 0, 0, 0)
!= 0) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu, pdu_size, ISNS_EID_ATTR_ID,
node_name_len, node_name, 0) != 0) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu, pdu_size, ISNS_ENTITY_PROTOCOL_ATTR_ID, 4,
0, ISNS_ENTITY_PROTOCOL_ISCSI) != 0) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
node_name_len, node_name, 0) != 0) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
return (0);
}
if (node_alias_len > 1) {
if (isns_add_attr(pdu, pdu_size, ISNS_ISCSI_ALIAS_ATTR_ID,
node_alias_len, node_alias, 0) != 0) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
return (0);
}
}
if (isns_add_attr(pdu, pdu_size, ISNS_ISCSI_NODE_TYPE_ATTR_ID, 4,
0, node_type) != 0) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
return (0);
}
mutex_enter(&esi_scn_thr_mutex);
if (instance_listening_so != NULL) {
rval = find_listening_addr(&local_addr, instance_listening_so);
if (rval == B_FALSE) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
mutex_exit(&esi_scn_thr_mutex);
return (0);
}
} else {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
mutex_exit(&esi_scn_thr_mutex);
return (0);
}
local_port = local_addr.a_port;
if (isns_add_attr(pdu, pdu_size, ISNS_PORTAL_IP_ADDR_ATTR_ID, 16,
&(local_addr.a_addr.i_addr.in4),
local_addr.a_addr.i_insize) != 0) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
mutex_exit(&esi_scn_thr_mutex);
return (0);
}
mutex_exit(&esi_scn_thr_mutex);
if (isns_add_attr(pdu, pdu_size, ISNS_PORTAL_PORT_ATTR_ID, 4, 0,
local_port) != 0) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu, pdu_size, ISNS_SCN_PORT_ATTR_ID, 4, 0,
local_port) != 0) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu, pdu_size, ISNS_ESI_PORT_ATTR_ID, 4, 0,
local_port) != 0) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
return (0);
}
*out_pdu = pdu;
return (pdu_size);
}
static size_t
isns_create_dev_dereg_pdu(
uint8_t *node_name,
uint16_t *xid_p,
isns_pdu_t **out_pdu)
{
isns_pdu_t *pdu;
size_t pdu_size, node_name_len;
uint16_t flags;
ASSERT(node_name != NULL);
node_name_len = strlen((char *)node_name) + 1;
if (node_name_len == 1) {
*out_pdu = NULL;
return (0);
}
flags = ISNS_FLAG_FIRST_PDU |
ISNS_FLAG_LAST_PDU;
pdu_size = isns_create_pdu_header(ISNS_DEV_DEREG, flags, &pdu);
*xid_p = pdu->xid;
if (isns_add_attr(pdu, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
node_name_len, node_name, 0) != 0) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu, pdu_size, ISNS_DELIMITER_ATTR_ID, 0, 0, 0)
!= 0) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu, pdu_size, ISNS_EID_ATTR_ID,
node_name_len, node_name, 0) != 0) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
return (0);
}
*out_pdu = pdu;
return (pdu_size);
}
static size_t
isns_create_dev_attr_qry_target_nodes_pdu(
uint8_t *node_name,
uint8_t *node_alias,
uint16_t *xid_p, isns_pdu_t **out_pdu)
{
isns_pdu_t *pdu_p;
uint16_t flags;
size_t pdu_size, node_name_len;
ASSERT(node_name != NULL);
ASSERT(node_alias != NULL);
node_name_len = strlen((char *)node_name) + 1;
if (node_name_len == 1) {
*out_pdu = NULL;
return (0);
}
flags = ISNS_FLAG_FIRST_PDU |
ISNS_FLAG_LAST_PDU;
pdu_size = isns_create_pdu_header(ISNS_DEV_ATTR_QRY, flags, &pdu_p);
*xid_p = pdu_p->xid;
if (isns_add_attr(pdu_p, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
node_name_len, node_name, 0) != 0) {
kmem_free(pdu_p, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu_p, pdu_size, ISNS_ISCSI_NODE_TYPE_ATTR_ID,
4, 0, ISNS_TARGET_NODE_TYPE) != 0) {
kmem_free(pdu_p, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu_p, pdu_size,
ISNS_DELIMITER_ATTR_ID, 0, 0, 0) != 0) {
kmem_free(pdu_p, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu_p, pdu_size, ISNS_PG_ISCSI_NAME_ATTR_ID,
0, 0, 0) != 0) {
kmem_free(pdu_p, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu_p, pdu_size, ISNS_PG_PORTAL_IP_ADDR_ATTR_ID,
0, 0, 0) != 0) {
kmem_free(pdu_p, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu_p, pdu_size, ISNS_PG_PORTAL_PORT_ATTR_ID,
0, 0, 0) != 0) {
kmem_free(pdu_p, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu_p, pdu_size,
ISNS_PG_TAG_ATTR_ID, 0, 0, 0) != 0) {
kmem_free(pdu_p, pdu_size);
*out_pdu = NULL;
return (0);
}
*out_pdu = pdu_p;
return (pdu_size);
}
static
size_t
isns_create_dev_attr_qry_one_pg_pdu(
uint8_t *target_node_name,
uint8_t *source_node_name,
uint16_t *xid_p,
isns_pdu_t **out_pdu)
{
isns_pdu_t *pdu_p;
uint16_t flags;
size_t pdu_size, source_node_name_len, target_node_name_len;
ASSERT(target_node_name != NULL);
ASSERT(source_node_name != NULL);
source_node_name_len = strlen((char *)source_node_name) + 1;
target_node_name_len = strlen((char *)target_node_name) + 1;
if (source_node_name_len == 1) {
*out_pdu = NULL;
return (0);
}
flags = ISNS_FLAG_FIRST_PDU |
ISNS_FLAG_LAST_PDU;
pdu_size = isns_create_pdu_header(ISNS_DEV_ATTR_QRY, flags, &pdu_p);
*xid_p = pdu_p->xid;
if (isns_add_attr(pdu_p, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
source_node_name_len, source_node_name, 0) != 0) {
kmem_free(pdu_p, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu_p, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
target_node_name_len,
target_node_name, 0) != 0) {
kmem_free(pdu_p, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu_p, pdu_size,
ISNS_DELIMITER_ATTR_ID, 0, 0, 0) != 0) {
kmem_free(pdu_p, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu_p, pdu_size, ISNS_PG_ISCSI_NAME_ATTR_ID,
0, 0, 0) != 0) {
kmem_free(pdu_p, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu_p, pdu_size, ISNS_PG_PORTAL_IP_ADDR_ATTR_ID,
0, 0, 0) != 0) {
kmem_free(pdu_p, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu_p, pdu_size, ISNS_PG_PORTAL_PORT_ATTR_ID,
0, 0, 0) != 0) {
kmem_free(pdu_p, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu_p, pdu_size,
ISNS_PG_TAG_ATTR_ID, 0, 0, 0) != 0) {
kmem_free(pdu_p, pdu_size);
*out_pdu = NULL;
return (0);
}
*out_pdu = pdu_p;
return (pdu_size);
}
static
size_t
isns_create_scn_reg_pdu(
uint8_t *node_name,
uint8_t *node_alias,
uint16_t *xid_p,
isns_pdu_t **out_pdu)
{
isns_pdu_t *pdu;
size_t pdu_size, node_name_len;
uint16_t flags;
ASSERT(node_name != NULL);
ASSERT(node_alias != NULL);
node_name_len = strlen((char *)node_name) + 1;
if (node_name_len == 1) {
*out_pdu = NULL;
return (0);
}
flags = ISNS_FLAG_FIRST_PDU |
ISNS_FLAG_LAST_PDU;
pdu_size = isns_create_pdu_header(ISNS_SCN_REG, flags, &pdu);
*xid_p = pdu->xid;
if (isns_add_attr(pdu, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
node_name_len, node_name, 0) != 0) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
node_name_len, node_name, 0) != 0) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu, pdu_size, ISNS_DELIMITER_ATTR_ID, 0, 0, 0)
!= 0) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu, pdu_size, ISNS_ISCSI_SCN_BITMAP_ATTR_ID,
4,
0,
ISNS_TARGET_SELF_INFO_ONLY |
ISNS_OBJ_REMOVED |
ISNS_OBJ_ADDED |
ISNS_OBJ_UPDATED) != 0) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
return (0);
}
*out_pdu = pdu;
return (pdu_size);
}
static
size_t
isns_create_scn_dereg_pdu(
uint8_t *node_name,
uint16_t *xid_p,
isns_pdu_t **out_pdu)
{
isns_pdu_t *pdu;
size_t pdu_size, node_name_len;
uint16_t flags;
ASSERT(node_name != NULL);
node_name_len = strlen((char *)node_name) + 1;
if (node_name_len == 1) {
*out_pdu = NULL;
return (0);
}
flags = ISNS_FLAG_FIRST_PDU |
ISNS_FLAG_LAST_PDU;
pdu_size = isns_create_pdu_header(ISNS_SCN_DEREG, flags, &pdu);
*xid_p = pdu->xid;
if (isns_add_attr(pdu, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
node_name_len, node_name, 0) != 0) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
node_name_len, node_name, 0) != 0) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
return (0);
}
if (isns_add_attr(pdu, pdu_size, ISNS_DELIMITER_ATTR_ID, 0, 0, 0)
!= 0) {
kmem_free(pdu, pdu_size);
*out_pdu = NULL;
return (0);
}
*out_pdu = pdu;
return (pdu_size);
}
static
size_t
isns_create_esi_rsp_pdu(uint32_t rsp_status_code,
isns_pdu_t *esi_pdu,
uint16_t *xid_p,
isns_pdu_t **out_pdu)
{
isns_pdu_t *pdu_p;
uint16_t flags;
uint8_t *payload_ptr;
uint32_t swapped_status_code = htonl(rsp_status_code);
size_t pdu_size, payload_len = 0;
flags = ISNS_FLAG_FIRST_PDU |
ISNS_FLAG_LAST_PDU;
pdu_size = isns_create_pdu_header(ISNS_ESI_RSP, flags, &pdu_p);
*xid_p = pdu_p->xid;
payload_len = ntohs(pdu_p->payload_len);
payload_ptr = pdu_p->payload + payload_len;
bcopy(&swapped_status_code, payload_ptr, 4);
payload_len += 4;
payload_ptr = pdu_p->payload + payload_len;
if ((esi_pdu->payload_len) < ISNSP_MAX_PAYLOAD_SIZE) {
bcopy(esi_pdu->payload, payload_ptr,
(esi_pdu->payload_len));
payload_len += (esi_pdu->payload_len);
} else {
bcopy(esi_pdu->payload, payload_ptr, ISNSP_MAX_PAYLOAD_SIZE);
payload_len += ISNSP_MAX_PAYLOAD_SIZE;
}
pdu_p->payload_len = htons(payload_len);
if (isns_add_attr(pdu_p, pdu_size, ISNS_DELIMITER_ATTR_ID, 0, 0, 0)
!= 0) {
kmem_free(pdu_p, pdu_size);
*out_pdu = NULL;
return (0);
}
*out_pdu = pdu_p;
return (pdu_size);
}
static
size_t
isns_create_scn_rsp_pdu(uint32_t rsp_status_code,
isns_pdu_t *scn_pdu,
uint16_t *xid_p,
isns_pdu_t **out_pdu)
{
isns_pdu_t *pdu_p;
uint16_t flags;
uint8_t *payload_ptr;
uint32_t swapped_status_code = htonl(rsp_status_code);
size_t pdu_size, payload_len = 0;
flags = ISNS_FLAG_FIRST_PDU |
ISNS_FLAG_LAST_PDU;
pdu_size = isns_create_pdu_header(ISNS_SCN_RSP, flags, &pdu_p);
*xid_p = pdu_p->xid;
payload_len = ntohs(pdu_p->payload_len);
payload_ptr = pdu_p->payload + payload_len;
bcopy(&swapped_status_code, payload_ptr, 4);
payload_len += 4;
payload_ptr = pdu_p->payload + payload_len;
if ((scn_pdu->payload_len) < ISNSP_MAX_PAYLOAD_SIZE) {
bcopy(scn_pdu->payload, payload_ptr,
(scn_pdu->payload_len));
payload_len += (scn_pdu->payload_len);
} else {
bcopy(scn_pdu->payload, payload_ptr, ISNSP_MAX_PAYLOAD_SIZE);
payload_len += ISNSP_MAX_PAYLOAD_SIZE;
}
pdu_p->payload_len = htons(payload_len);
if (isns_add_attr(pdu_p, pdu_size, ISNS_DELIMITER_ATTR_ID, 0, 0, 0)
!= 0) {
kmem_free(pdu_p, pdu_size);
*out_pdu = NULL;
return (0);
}
*out_pdu = pdu_p;
return (pdu_size);
}
static
uint32_t
isns_process_dev_attr_reg_rsp(isns_pdu_t *resp_pdu_p)
{
isns_resp_t *resp_p;
if (resp_pdu_p->func_id != ISNS_DEV_ATTR_REG_RSP) {
return (ISNS_RSP_MSG_FORMAT_ERROR);
}
resp_p = (isns_resp_t *)resp_pdu_p->payload;
if (ntohl(resp_p->status) != ISNS_RSP_SUCCESSFUL) {
return (ntohl(resp_p->status));
}
return (ISNS_RSP_SUCCESSFUL);
}
static
uint32_t
isns_process_dev_attr_dereg_rsp(isns_pdu_t *resp_pdu_p)
{
isns_resp_t *resp_p;
if (resp_pdu_p->func_id != ISNS_DEV_DEREG_RSP) {
return (ISNS_RSP_MSG_FORMAT_ERROR);
}
resp_p = (isns_resp_t *)resp_pdu_p->payload;
if (ntohl(resp_p->status) != ISNS_RSP_SUCCESSFUL) {
return (ntohl(resp_p->status));
}
return (ISNS_RSP_SUCCESSFUL);
}
static
uint32_t
isns_process_scn_reg_rsp(isns_pdu_t *resp_pdu_p)
{
isns_resp_t *resp_p;
ASSERT(resp_pdu_p != NULL);
if (resp_pdu_p->func_id != ISNS_SCN_REG_RSP) {
return (ISNS_RSP_MSG_FORMAT_ERROR);
}
resp_p = (isns_resp_t *)resp_pdu_p->payload;
if (ntohl(resp_p->status) != ISNS_RSP_SUCCESSFUL) {
return (ntohl(resp_p->status));
}
return (ISNS_RSP_SUCCESSFUL);
}
static
uint32_t
isns_process_scn_dereg_rsp(isns_pdu_t *resp_pdu_p)
{
isns_resp_t *resp_p;
ASSERT(resp_pdu_p != NULL);
if (resp_pdu_p->func_id != ISNS_SCN_DEREG_RSP) {
return (ISNS_RSP_MSG_FORMAT_ERROR);
}
resp_p = (isns_resp_t *)resp_pdu_p->payload;
if (ntohl(resp_p->status) != ISNS_RSP_SUCCESSFUL) {
return (ntohl(resp_p->status));
}
return (ISNS_RSP_SUCCESSFUL);
}
static
uint32_t
isns_process_dev_attr_qry_target_nodes_pdu(
iscsi_addr_t *isns_server_addr, uint16_t payload_funcId,
isns_resp_t *resp_p, size_t resp_len,
isns_portal_group_list_t **pg_list)
{
boolean_t done_b, found_delimiter_b, target_node_type_b;
int num_of_pgs = 0, pg_sz, idx;
isns_tlv_t *attr_tlv_p;
uint8_t *data_p;
uint32_t len, total_payload_len = 0;
isns_portal_group_t *pg;
uint8_t junk[IPV4_RSVD_BYTES];
*pg_list = NULL;
bzero(junk, IPV4_RSVD_BYTES);
if (payload_funcId != ISNS_DEV_ATTR_QRY_RSP) {
return (ISNS_RSP_MSG_FORMAT_ERROR);
}
if (ntohl(resp_p->status) != ISNS_RSP_SUCCESSFUL) {
return (ntohl(resp_p->status));
}
if (resp_len < (ISNS_TLV_ATTR_ID_LEN +
ISNS_TLV_ATTR_LEN_LEN)) {
return (ISNS_RSP_MSG_FORMAT_ERROR);
}
data_p = resp_p->data;
done_b = B_FALSE;
found_delimiter_b = B_FALSE;
num_of_pgs = 0;
total_payload_len = sizeof (resp_p->status);
while (!done_b) {
attr_tlv_p = (isns_tlv_t *)data_p;
if (ntohl(attr_tlv_p->attr_id) == ISNS_DELIMITER_ATTR_ID) {
if (found_delimiter_b) {
done_b = B_TRUE;
} else {
found_delimiter_b = B_TRUE;
}
} else if (ntohl(attr_tlv_p->attr_id) ==
ISNS_PG_TAG_ATTR_ID) {
if (ntohl(attr_tlv_p->attr_len) > 0) {
num_of_pgs++;
}
}
len = ntohl(attr_tlv_p->attr_len);
total_payload_len += (ISNS_TLV_ATTR_ID_LEN +
ISNS_TLV_ATTR_LEN_LEN + len);
if (total_payload_len >= resp_len) {
done_b = B_TRUE;
} else {
data_p += (ISNS_TLV_ATTR_ID_LEN +
ISNS_TLV_ATTR_LEN_LEN + len);
}
}
pg_sz = sizeof (isns_portal_group_list_t);
if (num_of_pgs > 0) {
pg_sz += (num_of_pgs - 1) * sizeof (isns_portal_group_t);
}
DTRACE_PROBE1(isns_process_dev_attr_qry_target_nodes_pdu_pg_size,
int, pg_sz);
*pg_list = (isns_portal_group_list_t *)kmem_zalloc(pg_sz, KM_SLEEP);
(*pg_list)->pg_out_cnt = 0;
for (idx = 0; idx < num_of_pgs; idx++) {
pg = &((*pg_list)->pg_list[idx]);
bcopy(&isns_server_addr->a_addr, &pg->isns_server_ip,
sizeof (iscsi_ipaddr_t));
pg->isns_server_port = isns_server_addr->a_port;
}
data_p = resp_p->data;
done_b = B_FALSE;
found_delimiter_b = B_FALSE;
total_payload_len = sizeof (resp_p->status);
while (!done_b) {
attr_tlv_p = (isns_tlv_t *)data_p;
pg = &((*pg_list)->pg_list[(*pg_list)->pg_out_cnt]);
switch (ntohl(attr_tlv_p->attr_id)) {
case ISNS_DELIMITER_ATTR_ID:
if (found_delimiter_b) {
done_b = B_TRUE;
} else {
found_delimiter_b = B_TRUE;
}
break;
case ISNS_PG_ISCSI_NAME_ATTR_ID:
target_node_type_b = B_TRUE;
bcopy(attr_tlv_p->attr_value,
(char *)pg->pg_iscsi_name,
ntohl(attr_tlv_p->attr_len) <
ISCSI_MAX_NAME_LEN ?
ntohl(attr_tlv_p->attr_len) :
ISCSI_MAX_NAME_LEN);
DTRACE_PROBE1(isns_dev_attr_qry_process1,
char *, (char *)pg->pg_iscsi_name);
break;
case ISNS_PG_PORTAL_IP_ADDR_ATTR_ID:
if (target_node_type_b) {
if (ntohl(attr_tlv_p->attr_len) != 16) {
#define STRING_AALR "address attribute length received "
#define STRING_FISE16 "from iSNS server, Expected = 16, "
cmn_err(CE_NOTE, "Wrong IP "
STRING_AALR
STRING_FISE16
"Received = %d",
ntohl(
attr_tlv_p->attr_len));
return (
ISNS_RSP_MSG_FORMAT_ERROR);
#undef STRING_AALR
#undef STRING_FISE16
}
if ((bcmp(attr_tlv_p->attr_value, junk,
IPV4_RSVD_BYTES) == 0) &&
(((attr_tlv_p->attr_value[10] ==
0x00) &&
(attr_tlv_p->attr_value[11] ==
0x00)) ||
((attr_tlv_p->attr_value[10] ==
0xFF) &&
(attr_tlv_p->attr_value[11] ==
0xFF)))) {
bcopy(attr_tlv_p->attr_value +
12, &pg->pg_ip_addr.u_ip4,
sizeof (struct in_addr));
pg->insize =
sizeof (struct in_addr);
} else {
bcopy(attr_tlv_p->attr_value,
&pg->pg_ip_addr.u_ip6,
sizeof (struct in6_addr));
pg->insize =
sizeof (struct in6_addr);
}
}
break;
case ISNS_PG_PORTAL_PORT_ATTR_ID:
if (target_node_type_b) {
pg->pg_port =
ntohl(*(uint32_t *)
(*attr_tlv_p).
attr_value);
}
break;
case ISNS_PG_TAG_ATTR_ID:
if (target_node_type_b) {
pg->pg_tag =
ntohl(*(uint32_t *)
(*attr_tlv_p).
attr_value);
}
target_node_type_b = B_FALSE;
if (ntohl(attr_tlv_p->attr_len) > 0) {
(*pg_list)->pg_out_cnt++;
}
break;
default:
break;
}
len = ntohl(attr_tlv_p->attr_len);
total_payload_len += (ISNS_TLV_ATTR_ID_LEN +
ISNS_TLV_ATTR_LEN_LEN + len);
if ((total_payload_len >= resp_len) ||
((*pg_list)->pg_out_cnt == num_of_pgs)) {
done_b = B_TRUE;
} else {
data_p += (ISNS_TLV_ATTR_ID_LEN +
ISNS_TLV_ATTR_LEN_LEN + len);
}
}
return (ISNS_RSP_SUCCESSFUL);
}
static
uint32_t
isns_process_esi(isns_pdu_t *esi_pdu_p)
{
return (ISNS_RSP_SUCCESSFUL);
}
static
uint32_t
isns_process_scn(isns_pdu_t *scn_pdu_p, uint8_t *lhba_handle)
{
boolean_t dest_attr_found_b;
boolean_t done_b;
boolean_t scn_type_found_b;
isns_scn_callback_arg_t *scn_args_p;
isns_tlv_t *attr_tlv_p;
uint8_t *data_p;
uint8_t *src_attr;
uint32_t attr_eff_len, normalized_attr_len;
uint32_t scn_type;
uint32_t total_payload_len;
void (*scn_callback_to_use)(void *);
scn_callback_to_use = scn_callback_lookup(lhba_handle);
if (scn_callback_to_use == NULL) {
return (ISNS_RSP_INTERNAL_ERROR);
}
dest_attr_found_b = B_FALSE;
scn_type = 0;
scn_type_found_b = B_FALSE;
data_p = scn_pdu_p->payload;
done_b = B_FALSE;
total_payload_len = 0;
src_attr = (uint8_t *)kmem_zalloc(ISCSI_MAX_NAME_LEN, KM_SLEEP);
while (!done_b) {
attr_tlv_p = (isns_tlv_t *)data_p;
switch (ntohl(attr_tlv_p->attr_id)) {
case ISNS_ISCSI_NAME_ATTR_ID:
attr_eff_len = strlen(
(char *)attr_tlv_p->attr_value) + 1;
normalized_attr_len = (attr_eff_len % 4) == 0 ?
(attr_eff_len) :
(attr_eff_len + (4 - (attr_eff_len % 4)));
if (normalized_attr_len !=
ntohl(attr_tlv_p->attr_len)) {
kmem_free(src_attr, ISCSI_MAX_NAME_LEN);
return (ISNS_RSP_MSG_FORMAT_ERROR);
}
if ((dest_attr_found_b == B_TRUE) &&
(scn_type_found_b == B_TRUE)) {
bzero(src_attr, ISCSI_MAX_NAME_LEN);
bcopy(attr_tlv_p->attr_value,
(char *)src_attr,
ntohl(attr_tlv_p->attr_len) <
ISCSI_MAX_NAME_LEN ?
ntohl(attr_tlv_p->attr_len) :
ISCSI_MAX_NAME_LEN);
scn_args_p =
(isns_scn_callback_arg_t *)kmem_zalloc(
sizeof (isns_scn_callback_arg_t),
KM_SLEEP);
scn_args_p->scn_type = ntohl(scn_type);
bcopy(src_attr, scn_args_p->source_key_attr,
sizeof (scn_args_p->source_key_attr));
mutex_enter(&scn_taskq_mutex);
if (scn_taskq != NULL) {
(void) ddi_taskq_dispatch(scn_taskq,
scn_callback_to_use,
scn_args_p, DDI_SLEEP);
}
mutex_exit(&scn_taskq_mutex);
} else {
dest_attr_found_b = B_TRUE;
}
break;
case ISNS_ISCSI_SCN_BITMAP_ATTR_ID:
scn_type_found_b = B_TRUE;
bcopy(&(attr_tlv_p->attr_value), &scn_type, 4);
break;
case ISNS_DELIMITER_ATTR_ID:
done_b = B_TRUE;
break;
}
if (done_b == B_FALSE) {
total_payload_len += ntohl(attr_tlv_p->attr_len) +
ISNS_TLV_ATTR_ID_LEN + ISNS_TLV_ATTR_LEN_LEN;
if ((total_payload_len >= scn_pdu_p->payload_len) ||
(total_payload_len > ISNSP_MAX_PAYLOAD_SIZE)) {
done_b = B_TRUE;
} else {
if (scn_pdu_p->payload_len -
total_payload_len <=
ISNS_TLV_ATTR_ID_LEN +
ISNS_TLV_ATTR_LEN_LEN) {
done_b = B_TRUE;
} else {
data_p += (ISNS_TLV_ATTR_ID_LEN +
ISNS_TLV_ATTR_LEN_LEN +
ntohl(attr_tlv_p->attr_len));
}
}
}
}
kmem_free(src_attr, ISCSI_MAX_NAME_LEN);
return (ISNS_RSP_SUCCESSFUL);
}
static
size_t
isns_create_pdu_header(uint16_t func_id, uint16_t flags, isns_pdu_t **pdu)
{
size_t pdu_size = ISNSP_MAX_PDU_SIZE;
*pdu = (isns_pdu_t *)kmem_zalloc(pdu_size, KM_SLEEP);
(void) memset((*pdu), 0, pdu_size);
(*pdu)->version = htons((uint16_t)ISNSP_VERSION);
(*pdu)->func_id = htons((uint16_t)func_id);
(*pdu)->payload_len = htons(0);
(*pdu)->flags = htons((uint16_t)(flags | ISNS_FLAG_CLIENT));
(*pdu)->xid = htons(create_xid());
(*pdu)->seq = htons(0);
return (pdu_size);
}
static
int
isns_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:
bcopy((char *)attr_data,
attr_tlv->attr_value,
attr_len);
break;
default:
switch (normalized_attr_len) {
case 0:
break;
case 4:
*(uint32_t *)attr_tlv->attr_value =
htonl(attr_numeric_data);
break;
case 8:
*(uint64_t *)attr_tlv->attr_value =
BE_64((uint64_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
isns_service_esi_scn(iscsi_thread_t *thread, void *arg)
{
int clnt_len;
isns_async_thread_arg_t *larg;
isns_pdu_t *in_pdu;
size_t bytes_received, in_pdu_size = 0;
uint8_t *lhba_handle;
struct sockaddr_in6 t_addr;
socklen_t t_addrlen;
union {
struct sockaddr sin;
struct sockaddr_in s_in4;
struct sockaddr_in6 s_in6;
} clnt_addr = { 0 };
union {
struct sockaddr_in soa4;
struct sockaddr_in6 soa6;
} local_conn_prop;
void *listening_so, *connecting_so;
larg = (isns_async_thread_arg_t *)arg;
listening_so = larg->listening_so;
lhba_handle = larg->lhba_handle;
kmem_free(larg, sizeof (*larg));
bzero(&t_addr, sizeof (struct sockaddr_in6));
t_addrlen = sizeof (struct sockaddr_in6);
(void) iscsi_net->getsockname(listening_so,
(struct sockaddr *)&t_addr, &t_addrlen);
if (t_addrlen <= sizeof (local_conn_prop)) {
bcopy(&t_addr, &local_conn_prop, t_addrlen);
}
if (iscsi_net->listen(listening_so, 5) < 0) {
iscsi_net->close(listening_so);
}
for (;;) {
int rval;
isns_pdu_t *out_pdu;
size_t out_pdu_size;
clnt_len = sizeof (clnt_addr);
connecting_so = iscsi_net->accept(
listening_so, &clnt_addr.sin, &clnt_len);
mutex_enter(&esi_scn_thr_mutex);
if (esi_scn_thr_to_shutdown == B_TRUE) {
mutex_exit(&esi_scn_thr_mutex);
return;
}
mutex_exit(&esi_scn_thr_mutex);
if (connecting_so == NULL) {
iscsi_net->close(listening_so);
continue;
}
bytes_received = isns_rcv_pdu(connecting_so, &in_pdu,
&in_pdu_size);
if (in_pdu == NULL) {
continue;
}
if (bytes_received == 0) {
continue;
}
switch (in_pdu->func_id) {
case ISNS_ESI:
case ISNS_SCN:
if (in_pdu->func_id == ISNS_ESI) {
rval = isns_process_esi(in_pdu);
out_pdu_size = isns_create_esi_rsp_pdu(
rval,
in_pdu,
&xid,
&out_pdu);
} else if (in_pdu->func_id == ISNS_SCN) {
rval = isns_process_scn(in_pdu,
lhba_handle);
out_pdu_size = isns_create_scn_rsp_pdu(
rval,
in_pdu,
&xid,
&out_pdu);
} else {
kmem_free(in_pdu, in_pdu_size);
in_pdu = NULL;
continue;
}
if (out_pdu_size == 0) {
kmem_free(in_pdu, in_pdu_size);
in_pdu = NULL;
continue;
}
(void) isns_send_pdu(connecting_so, out_pdu);
kmem_free(out_pdu, out_pdu_size);
out_pdu = NULL;
kmem_free(in_pdu, in_pdu_size);
in_pdu = NULL;
iscsi_net->close(connecting_so);
break;
default:
kmem_free(in_pdu, in_pdu_size);
in_pdu = NULL;
continue;
}
}
}
static
boolean_t
find_listening_addr(iscsi_addr_t *local_addr, void *listening_so)
{
union {
struct sockaddr_in soa4;
struct sockaddr_in6 soa6;
} local_conn_prop = { 0 };
struct sockaddr_in6 t_addr;
socklen_t t_addrlen;
if (local_addr == NULL || listening_so == NULL) {
return (B_FALSE);
}
bzero(&t_addr, sizeof (struct sockaddr_in6));
t_addrlen = sizeof (struct sockaddr_in6);
(void) iscsi_net->getsockname(listening_so, (struct sockaddr *)&t_addr,
&t_addrlen);
if (t_addrlen > sizeof (local_conn_prop)) {
return (B_FALSE);
}
bcopy(&t_addr, &local_conn_prop, t_addrlen);
if (local_conn_prop.soa4.sin_family == AF_INET) {
local_addr->a_addr.i_addr.in4.s_addr =
local_conn_prop.soa4.sin_addr.s_addr;
local_addr->a_addr.i_insize = sizeof (in_addr_t);
} else if (local_conn_prop.soa4.sin_family == AF_INET6) {
return (B_FALSE);
} else {
return (B_FALSE);
}
local_addr->a_port = ntohs(local_conn_prop.soa4.sin_port);
return (B_TRUE);
}
static
boolean_t
find_local_portal(iscsi_addr_t *isns_server_addr,
iscsi_addr_t **local_addr, void **listening_so)
{
union {
struct sockaddr_in soa4;
struct sockaddr_in6 soa6;
} local_conn_prop = { 0 };
union {
struct sockaddr sin;
struct sockaddr_in s_in4;
struct sockaddr_in6 s_in6;
} serv_addr = { 0 };
void *so;
struct sockaddr_in6 t_addr;
socklen_t t_addrlen;
if (listening_so == NULL) {
return (B_FALSE);
}
if (local_addr != NULL) {
*local_addr = NULL;
}
*listening_so = NULL;
bzero(&t_addr, sizeof (struct sockaddr_in6));
t_addrlen = sizeof (struct sockaddr_in6);
if (local_addr != NULL) {
so = isns_open(isns_server_addr);
if (so == NULL) {
return (B_FALSE);
}
iscsi_net->getsockname(so,
(struct sockaddr *)&t_addr, &t_addrlen);
if (t_addrlen > sizeof (local_conn_prop)) {
iscsi_net->close(so);
return (B_FALSE);
}
bcopy(&t_addr, &local_conn_prop, t_addrlen);
t_addrlen = sizeof (struct sockaddr_in6);
if (local_conn_prop.soa4.sin_family == AF_INET) {
*local_addr =
(iscsi_addr_t *)kmem_zalloc(sizeof (iscsi_addr_t),
KM_SLEEP);
(*local_addr)->a_addr.i_addr.in4.s_addr =
local_conn_prop.soa4.sin_addr.s_addr;
(*local_addr)->a_addr.i_insize = sizeof (in_addr_t);
} else if (local_conn_prop.soa4.sin_family == AF_INET6) {
return (B_FALSE);
} else {
iscsi_net->close(so);
return (B_FALSE);
}
iscsi_net->close(so);
}
serv_addr.s_in4.sin_family = AF_INET;
serv_addr.s_in4.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.s_in4.sin_port = htons(0);
so = iscsi_net->socket(AF_INET, SOCK_STREAM, 0);
if (so == NULL) {
if (local_addr != NULL && (*local_addr != NULL)) {
kmem_free((*local_addr), sizeof (iscsi_addr_t));
*local_addr = NULL;
}
return (B_FALSE);
}
if (iscsi_net->bind(so, &serv_addr.sin,
sizeof (struct sockaddr), 0, 0) < 0) {
if (local_addr != NULL && (*local_addr != NULL)) {
kmem_free((*local_addr), sizeof (iscsi_addr_t));
*local_addr = NULL;
}
iscsi_net->close(so);
return (B_FALSE);
}
if (local_addr != NULL && (*local_addr != NULL)) {
(void) iscsi_net->getsockname(so, (struct sockaddr *)&t_addr,
&t_addrlen);
if (t_addrlen <= sizeof (local_conn_prop)) {
bcopy(&t_addr, &local_conn_prop, t_addrlen);
(*local_addr)->a_port =
ntohs(local_conn_prop.soa4.sin_port);
} else {
(*local_addr)->a_port = ISNS_DEFAULT_ESI_SCN_PORT;
}
}
*listening_so = so;
return (B_TRUE);
}
static
void
(*scn_callback_lookup(uint8_t *lhba_handle))(void *)
{
return (scn_callback_p);
}
static
uint16_t
create_xid()
{
return (xid++ % MAX_XID);
}
static
void
esi_scn_thr_cleanup()
{
boolean_t unblock_esi_scn_thr_b = B_FALSE;
iscsi_addr_t local_addr;
mutex_enter(&esi_scn_thr_mutex);
if (esi_scn_thr_to_shutdown == B_FALSE) {
esi_scn_thr_to_shutdown = B_TRUE;
if (instance_listening_so != NULL &&
(find_listening_addr(&local_addr,
instance_listening_so) == B_TRUE)) {
isns_pdu_t *out_pdu;
size_t out_pdu_size;
void *connecting_so;
connecting_so = isns_open(&local_addr);
if (connecting_so == NULL) {
unblock_esi_scn_thr_b = B_FALSE;
esi_scn_thr_to_shutdown = B_FALSE;
} else {
out_pdu_size = isns_create_pdu_header(0,
ISNS_FLAG_FIRST_PDU |
ISNS_FLAG_LAST_PDU,
&out_pdu);
if (isns_send_pdu(connecting_so,
out_pdu) != 0) {
unblock_esi_scn_thr_b = B_FALSE;
esi_scn_thr_to_shutdown = B_FALSE;
} else {
unblock_esi_scn_thr_b = B_TRUE;
}
iscsi_net->close(connecting_so);
kmem_free(out_pdu, out_pdu_size);
out_pdu = NULL;
}
}
if (unblock_esi_scn_thr_b == B_TRUE) {
mutex_exit(&esi_scn_thr_mutex);
(void) iscsi_thread_stop(esi_scn_thr_id);
iscsi_thread_destroy(esi_scn_thr_id);
mutex_enter(&esi_scn_thr_mutex);
esi_scn_thr_id = NULL;
iscsi_net->shutdown(instance_listening_so, 2);
iscsi_net->close(instance_listening_so);
instance_listening_so = NULL;
}
}
mutex_exit(&esi_scn_thr_mutex);
}