1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  *
  25  * iSNS Client
  26  */
  27 
  28 #include "iscsi.h"              /* For ISCSI_MAX_IOVEC */
  29 #include "isns_protocol.h"
  30 #include "isns_client.h"
  31 #include "persistent.h"
  32 
  33 #ifdef _KERNEL
  34 #include <sys/sunddi.h>
  35 #else
  36 #include <stdlib.h>
  37 #endif
  38 #include <netinet/tcp.h>
  39 #include <sys/types.h>
  40 
  41 /* For local use */
  42 #define ISNS_MAX_IOVEC          5
  43 #define MAX_XID                 (2^16)
  44 #define MAX_RCV_RSP_COUNT       10      /* Maximum number of unmatched xid */
  45 #define ISNS_RCV_TIMEOUT        5
  46 #define ISNS_RCV_RETRY_MAX      2
  47 #define IPV4_RSVD_BYTES         10
  48 
  49 typedef struct isns_reg_arg {
  50         iscsi_addr_t *isns_server_addr;
  51         uint8_t *node_name;
  52         size_t node_name_len;
  53         uint8_t *node_alias;
  54         size_t node_alias_len;
  55         uint32_t node_type;
  56         uint8_t *lhba_handle;
  57 } isns_reg_arg_t;
  58 
  59 typedef struct isns_async_thread_arg {
  60         uint8_t *lhba_handle;
  61         void *listening_so;
  62 } isns_async_thread_arg_t;
  63 
  64 /* One global queue to serve all LHBA instances. */
  65 static ddi_taskq_t *reg_query_taskq;
  66 static kmutex_t reg_query_taskq_mutex;
  67 
  68 /* One global queue to serve all LHBA instances. */
  69 static ddi_taskq_t *scn_taskq;
  70 static kmutex_t scn_taskq_mutex;
  71 
  72 /* One globally maintained transaction ID. */
  73 static uint16_t xid = 0;
  74 
  75 /*
  76  * One SCN callback registration per LHBA instance. For now, since we
  77  * support only one instance, we create one place holder for the
  78  * callback.
  79  */
  80 void (*scn_callback_p)(void *);
  81 
  82 /*
  83  * One thread, port, local address, and listening socket per LHBA instance.
  84  * For now, since we support only one instance, we create one set of place
  85  * holder for these data.
  86  */
  87 static boolean_t esi_scn_thr_to_shutdown = B_FALSE;
  88 static iscsi_thread_t *esi_scn_thr_id = NULL;
  89 static void *instance_listening_so = NULL;
  90 /*
  91  * This mutex protects all the per LHBA instance variables, i.e.,
  92  * esi_scn_thr_to_shutdown, esi_scn_thr_id, and instance_listening_so.
  93  */
  94 static kmutex_t esi_scn_thr_mutex;
  95 
  96 /* iSNS related helpers */
  97 /* Return status */
  98 #define ISNS_OK                         0
  99 #define ISNS_BAD_SVR_ADDR               1
 100 #define ISNS_INTERNAL_ERR               2
 101 #define ISNS_CANNOT_FIND_LOCAL_ADDR     3
 102 static int discover_isns_server(uint8_t *lhba_handle,
 103     iscsi_addr_list_t **isns_server_addrs);
 104 static int create_esi_scn_thr(uint8_t *lhba_handle,
 105     iscsi_addr_t *isns_server_addr);
 106 static void esi_scn_thr_cleanup(void);
 107 static void register_isns_client(void *arg);
 108 static isns_status_t do_isns_dev_attr_reg(iscsi_addr_t *isns_server_addr,
 109     uint8_t *node_name, uint8_t *node_alias, uint32_t node_type);
 110 static isns_status_t do_isns_dev_dereg(iscsi_addr_t *isns_server_addr,
 111     uint8_t *node_name);
 112 
 113 /*
 114  * Make query to all iSNS servers visible to the specified LHBA.
 115  * The query could be made for all target nodes or for a specific target
 116  * node.
 117  */
 118 static isns_status_t do_isns_query(boolean_t is_query_all_nodes_b,
 119     uint8_t *lhba_handle, uint8_t *target_node_name,
 120     uint8_t *source_node_name, uint8_t *source_node_alias,
 121     uint32_t source_node_type, isns_portal_group_list_t **pg_list);
 122 
 123 /*
 124  * Create DevAttrQuery message requesting portal group information for all
 125  * target nodes. Send it to the specified iSNS server. Parse the
 126  * DevAttrQueryRsp PDU and translate the results into a portal group list
 127  * object.
 128  */
 129 static isns_status_t do_isns_dev_attr_query_all_nodes(
 130     iscsi_addr_t *isns_server_addr, uint8_t *node_name,
 131     uint8_t *node_alias, isns_portal_group_list_t **pg_list);
 132 
 133 /*
 134  * Create DevAttrQuery message requesting portal group information for the
 135  * specified target node. Send it to the specified iSNS server. Parse the
 136  * DevAttrQueryRsp PDU and translate the results into a portal group list
 137  * object.
 138  */
 139 static isns_status_t do_isns_dev_attr_query_one_node(
 140     iscsi_addr_t *isns_server_addr, uint8_t *target_node_name,
 141     uint8_t *source_node_name, uint8_t *source_node_alias,
 142     uint32_t source_node_type, isns_portal_group_list_t **pg_list);
 143 
 144 static void isns_service_esi_scn(iscsi_thread_t *thread, void* arg);
 145 static void (*scn_callback_lookup(uint8_t *lhba_handle))(void *);
 146 
 147 /* Transport related helpers */
 148 static void *isns_open(iscsi_addr_t *isns_server_addr);
 149 static ssize_t isns_send_pdu(void *socket, isns_pdu_t *pdu);
 150 static size_t isns_rcv_pdu(void *so, isns_pdu_t **pdu, size_t *pdu_size);
 151 static boolean_t find_listening_addr(iscsi_addr_t *local_addr,
 152     void *listening_so);
 153 static boolean_t find_local_portal(iscsi_addr_t *isns_server_addr,
 154     iscsi_addr_t **local_addr, void **listening_so);
 155 
 156 /* iSNS protocol related helpers */
 157 static size_t isns_create_pdu_header(uint16_t func_id,
 158     uint16_t flags, isns_pdu_t **pdu);
 159 static int isns_add_attr(isns_pdu_t *pdu,
 160     size_t max_pdu_size, uint32_t attr_id, uint32_t attr_len,
 161     void *attr_data, uint32_t attr_numeric_data);
 162 static uint16_t create_xid(void);
 163 static size_t isns_create_dev_attr_reg_pdu(
 164     uint8_t *node_name, uint8_t *node_alias, uint32_t node_type,
 165     uint16_t *xid, isns_pdu_t **out_pdu);
 166 static size_t isns_create_dev_dereg_pdu(uint8_t *node_name,
 167     uint16_t *xid_p, isns_pdu_t **out_pdu);
 168 static size_t isns_create_dev_attr_qry_target_nodes_pdu(
 169     uint8_t *node_name, uint8_t *node_alias, uint16_t *xid,
 170     isns_pdu_t **out_pdu);
 171 static size_t isns_create_dev_attr_qry_one_pg_pdu(
 172     uint8_t *target_node_name, uint8_t *source_node_name,
 173     uint16_t *xid, isns_pdu_t **out_pdu);
 174 static size_t isns_create_esi_rsp_pdu(uint32_t rsp_status_code,
 175     isns_pdu_t *pdu, uint16_t *xid, isns_pdu_t **out_pdu);
 176 static size_t isns_create_scn_reg_pdu(uint8_t *node_name,
 177     uint8_t *node_alias, uint16_t *xid, isns_pdu_t **out_pdu);
 178 static size_t isns_create_scn_dereg_pdu(uint8_t *node_name,
 179     uint16_t *xid_p, isns_pdu_t **out_pdu);
 180 static size_t isns_create_scn_rsp_pdu(uint32_t rsp_status_code,
 181     isns_pdu_t *pdu, uint16_t *xid, isns_pdu_t **out_pdu);
 182 static uint32_t isns_process_dev_attr_reg_rsp(isns_pdu_t *resp_pdu_p);
 183 static uint32_t isns_process_dev_attr_dereg_rsp(isns_pdu_t *resp_pdu_p);
 184 
 185 /*
 186  * Process and parse a DevAttrQryRsp message. The routine creates a list
 187  * of Portal Group objects if the message is parasable without any issue.
 188  * If the parsing is not successful, the pg_list will be set to NULL.
 189  */
 190 static uint32_t isns_process_dev_attr_qry_target_nodes_pdu(
 191     iscsi_addr_t *isns_server_addr, uint16_t payload_funcId,
 192     isns_resp_t *resp_p, size_t resp_len,
 193     isns_portal_group_list_t **pg_list);
 194 static uint32_t isns_process_scn_reg_rsp(isns_pdu_t *resp_pdu_p);
 195 static uint32_t isns_process_scn_dereg_rsp(isns_pdu_t *resp_pdu_p);
 196 static uint32_t isns_process_esi(isns_pdu_t *esi_pdu_p);
 197 static uint32_t isns_process_scn(isns_pdu_t *scn_pdu_p, uint8_t *lhba_handle);
 198 
 199 void
 200 isns_client_init()
 201 {
 202         mutex_init(&reg_query_taskq_mutex, NULL, MUTEX_DRIVER, NULL);
 203         mutex_enter(&reg_query_taskq_mutex);
 204         reg_query_taskq = ddi_taskq_create(NULL, "isns_reg_query_taskq",
 205             1, TASKQ_DEFAULTPRI, 0);
 206         mutex_exit(&reg_query_taskq_mutex);
 207 
 208         mutex_init(&scn_taskq_mutex, NULL, MUTEX_DRIVER, NULL);
 209         mutex_enter(&scn_taskq_mutex);
 210         scn_taskq = ddi_taskq_create(NULL, "isns_scn_taskq",
 211             1, TASKQ_DEFAULTPRI, 0);
 212         mutex_exit(&scn_taskq_mutex);
 213 
 214         mutex_init(&esi_scn_thr_mutex, NULL, MUTEX_DRIVER, NULL);
 215 
 216         /* MISC initializations. */
 217         scn_callback_p = NULL;
 218         esi_scn_thr_id = NULL;
 219         instance_listening_so = NULL;
 220         esi_scn_thr_to_shutdown = B_FALSE;
 221         xid = 0;
 222 }
 223 
 224 void
 225 isns_client_cleanup()
 226 {
 227         ddi_taskq_t *tmp_taskq_p;
 228 
 229         mutex_enter(&scn_taskq_mutex);
 230         tmp_taskq_p = scn_taskq;
 231         scn_taskq = NULL;
 232         mutex_exit(&scn_taskq_mutex);
 233         ddi_taskq_destroy(tmp_taskq_p);
 234 
 235         mutex_enter(&reg_query_taskq_mutex);
 236         tmp_taskq_p = reg_query_taskq;
 237         reg_query_taskq = NULL;
 238         mutex_exit(&reg_query_taskq_mutex);
 239         ddi_taskq_destroy(tmp_taskq_p);
 240 
 241         mutex_destroy(&reg_query_taskq_mutex);
 242         mutex_destroy(&scn_taskq_mutex);
 243 
 244         esi_scn_thr_cleanup();
 245 
 246         mutex_destroy(&esi_scn_thr_mutex);
 247 }
 248 
 249 isns_status_t
 250 isns_reg(uint8_t *lhba_handle,
 251         uint8_t *node_name,
 252         size_t node_name_len,
 253         uint8_t *node_alias,
 254         size_t node_alias_len,
 255         uint32_t node_type,
 256         void (*scn_callback)(void *))
 257 {
 258         int i;
 259         int list_space;
 260         iscsi_addr_list_t *isns_server_addr_list;
 261         isns_reg_arg_t *reg_args_p;
 262 
 263         /* Look up the iSNS Server address(es) based on the specified ISID */
 264         if (discover_isns_server(lhba_handle, &isns_server_addr_list) !=
 265             ISNS_OK) {
 266                 return (isns_no_svr_found);
 267         }
 268 
 269         /* No iSNS server discovered - no registration needed. */
 270         if (isns_server_addr_list->al_out_cnt == 0) {
 271                 list_space = sizeof (iscsi_addr_list_t);
 272                 kmem_free(isns_server_addr_list, list_space);
 273                 isns_server_addr_list = NULL;
 274                 return (isns_no_svr_found);
 275         }
 276 
 277         /* Check and create ESI/SCN threads and populate local address */
 278         for (i = 0; i < isns_server_addr_list->al_out_cnt; i++) {
 279                 if (create_esi_scn_thr(lhba_handle,
 280                     &(isns_server_addr_list->al_addrs[i])) == ISNS_OK) {
 281                         break;
 282                 }
 283         }
 284         if (i == isns_server_addr_list->al_out_cnt) {
 285                 /*
 286                  * Problem creating ESI/SCN thread
 287                  * Free the server list
 288                  */
 289                 list_space = sizeof (iscsi_addr_list_t);
 290                 if (isns_server_addr_list->al_out_cnt > 0) {
 291                         list_space += (sizeof (iscsi_addr_t) *
 292                             (isns_server_addr_list->al_out_cnt - 1));
 293                 }
 294                 kmem_free(isns_server_addr_list, list_space);
 295                 isns_server_addr_list = NULL;
 296                 return (isns_internal_err);
 297         }
 298 
 299         /* Register against all iSNS servers discovered. */
 300         for (i = 0; i < isns_server_addr_list->al_out_cnt; i++) {
 301                 reg_args_p = kmem_zalloc(sizeof (isns_reg_arg_t), KM_SLEEP);
 302                 reg_args_p->isns_server_addr =
 303                     kmem_zalloc(sizeof (iscsi_addr_t), KM_SLEEP);
 304                 bcopy(&isns_server_addr_list->al_addrs[i],
 305                     reg_args_p->isns_server_addr, sizeof (iscsi_addr_t));
 306                 reg_args_p->node_name = kmem_zalloc(node_name_len, KM_SLEEP);
 307                 bcopy(node_name, reg_args_p->node_name, node_name_len);
 308                 reg_args_p->node_name_len = node_name_len;
 309                 reg_args_p->node_alias = kmem_zalloc(node_alias_len, KM_SLEEP);
 310                 bcopy(node_alias, reg_args_p->node_alias, node_alias_len);
 311                 reg_args_p->node_alias_len = node_alias_len;
 312                 reg_args_p->node_type = node_type;
 313 
 314                 /* Dispatch the registration request */
 315                 register_isns_client(reg_args_p);
 316         }
 317 
 318         /* Free the server list */
 319         list_space = sizeof (iscsi_addr_list_t);
 320         if (isns_server_addr_list->al_out_cnt > 0) {
 321                 list_space += (sizeof (iscsi_addr_t) *
 322                     (isns_server_addr_list->al_out_cnt - 1));
 323         }
 324         kmem_free(isns_server_addr_list, list_space);
 325         isns_server_addr_list = NULL;
 326 
 327         /* Register the scn_callback. */
 328         scn_callback_p = scn_callback;
 329 
 330         return (isns_ok);
 331 }
 332 
 333 isns_status_t
 334 isns_reg_one_server(entry_t *isns_server,
 335         uint8_t *lhba_handle,
 336         uint8_t *node_name,
 337         size_t node_name_len,
 338         uint8_t *node_alias,
 339         size_t node_alias_len,
 340         uint32_t node_type,
 341         void (*scn_callback)(void *))
 342 {
 343         int status;
 344         iscsi_addr_t *ap;
 345         isns_reg_arg_t *reg_args_p;
 346 
 347         ap = (iscsi_addr_t *)kmem_zalloc(sizeof (iscsi_addr_t), KM_SLEEP);
 348         ap->a_port = isns_server->e_port;
 349         ap->a_addr.i_insize = isns_server->e_insize;
 350         if (isns_server->e_insize == sizeof (struct in_addr)) {
 351                 ap->a_addr.i_addr.in4.s_addr = (isns_server->e_u.u_in4.s_addr);
 352         } else if (isns_server->e_insize == sizeof (struct in6_addr)) {
 353                 bcopy(&(isns_server->e_u.u_in6.s6_addr),
 354                     ap->a_addr.i_addr.in6.s6_addr,
 355                     sizeof (struct in6_addr));
 356         } else {
 357                 kmem_free(ap, sizeof (iscsi_addr_t));
 358                 return (isns_op_failed);
 359         }
 360 
 361         /* Check and create ESI/SCN threads and populate local address */
 362         if ((status = create_esi_scn_thr(lhba_handle, ap))
 363             != ISNS_OK) {
 364                 /* Problem creating ESI/SCN thread */
 365                 DTRACE_PROBE1(isns_reg_one_server_create_esi_scn_thr,
 366                     int, status);
 367                 kmem_free(ap, sizeof (iscsi_addr_t));
 368                 return (isns_internal_err);
 369         }
 370 
 371         reg_args_p = kmem_zalloc(sizeof (isns_reg_arg_t), KM_SLEEP);
 372         reg_args_p->isns_server_addr =
 373             kmem_zalloc(sizeof (iscsi_addr_t), KM_SLEEP);
 374         bcopy(ap, reg_args_p->isns_server_addr, sizeof (iscsi_addr_t));
 375         reg_args_p->node_name = kmem_zalloc(node_name_len, KM_SLEEP);
 376         bcopy(node_name, reg_args_p->node_name, node_name_len);
 377         reg_args_p->node_name_len = node_name_len;
 378         reg_args_p->node_alias = kmem_zalloc(node_alias_len, KM_SLEEP);
 379         bcopy(node_alias, reg_args_p->node_alias, node_alias_len);
 380         reg_args_p->node_alias_len = node_alias_len;
 381         reg_args_p->node_type = node_type;
 382 
 383         /* Dispatch the registration request */
 384         register_isns_client(reg_args_p);
 385 
 386         /* Register the scn_callback. */
 387         scn_callback_p = scn_callback;
 388 
 389         kmem_free(ap, sizeof (iscsi_addr_t));
 390         return (isns_ok);
 391 }
 392 
 393 isns_status_t
 394 isns_dereg(uint8_t *lhba_handle,
 395         uint8_t *node_name)
 396 {
 397         int i;
 398         int isns_svr_lst_sz;
 399         int list_space;
 400         iscsi_addr_list_t *isns_server_addr_list = NULL;
 401         isns_status_t dereg_stat, combined_dereg_stat;
 402 
 403         /* Look up the iSNS Server address(es) based on the specified ISID */
 404         if (discover_isns_server(lhba_handle, &isns_server_addr_list) !=
 405             ISNS_OK) {
 406                 return (isns_no_svr_found);
 407         }
 408         ASSERT(isns_server_addr_list != NULL);
 409         if (isns_server_addr_list->al_out_cnt == 0) {
 410                 isns_svr_lst_sz = sizeof (iscsi_addr_list_t);
 411                 kmem_free(isns_server_addr_list, isns_svr_lst_sz);
 412                 isns_server_addr_list = NULL;
 413                 return (isns_no_svr_found);
 414         }
 415 
 416         combined_dereg_stat = isns_ok;
 417         for (i = 0; i < isns_server_addr_list->al_out_cnt; i++) {
 418                 dereg_stat = do_isns_dev_dereg(
 419                     &isns_server_addr_list->al_addrs[i],
 420                     node_name);
 421                 if (dereg_stat == isns_ok) {
 422                         if (combined_dereg_stat != isns_ok) {
 423                                 combined_dereg_stat = isns_op_partially_failed;
 424                         }
 425                 } else {
 426                         if (combined_dereg_stat == isns_ok) {
 427                                 combined_dereg_stat = isns_op_partially_failed;
 428                         }
 429                 }
 430         }
 431 
 432         /* Free the server list. */
 433         list_space = sizeof (iscsi_addr_list_t);
 434         if (isns_server_addr_list->al_out_cnt > 0) {
 435                 list_space += (sizeof (iscsi_addr_t) *
 436                     (isns_server_addr_list->al_out_cnt - 1));
 437         }
 438         kmem_free(isns_server_addr_list, list_space);
 439         isns_server_addr_list = NULL;
 440 
 441         /* Cleanup ESI/SCN thread. */
 442         esi_scn_thr_cleanup();
 443 
 444         return (combined_dereg_stat);
 445 }
 446 
 447 isns_status_t
 448 isns_dereg_one_server(entry_t *isns_server,
 449         uint8_t *node_name,
 450         boolean_t is_last_isns_server_b)
 451 {
 452         iscsi_addr_t *ap;
 453         isns_status_t dereg_stat;
 454 
 455         ap = (iscsi_addr_t *)kmem_zalloc(sizeof (iscsi_addr_t), KM_SLEEP);
 456         ap->a_port = isns_server->e_port;
 457         ap->a_addr.i_insize = isns_server->e_insize;
 458         if (isns_server->e_insize == sizeof (struct in_addr)) {
 459                 ap->a_addr.i_addr.in4.s_addr = (isns_server->e_u.u_in4.s_addr);
 460         } else if (isns_server->e_insize == sizeof (struct in6_addr)) {
 461                 bcopy(&(isns_server->e_u.u_in6.s6_addr),
 462                     ap->a_addr.i_addr.in6.s6_addr,
 463                     sizeof (struct in6_addr));
 464         } else {
 465                 kmem_free(ap, sizeof (iscsi_addr_t));
 466                 return (isns_op_failed);
 467         }
 468 
 469         dereg_stat = do_isns_dev_dereg(ap, node_name);
 470 
 471         kmem_free(ap, sizeof (iscsi_addr_t));
 472 
 473         if (is_last_isns_server_b == B_TRUE) {
 474                 /*
 475                  * Clean up ESI/SCN thread resource if it is the
 476                  * last known iSNS server.
 477                  */
 478                 esi_scn_thr_cleanup();
 479         }
 480 
 481         return (dereg_stat);
 482 }
 483 
 484 isns_status_t
 485 isns_query(uint8_t *lhba_handle,
 486         uint8_t *node_name,
 487         uint8_t *node_alias,
 488         uint32_t node_type,
 489         isns_portal_group_list_t **pg_list)
 490 {
 491         return (do_isns_query(B_TRUE,
 492             lhba_handle,
 493             (uint8_t *)"",
 494             node_name,
 495             node_alias,
 496             node_type,
 497             pg_list));
 498 }
 499 
 500 /* ARGSUSED */
 501 isns_status_t
 502 isns_query_one_server(iscsi_addr_t *isns_server_addr,
 503         uint8_t *lhba_handle,
 504         uint8_t *node_name,
 505         uint8_t *node_alias,
 506         uint32_t node_type,
 507         isns_portal_group_list_t **pg_list)
 508 {
 509         return (do_isns_dev_attr_query_all_nodes(isns_server_addr,
 510             node_name,
 511             node_alias,
 512             pg_list));
 513 }
 514 
 515 isns_status_t
 516 isns_query_one_node(uint8_t *target_node_name,
 517         uint8_t *lhba_handle,
 518         uint8_t *source_node_name,
 519         uint8_t *source_node_alias,
 520         uint32_t source_node_type,
 521         isns_portal_group_list_t **pg_list)
 522 {
 523         return (do_isns_query(B_FALSE,
 524             lhba_handle,
 525             target_node_name,
 526             source_node_name,
 527             source_node_alias,
 528             source_node_type,
 529             pg_list));
 530 }
 531 
 532 /* ARGSUSED */
 533 isns_status_t
 534 isns_query_one_server_one_node(iscsi_addr_t *isns_server_addr,
 535         uint8_t *target_node_name,
 536         uint8_t *lhba_handle,
 537         uint8_t *source_node_name,
 538         uint8_t *source_node_alias,
 539         uint32_t source_node_type,
 540         isns_portal_group_list_t **pg_list) {
 541         /* Not supported yet. */
 542         *pg_list = NULL;
 543         return (isns_op_failed);
 544 }
 545 
 546 /* ARGSUSED */
 547 static
 548 int
 549 discover_isns_server(uint8_t *lhba_handle,
 550         iscsi_addr_list_t **isns_server_addrs)
 551 {
 552         entry_t e;
 553         int i;
 554         int isns_server_count = 1;
 555         int list_space;
 556         void *void_p;
 557 
 558         /*
 559          * Use supported iSNS server discovery method to find out all the
 560          * iSNS servers. For now, only static configuration method is
 561          * supported.
 562          */
 563         isns_server_count = 0;
 564         void_p = NULL;
 565         persistent_isns_addr_lock();
 566         while (persistent_isns_addr_next(&void_p, &e) == B_TRUE) {
 567                 isns_server_count++;
 568         }
 569         persistent_isns_addr_unlock();
 570 
 571         list_space = sizeof (iscsi_addr_list_t);
 572         if (isns_server_count > 0) {
 573                 list_space += (sizeof (iscsi_addr_t) * (isns_server_count - 1));
 574         }
 575         *isns_server_addrs = (iscsi_addr_list_t *)kmem_zalloc(list_space,
 576             KM_SLEEP);
 577         (*isns_server_addrs)->al_out_cnt = isns_server_count;
 578 
 579         persistent_isns_addr_lock();
 580         i = 0;
 581         void_p = NULL;
 582         while (persistent_isns_addr_next(&void_p, &e) == B_TRUE) {
 583                 iscsi_addr_t *ap;
 584 
 585                 ap = &((*isns_server_addrs)->al_addrs[i]);
 586                 ap->a_port = e.e_port;
 587                 ap->a_addr.i_insize = e.e_insize;
 588                 if (e.e_insize == sizeof (struct in_addr)) {
 589                         ap->a_addr.i_addr.in4.s_addr = (e.e_u.u_in4.s_addr);
 590                 } else if (e.e_insize == sizeof (struct in6_addr)) {
 591                         bcopy(&e.e_u.u_in6.s6_addr,
 592                             ap->a_addr.i_addr.in6.s6_addr,
 593                             sizeof (struct in6_addr));
 594                 } else {
 595                         kmem_free(*isns_server_addrs, list_space);
 596                         *isns_server_addrs = NULL;
 597                         return (ISNS_BAD_SVR_ADDR);
 598                 }
 599                 i++;
 600         }
 601         persistent_isns_addr_unlock();
 602 
 603         return (ISNS_OK);
 604 }
 605 
 606 static
 607 int
 608 create_esi_scn_thr(uint8_t *lhba_handle, iscsi_addr_t *isns_server_address)
 609 {
 610         void *listening_so  = NULL;
 611         boolean_t found     = B_FALSE;
 612 
 613         ASSERT(lhba_handle != NULL);
 614         ASSERT(isns_server_address != NULL);
 615 
 616         /*
 617          * Bringing up of the thread should happen regardless of the
 618          * subsequent registration status. That means, do not destroy the
 619          * ESI/SCN thread already created.
 620          */
 621         /* Check and create ESI/SCN thread. */
 622         mutex_enter(&esi_scn_thr_mutex);
 623 
 624         /* Determine local port and address. */
 625         found = find_local_portal(isns_server_address,
 626             NULL, &listening_so);
 627         if (found == B_FALSE) {
 628                 if (listening_so != NULL) {
 629                         iscsi_net->close(listening_so);
 630                 }
 631                 mutex_exit(&esi_scn_thr_mutex);
 632                 return (ISNS_CANNOT_FIND_LOCAL_ADDR);
 633         }
 634 
 635         if (esi_scn_thr_id == NULL) {
 636                 char thr_name[ISCSI_TH_MAX_NAME_LEN];
 637                 int rval;
 638                 isns_async_thread_arg_t *larg;
 639 
 640                 /* Assume the LHBA handle has a length of 4 */
 641                 if (snprintf(thr_name, sizeof (thr_name) - 1,
 642                     "isns_client_esi_%x%x%x%x",
 643                     lhba_handle[0],
 644                     lhba_handle[1],
 645                     lhba_handle[2],
 646                     lhba_handle[3]) >=
 647                     sizeof (thr_name)) {
 648                         esi_scn_thr_id = NULL;
 649                         if (listening_so != NULL) {
 650                                 iscsi_net->close(listening_so);
 651                                 listening_so = NULL;
 652                         }
 653                         mutex_exit(&esi_scn_thr_mutex);
 654                         return (ISNS_INTERNAL_ERR);
 655                 }
 656 
 657                 larg = kmem_zalloc(sizeof (isns_async_thread_arg_t), KM_SLEEP);
 658                 larg->lhba_handle = lhba_handle;
 659                 larg->listening_so = listening_so;
 660                 instance_listening_so = listening_so;
 661                 esi_scn_thr_to_shutdown = B_FALSE;
 662                 esi_scn_thr_id = iscsi_thread_create(NULL,
 663                     thr_name, isns_service_esi_scn, (void *)larg);
 664                 if (esi_scn_thr_id == NULL) {
 665                         if (listening_so != NULL) {
 666                                 iscsi_net->close(listening_so);
 667                                 listening_so = NULL;
 668                                 instance_listening_so = NULL;
 669                         }
 670                         mutex_exit(&esi_scn_thr_mutex);
 671                         return (ISNS_INTERNAL_ERR);
 672                 }
 673 
 674                 rval = iscsi_thread_start(esi_scn_thr_id);
 675                 if (rval == B_FALSE) {
 676                         iscsi_thread_destroy(esi_scn_thr_id);
 677                         esi_scn_thr_id = NULL;
 678                         if (listening_so != NULL) {
 679                                 iscsi_net->close(listening_so);
 680                                 listening_so = NULL;
 681                                 instance_listening_so = NULL;
 682                         }
 683                         mutex_exit(&esi_scn_thr_mutex);
 684                         return (ISNS_INTERNAL_ERR);
 685                 }
 686                 (void) iscsi_thread_send_wakeup(esi_scn_thr_id);
 687         }
 688         mutex_exit(&esi_scn_thr_mutex);
 689 
 690         return (ISNS_OK);
 691 }
 692 
 693 static
 694 void
 695 register_isns_client(void *arg)
 696 {
 697         isns_reg_arg_t *reg_args;
 698         isns_status_t status;
 699 
 700         reg_args = (isns_reg_arg_t *)arg;
 701 
 702         /* Deregister stale registration (if any). */
 703         status = do_isns_dev_dereg(reg_args->isns_server_addr,
 704             reg_args->node_name);
 705 
 706         if (status == isns_open_conn_err) {
 707                 /* Cannot open connection to the server. Stop proceeding. */
 708                 kmem_free(reg_args->isns_server_addr, sizeof (iscsi_addr_t));
 709                 reg_args->isns_server_addr = NULL;
 710                 kmem_free(reg_args->node_name, reg_args->node_name_len);
 711                 reg_args->node_name = NULL;
 712                 kmem_free(reg_args->node_alias, reg_args->node_alias_len);
 713                 reg_args->node_alias = NULL;
 714                 kmem_free(reg_args, sizeof (isns_reg_arg_t));
 715                 return;
 716         }
 717 
 718         DTRACE_PROBE1(register_isns_client_dereg, isns_status_t, status);
 719 
 720         /* New registration. */
 721         status =  do_isns_dev_attr_reg(reg_args->isns_server_addr,
 722             reg_args->node_name, reg_args->node_alias, reg_args->node_type);
 723 
 724         DTRACE_PROBE1(register_isns_client_reg, isns_status_t, status);
 725 
 726         /* Cleanup */
 727         kmem_free(reg_args->isns_server_addr, sizeof (iscsi_addr_t));
 728         reg_args->isns_server_addr = NULL;
 729         kmem_free(reg_args->node_name, reg_args->node_name_len);
 730         reg_args->node_name = NULL;
 731         kmem_free(reg_args->node_alias, reg_args->node_alias_len);
 732         reg_args->node_alias = NULL;
 733         kmem_free(reg_args, sizeof (isns_reg_arg_t));
 734 }
 735 
 736 static
 737 isns_status_t
 738 do_isns_dev_attr_reg(iscsi_addr_t *isns_server_addr,
 739         uint8_t *node_name, uint8_t *node_alias, uint32_t node_type)
 740 {
 741         int rcv_rsp_cnt = 0;
 742         int rsp_status;
 743         isns_pdu_t *in_pdu, *out_pdu;
 744         isns_status_t rval;
 745         size_t bytes_received, in_pdu_size = 0, out_pdu_size = 0;
 746         uint16_t xid;
 747         void *so = NULL;
 748 
 749         out_pdu_size = isns_create_dev_attr_reg_pdu(
 750             node_name,
 751             node_alias,
 752             node_type,
 753             &xid, &out_pdu);
 754         if (out_pdu_size == 0) {
 755                 return (isns_create_msg_err);
 756         }
 757 
 758         ASSERT(out_pdu != NULL);
 759         ASSERT(out_pdu_size > 0);
 760 
 761         so = isns_open(isns_server_addr);
 762         if (so == NULL) {
 763                 /* Log a message and return */
 764                 kmem_free(out_pdu, out_pdu_size);
 765                 out_pdu = NULL;
 766                 return (isns_open_conn_err);
 767         }
 768 
 769         if (isns_send_pdu(so, out_pdu) != 0) {
 770                 iscsi_net->close(so);
 771                 kmem_free(out_pdu, out_pdu_size);
 772                 out_pdu = NULL;
 773                 return (isns_send_msg_err);
 774         }
 775 
 776         /* Done with the out PDU - free it */
 777         kmem_free(out_pdu, out_pdu_size);
 778         out_pdu = NULL;
 779 
 780         rcv_rsp_cnt = 0;
 781         rval = isns_ok;
 782         for (;;) {
 783                 bytes_received = isns_rcv_pdu(so, &in_pdu, &in_pdu_size);
 784                 ASSERT(bytes_received >= (size_t)0);
 785                 if (bytes_received == 0) {
 786                         ASSERT(in_pdu == NULL);
 787                         ASSERT(in_pdu_size == 0);
 788                         rval = isns_rcv_msg_err;
 789                         break;
 790                 }
 791 
 792                 ASSERT(in_pdu != NULL);
 793                 ASSERT(in_pdu_size > 0);
 794 
 795                 if (ntohs(in_pdu->xid) != xid) {
 796                         rcv_rsp_cnt++;
 797                         if (rcv_rsp_cnt < MAX_RCV_RSP_COUNT) {
 798                                 continue;
 799                         } else {
 800                                 /* Exceed maximum receive count. */
 801                                 kmem_free(in_pdu, in_pdu_size);
 802                                 in_pdu = NULL;
 803                                 rval = isns_no_rsp_rcvd;
 804                                 break;
 805                         }
 806                 }
 807 
 808                 rsp_status = isns_process_dev_attr_reg_rsp(in_pdu);
 809                 if (rsp_status != ISNS_RSP_SUCCESSFUL) {
 810                         if (rsp_status == ISNS_RSP_SRC_UNAUTHORIZED) {
 811                                 rval = isns_op_partially_failed;
 812                         } else {
 813                                 rval = isns_op_failed;
 814                         }
 815                 }
 816                 kmem_free(in_pdu, in_pdu_size);
 817                 in_pdu = NULL;
 818                 break;
 819         }
 820 
 821         if (rval != isns_ok) {
 822                 iscsi_net->close(so);
 823                 return (rval);
 824         }
 825 
 826         /* Always register SCN */
 827         out_pdu_size = isns_create_scn_reg_pdu(
 828             node_name, node_alias,
 829             &xid, &out_pdu);
 830         if (out_pdu_size == 0) {
 831                 iscsi_net->close(so);
 832                 return (isns_create_msg_err);
 833         }
 834 
 835         ASSERT(out_pdu != NULL);
 836         ASSERT(out_pdu_size > 0);
 837 
 838         if (isns_send_pdu(so, out_pdu) != 0) {
 839                 iscsi_net->close(so);
 840                 kmem_free(out_pdu, out_pdu_size);
 841                 out_pdu = NULL;
 842                 return (isns_send_msg_err);
 843         }
 844 
 845         /* Done with the out PDU - free it */
 846         kmem_free(out_pdu, out_pdu_size);
 847         out_pdu = NULL;
 848 
 849         rcv_rsp_cnt = 0;
 850         for (;;) {
 851                 bytes_received = isns_rcv_pdu(so, &in_pdu, &in_pdu_size);
 852                 ASSERT(bytes_received >= (size_t)0);
 853                 if (bytes_received == 0) {
 854                         ASSERT(in_pdu == NULL);
 855                         ASSERT(in_pdu_size == 0);
 856                         rval = isns_rcv_msg_err;
 857                         break;
 858                 }
 859 
 860                 ASSERT(in_pdu != NULL);
 861                 ASSERT(in_pdu_size > 0);
 862 
 863                 if (ntohs(in_pdu->xid) != xid) {
 864                         rcv_rsp_cnt++;
 865                         if (rcv_rsp_cnt < MAX_RCV_RSP_COUNT) {
 866                                 continue;
 867                         } else {
 868                                 /* Exceed maximum receive count. */
 869                                 kmem_free(in_pdu, in_pdu_size);
 870                                 in_pdu = NULL;
 871                                 rval = isns_no_rsp_rcvd;
 872                                 break;
 873                         }
 874                 }
 875 
 876                 rsp_status = isns_process_scn_reg_rsp(in_pdu);
 877                 if (rsp_status != ISNS_RSP_SUCCESSFUL) {
 878                         rval = isns_op_failed;
 879                 }
 880                 kmem_free(in_pdu, in_pdu_size);
 881                 in_pdu = NULL;
 882                 break;
 883         }
 884 
 885         iscsi_net->close(so);
 886 
 887         return (rval);
 888 }
 889 
 890 static
 891 isns_status_t
 892 do_isns_dev_dereg(iscsi_addr_t *isns_server_addr,
 893         uint8_t *node_name)
 894 {
 895         int rcv_rsp_cnt = 0;
 896         int rsp_status;
 897         isns_pdu_t *in_pdu, *out_pdu;
 898         isns_status_t rval;
 899         size_t bytes_received, in_pdu_size = 0, out_pdu_size = 0;
 900         uint16_t xid;
 901         void *so = NULL;
 902 
 903         out_pdu_size = isns_create_dev_dereg_pdu(
 904             node_name,
 905             &xid, &out_pdu);
 906         if (out_pdu_size == 0) {
 907                 return (isns_create_msg_err);
 908         }
 909 
 910         ASSERT(out_pdu != NULL);
 911         ASSERT(out_pdu_size > 0);
 912 
 913         so = isns_open(isns_server_addr);
 914         if (so == NULL) {
 915                 /* Log a message and return */
 916                 kmem_free(out_pdu, out_pdu_size);
 917                 out_pdu = NULL;
 918                 return (isns_open_conn_err);
 919         }
 920 
 921         if (isns_send_pdu(so, out_pdu) != 0) {
 922                 iscsi_net->close(so);
 923                 kmem_free(out_pdu, out_pdu_size);
 924                 out_pdu = NULL;
 925                 return (isns_send_msg_err);
 926         }
 927 
 928         /* Done with the out PDU - free it */
 929         kmem_free(out_pdu, out_pdu_size);
 930         out_pdu = NULL;
 931 
 932         rcv_rsp_cnt = 0;
 933         rval = isns_ok;
 934         for (;;) {
 935                 bytes_received = isns_rcv_pdu(so, &in_pdu, &in_pdu_size);
 936                 ASSERT(bytes_received >= (size_t)0);
 937                 if (bytes_received == 0) {
 938                         ASSERT(in_pdu == NULL);
 939                         ASSERT(in_pdu_size == 0);
 940                         rval = isns_rcv_msg_err;
 941                         break;
 942                 }
 943 
 944                 ASSERT(in_pdu != NULL);
 945                 ASSERT(in_pdu_size > 0);
 946 
 947                 if (ntohs(in_pdu->xid) != xid) {
 948                         rcv_rsp_cnt++;
 949                         if (rcv_rsp_cnt < MAX_RCV_RSP_COUNT) {
 950                                 continue;
 951                         } else {
 952                                 /* Exceed maximum receive count. */
 953                                 kmem_free(in_pdu, in_pdu_size);
 954                                 in_pdu = NULL;
 955                                 rval = isns_no_rsp_rcvd;
 956                                 break;
 957                         }
 958                 }
 959 
 960                 rsp_status = isns_process_dev_attr_dereg_rsp(in_pdu);
 961                 if (rsp_status != ISNS_RSP_SUCCESSFUL) {
 962                         rval = isns_op_failed;
 963                 }
 964                 kmem_free(in_pdu, in_pdu_size);
 965                 in_pdu = NULL;
 966                 break;
 967         }
 968 
 969         if (rval != isns_ok) {
 970                 iscsi_net->close(so);
 971                 return (rval);
 972         }
 973 
 974         /* Always deregister SCN */
 975         out_pdu_size = isns_create_scn_dereg_pdu(
 976             node_name,
 977             &xid, &out_pdu);
 978         if (out_pdu_size == 0) {
 979                 iscsi_net->close(so);
 980                 return (isns_create_msg_err);
 981         }
 982 
 983         ASSERT(out_pdu != NULL);
 984         ASSERT(out_pdu_size > 0);
 985 
 986         if (isns_send_pdu(so, out_pdu) != 0) {
 987                 iscsi_net->close(so);
 988                 kmem_free(out_pdu, out_pdu_size);
 989                 out_pdu = NULL;
 990                 return (isns_send_msg_err);
 991         }
 992 
 993         /* Done with the out PDU - free it */
 994         kmem_free(out_pdu, out_pdu_size);
 995         out_pdu = NULL;
 996 
 997         rcv_rsp_cnt = 0;
 998         for (;;) {
 999                 bytes_received = isns_rcv_pdu(so, &in_pdu, &in_pdu_size);
1000                 ASSERT(bytes_received >= (size_t)0);
1001                 if (bytes_received == 0) {
1002                         ASSERT(in_pdu == NULL);
1003                         ASSERT(in_pdu_size == 0);
1004                         rval = isns_rcv_msg_err;
1005                         break;
1006                 }
1007 
1008                 ASSERT(in_pdu != NULL);
1009                 ASSERT(in_pdu_size > 0);
1010 
1011                 if (ntohs(in_pdu->xid) != xid) {
1012                         rcv_rsp_cnt++;
1013                         if (rcv_rsp_cnt < MAX_RCV_RSP_COUNT) {
1014                                 continue;
1015                         } else {
1016                                 /* Exceed maximum receive count. */
1017                                 kmem_free(in_pdu, in_pdu_size);
1018                                 in_pdu = NULL;
1019                                 rval = isns_no_rsp_rcvd;
1020                                 break;
1021                         }
1022                 }
1023 
1024                 rsp_status = isns_process_scn_dereg_rsp(in_pdu);
1025                 if (rsp_status != ISNS_RSP_SUCCESSFUL) {
1026                         rval = isns_op_failed;
1027                 }
1028                 kmem_free(in_pdu, in_pdu_size);
1029                 in_pdu = NULL;
1030                 break;
1031         }
1032 
1033         iscsi_net->close(so);
1034 
1035         return (rval);
1036 }
1037 
1038 static
1039 isns_status_t
1040 do_isns_query(boolean_t is_query_all_nodes_b,
1041         uint8_t *lhba_handle,
1042         uint8_t *target_node_name,
1043         uint8_t *source_node_name,
1044         uint8_t *source_node_alias,
1045         uint32_t source_node_type,
1046         isns_portal_group_list_t **pg_list)
1047 {
1048         int i, j, k;
1049         int combined_num_of_pgs, combined_pg_lst_sz,
1050             isns_svr_lst_sz,
1051             tmp_pg_list_sz,
1052             tmp_pg_lists_sz;
1053         iscsi_addr_list_t *isns_server_addr_list = NULL;
1054         isns_portal_group_t *pg;
1055         isns_portal_group_list_t *combined_pg_list,
1056             *tmp_pg_list, **tmp_pg_lists;
1057         isns_status_t qry_stat, combined_qry_stat;
1058 
1059         /* Look up the iSNS Server address(es) based on the specified ISID */
1060         if (discover_isns_server(lhba_handle, &isns_server_addr_list) !=
1061             ISNS_OK) {
1062                 *pg_list = NULL;
1063                 return (isns_no_svr_found);
1064         }
1065         if (isns_server_addr_list->al_out_cnt == 0) {
1066                 isns_svr_lst_sz = sizeof (iscsi_addr_list_t);
1067                 kmem_free(isns_server_addr_list, isns_svr_lst_sz);
1068                 isns_server_addr_list = NULL;
1069                 *pg_list = NULL;
1070                 return (isns_no_svr_found);
1071         }
1072 
1073         /*
1074          * isns_server_addr_list->al_out_cnt should not be zero by the
1075          * time it comes to this point.
1076          */
1077         tmp_pg_lists_sz = isns_server_addr_list->al_out_cnt *
1078             sizeof (isns_portal_group_list_t *);
1079         tmp_pg_lists = (isns_portal_group_list_t **)kmem_zalloc(
1080             tmp_pg_lists_sz, KM_SLEEP);
1081         combined_num_of_pgs = 0;
1082         combined_qry_stat = isns_ok;
1083         for (i = 0; i < isns_server_addr_list->al_out_cnt; i++) {
1084                 if (is_query_all_nodes_b) {
1085                         qry_stat = do_isns_dev_attr_query_all_nodes(
1086                             &isns_server_addr_list->al_addrs[i],
1087                             source_node_name,
1088                             source_node_alias,
1089                             &tmp_pg_list);
1090                 } else {
1091                         qry_stat = do_isns_dev_attr_query_one_node(
1092                             &isns_server_addr_list->al_addrs[i],
1093                             target_node_name,
1094                             source_node_name,
1095                             source_node_alias,
1096                             source_node_type,
1097                             &tmp_pg_list);
1098                 }
1099 
1100                 /* Record the portal group list retrieved from this server. */
1101                 tmp_pg_lists[i] = tmp_pg_list;
1102                 if (tmp_pg_list != NULL) {
1103                         combined_num_of_pgs += tmp_pg_list->pg_out_cnt;
1104                 }
1105 
1106                 if (qry_stat == isns_ok) {
1107                         if (combined_qry_stat != isns_ok) {
1108                                 combined_qry_stat = isns_op_partially_failed;
1109                         }
1110                 } else {
1111                         if (combined_qry_stat != isns_op_partially_failed) {
1112                                 if (combined_qry_stat == isns_ok && i > 0) {
1113                                         combined_qry_stat =
1114                                             isns_op_partially_failed;
1115                                 } else {
1116                                         combined_qry_stat = qry_stat;
1117                                 }
1118                         }
1119                 }
1120 
1121                 if (is_query_all_nodes_b == B_FALSE) {
1122                         if (qry_stat == isns_ok) {
1123                                 /*
1124                                  * Break out of the loop if we already got
1125                                  * the node information for one node.
1126                                  */
1127                                 break;
1128                         }
1129                 }
1130         }
1131 
1132         /* Merge the retrieved portal lists */
1133         combined_pg_lst_sz = sizeof (isns_portal_group_list_t);
1134         if (combined_num_of_pgs > 0) {
1135                 combined_pg_lst_sz += (combined_num_of_pgs - 1) *
1136                     sizeof (isns_portal_group_t);
1137         }
1138         combined_pg_list = (isns_portal_group_list_t *)kmem_zalloc(
1139             combined_pg_lst_sz, KM_SLEEP);
1140 
1141         combined_pg_list->pg_out_cnt = combined_num_of_pgs;
1142         k = 0;
1143         for (i = 0; i < isns_server_addr_list->al_out_cnt; i++) {
1144                 if (tmp_pg_lists[i] == NULL) {
1145                         continue;
1146                 }
1147                 for (j = 0; j < tmp_pg_lists[i]->pg_out_cnt; j++) {
1148                         pg = &(combined_pg_list->pg_list[k]);
1149                         bcopy(&(tmp_pg_lists[i]->pg_list[j]),
1150                             pg, sizeof (isns_portal_group_t));
1151                         k++;
1152                 }
1153                 tmp_pg_list_sz = sizeof (isns_portal_group_list_t);
1154                 if (tmp_pg_lists[i]->pg_out_cnt > 0) {
1155                         tmp_pg_list_sz += (tmp_pg_lists[i]->pg_out_cnt - 1) *
1156                             sizeof (isns_portal_group_t);
1157                 }
1158                 kmem_free(tmp_pg_lists[i], tmp_pg_list_sz);
1159                 tmp_pg_lists[i] = NULL;
1160         }
1161         kmem_free(tmp_pg_lists, tmp_pg_lists_sz);
1162         tmp_pg_lists = NULL;
1163 
1164         isns_svr_lst_sz = sizeof (iscsi_addr_list_t);
1165         if (isns_server_addr_list->al_out_cnt > 0) {
1166                 isns_svr_lst_sz += (sizeof (iscsi_addr_t) *
1167                     (isns_server_addr_list->al_out_cnt - 1));
1168         }
1169         kmem_free(isns_server_addr_list, isns_svr_lst_sz);
1170         isns_server_addr_list = NULL;
1171 
1172         DTRACE_PROBE1(list, isns_portal_group_list_t *, combined_pg_list);
1173 
1174         *pg_list = combined_pg_list;
1175         return (combined_qry_stat);
1176 }
1177 
1178 static
1179 isns_status_t
1180 do_isns_dev_attr_query_all_nodes(iscsi_addr_t *isns_server_addr,
1181         uint8_t *node_name,
1182         uint8_t *node_alias,
1183         isns_portal_group_list_t **pg_list)
1184 {
1185         int bytes_received;
1186         int rcv_rsp_cnt = 0;
1187         int rsp_status;
1188         uint16_t xid, seq_id = 0, func_id;
1189         isns_pdu_t *in_pdu, *out_pdu;
1190         isns_pdu_mult_payload_t *combined_pdu = NULL, *old_combined_pdu = NULL;
1191         isns_status_t qry_stat;
1192         size_t out_pdu_size = 0, in_pdu_size = 0;
1193         size_t old_combined_pdu_size = 0, combined_pdu_size = 0;
1194         void *so = NULL;
1195         uint8_t *payload_ptr;
1196 
1197         /* Initialize */
1198         *pg_list = NULL;
1199 
1200         so = isns_open(isns_server_addr);
1201         if (so == NULL) {
1202                 /* Log a message and return */
1203                 return (isns_open_conn_err);
1204         }
1205 
1206         /*
1207          * Then, ask for all PG attributes. Filter the non-target nodes.
1208          */
1209         out_pdu_size = isns_create_dev_attr_qry_target_nodes_pdu(
1210             node_name, node_alias, &xid, &out_pdu);
1211         if (out_pdu_size == 0) {
1212                 iscsi_net->close(so);
1213                 return (isns_create_msg_err);
1214         }
1215 
1216         ASSERT(out_pdu != NULL);
1217         ASSERT(out_pdu_size > 0);
1218 
1219         if (isns_send_pdu(so, out_pdu) != 0) {
1220                 iscsi_net->close(so);
1221                 kmem_free(out_pdu, out_pdu_size);
1222                 out_pdu = NULL;
1223                 return (isns_send_msg_err);
1224         }
1225 
1226         /* Done with the out PDU - free it */
1227         kmem_free(out_pdu, out_pdu_size);
1228         out_pdu = NULL;
1229 
1230         rcv_rsp_cnt = 0;
1231         qry_stat = isns_ok;
1232         for (;;) {
1233                 uint16_t flags;
1234 
1235                 bytes_received = isns_rcv_pdu(so, &in_pdu, &in_pdu_size);
1236                 ASSERT(bytes_received >= 0);
1237                 if (bytes_received == 0) {
1238                         ASSERT(in_pdu == NULL);
1239                         ASSERT(in_pdu_size == 0);
1240                         qry_stat = isns_rcv_msg_err;
1241                         break;
1242                 }
1243 
1244                 ASSERT(in_pdu != NULL);
1245                 ASSERT(in_pdu_size > 0);
1246 
1247                 /*
1248                  * make sure we are processing the right transaction id
1249                  */
1250                 if (ntohs(in_pdu->xid) != xid) {
1251                         rcv_rsp_cnt++;
1252                         if (rcv_rsp_cnt < MAX_RCV_RSP_COUNT) {
1253                                 kmem_free(in_pdu, in_pdu_size);
1254                                 in_pdu = NULL;
1255                                 continue;
1256                         } else {
1257                                 /* Exceed maximum receive count. */
1258                                 kmem_free(in_pdu, in_pdu_size);
1259                                 in_pdu = NULL;
1260                                 qry_stat = isns_no_rsp_rcvd;
1261                                 break;
1262                         }
1263                 }
1264 
1265                 /*
1266                  * check to see if FIRST and LAST PDU flag is set
1267                  * if they are both set, then this response only has one
1268                  * pdu and we can process the pdu
1269                  */
1270                 flags = in_pdu->flags;
1271                 if (((flags & ISNS_FLAG_FIRST_PDU) == ISNS_FLAG_FIRST_PDU) &&
1272                     ((flags & ISNS_FLAG_LAST_PDU) == ISNS_FLAG_LAST_PDU)) {
1273                         rsp_status =
1274                             isns_process_dev_attr_qry_target_nodes_pdu(
1275                             isns_server_addr,
1276                             in_pdu->func_id,
1277                             (isns_resp_t *)in_pdu->payload,
1278                             (size_t)in_pdu->payload_len,
1279                             pg_list);
1280                         kmem_free(in_pdu, in_pdu_size);
1281                         in_pdu = NULL;
1282                         break;
1283                 }
1284                 /*
1285                  * this pdu is part of a multi-pdu response.  save off the
1286                  * the payload of this pdu and continue processing
1287                  */
1288                 if ((flags & ISNS_FLAG_FIRST_PDU) == ISNS_FLAG_FIRST_PDU) {
1289                         /* This is the first pdu, make sure sequence ID is 0 */
1290                         if (in_pdu->seq != 0) {
1291                                 cmn_err(CE_NOTE, "isns query response invalid: "
1292                                     "first pdu is not sequence ID 0");
1293                                 kmem_free(in_pdu, in_pdu_size);
1294                                 in_pdu = NULL;
1295                                 return (isns_op_failed);
1296                         }
1297                         seq_id = 0;
1298 
1299                         /* create new pdu and copy in data from old pdu */
1300                         combined_pdu_size = ISNSP_MULT_PAYLOAD_HEADER_SIZE +
1301                             in_pdu->payload_len;
1302                         combined_pdu = (isns_pdu_mult_payload_t *)kmem_zalloc(
1303                             combined_pdu_size, KM_SLEEP);
1304                         func_id = in_pdu->func_id;
1305                         combined_pdu->payload_len = in_pdu->payload_len;
1306                         bcopy(in_pdu->payload, combined_pdu->payload,
1307                             in_pdu->payload_len);
1308 
1309                         /* done with in_pdu, free it */
1310                         kmem_free(in_pdu, in_pdu_size);
1311                         in_pdu = NULL;
1312                 } else {
1313                         seq_id++;
1314                         if (in_pdu->seq != seq_id) {
1315                                 cmn_err(CE_NOTE, "isns query response invalid: "
1316                                     "Missing sequence ID %d from isns query "
1317                                     "response.", seq_id);
1318                                 kmem_free(in_pdu, in_pdu_size);
1319                                 in_pdu = NULL;
1320                                 if (combined_pdu != NULL) {
1321                                         kmem_free(combined_pdu,
1322                                             combined_pdu_size);
1323                                         combined_pdu = NULL;
1324                                 }
1325                                 return (isns_op_failed);
1326                         }
1327                         /*
1328                          * if conbined_pdu_size is still zero, then we never
1329                          * processed the first pdu
1330                          */
1331                         if (combined_pdu_size == 0) {
1332                                 cmn_err(CE_NOTE, "isns query response invalid: "
1333                                     "Did not receive first pdu.\n");
1334                                 kmem_free(in_pdu, in_pdu_size);
1335                                 in_pdu = NULL;
1336                                 return (isns_op_failed);
1337                         }
1338                         /* save off the old combined pdu */
1339                         old_combined_pdu_size = combined_pdu_size;
1340                         old_combined_pdu = combined_pdu;
1341 
1342                         /*
1343                          * alloc a new pdu big enough to also hold the new
1344                          * pdu payload
1345                          */
1346                         combined_pdu_size += in_pdu->payload_len;
1347                         combined_pdu = (isns_pdu_mult_payload_t *)kmem_zalloc(
1348                             combined_pdu_size, KM_SLEEP);
1349 
1350                         /*
1351                          * copy the old pdu into the new allocated pdu buffer
1352                          * and append on the new pdu payload that we just
1353                          * received
1354                          */
1355                         bcopy(old_combined_pdu, combined_pdu,
1356                             old_combined_pdu_size);
1357 
1358                         payload_ptr = combined_pdu->payload +
1359                             combined_pdu->payload_len;
1360                         combined_pdu->payload_len += in_pdu->payload_len;
1361                         bcopy(in_pdu->payload, payload_ptr,
1362                             in_pdu->payload_len);
1363 
1364                         /* free in_pdu and old_combined_pdu */
1365                         kmem_free(in_pdu, in_pdu_size);
1366                         kmem_free(old_combined_pdu, old_combined_pdu_size);
1367                         in_pdu = NULL;
1368                         old_combined_pdu = NULL;
1369                 }
1370                 /*
1371                  * check to see if this is the LAST pdu.
1372                  * if it is, we can process it and move on
1373                  * otherwise continue to wait for the next pdu
1374                  */
1375                 if ((flags & ISNS_FLAG_LAST_PDU) == ISNS_FLAG_LAST_PDU) {
1376                         rsp_status =
1377                             isns_process_dev_attr_qry_target_nodes_pdu(
1378                             isns_server_addr,
1379                             func_id,
1380                             (isns_resp_t *)combined_pdu->payload,
1381                             combined_pdu->payload_len,
1382                             pg_list);
1383                         kmem_free(combined_pdu, combined_pdu_size);
1384                         combined_pdu = NULL;
1385                         break;
1386                 }
1387         }
1388         if (rsp_status != ISNS_RSP_SUCCESSFUL) {
1389                 qry_stat = isns_op_failed;
1390         }
1391 
1392         iscsi_net->close(so);
1393 
1394         return (qry_stat);
1395 }
1396 
1397 /* ARGSUSED */
1398 static
1399 isns_status_t
1400 do_isns_dev_attr_query_one_node(iscsi_addr_t *isns_server_addr,
1401         uint8_t *target_node_name,
1402         uint8_t *source_node_name,
1403         uint8_t *source_node_alias,
1404         uint32_t source_node_type,
1405         isns_portal_group_list_t **pg_list)
1406 {
1407         int bytes_received;
1408         int rcv_rsp_cnt;
1409         int rsp_status;
1410         isns_pdu_t *in_pdu, *out_pdu;
1411         isns_status_t rval;
1412         size_t out_pdu_size = 0, in_pdu_size = 0;
1413         uint16_t xid;
1414         void *so = NULL;
1415 
1416         /* Obtain the list of target type storage nodes first */
1417         out_pdu_size = isns_create_dev_attr_qry_one_pg_pdu(
1418             target_node_name, source_node_name, &xid, &out_pdu);
1419         if (out_pdu_size == 0) {
1420                 return (isns_create_msg_err);
1421         }
1422 
1423         ASSERT(out_pdu != NULL);
1424         ASSERT(out_pdu_size > 0);
1425 
1426         so = isns_open(isns_server_addr);
1427         if (so == NULL) {
1428                 /* Log a message and return */
1429                 kmem_free(out_pdu, out_pdu_size);
1430                 out_pdu = NULL;
1431                 return (isns_open_conn_err);
1432         }
1433 
1434         if (isns_send_pdu(so, out_pdu) != 0) {
1435                 iscsi_net->close(so);
1436                 kmem_free(out_pdu, out_pdu_size);
1437                 out_pdu = NULL;
1438                 return (isns_send_msg_err);
1439         }
1440 
1441         /* Done with the out PDU - free it */
1442         kmem_free(out_pdu, out_pdu_size);
1443         out_pdu = NULL;
1444 
1445         rcv_rsp_cnt = 0;
1446         rval = isns_ok;
1447         for (;;) {
1448                 bytes_received = isns_rcv_pdu(so, &in_pdu, &in_pdu_size);
1449                 ASSERT(bytes_received >= 0);
1450                 if (bytes_received == 0) {
1451                         ASSERT(in_pdu == NULL);
1452                         ASSERT(in_pdu_size == 0);
1453                         rval = isns_rcv_msg_err;
1454                         break;
1455                 }
1456 
1457                 ASSERT(in_pdu != NULL);
1458                 ASSERT(in_pdu_size > 0);
1459 
1460                 if (ntohs(in_pdu->xid) != xid) {
1461                         rcv_rsp_cnt++;
1462                         if (rcv_rsp_cnt < MAX_RCV_RSP_COUNT) {
1463                                 continue;
1464                         } else {
1465                                 /* Exceed maximum receive count. */
1466                                 kmem_free(in_pdu, in_pdu_size);
1467                                 in_pdu = NULL;
1468                                 rval = isns_no_rsp_rcvd;
1469                                 break;
1470                         }
1471                 }
1472 
1473                 rsp_status = isns_process_dev_attr_qry_target_nodes_pdu(
1474                     isns_server_addr, in_pdu->func_id,
1475                     (isns_resp_t *)in_pdu->payload, (size_t)in_pdu->payload_len,
1476                     pg_list);
1477                 if (rsp_status != ISNS_RSP_SUCCESSFUL) {
1478                         rval = isns_op_failed;
1479                 }
1480                 kmem_free(in_pdu, in_pdu_size);
1481                 in_pdu = NULL;
1482                 break;
1483         }
1484 
1485         iscsi_net->close(so);
1486 
1487         return (rval);
1488 }
1489 
1490 static
1491 void
1492 *isns_open(iscsi_addr_t *isns_server_addr)
1493 {
1494         int rval = 0;
1495         union {
1496                 struct sockaddr sin;
1497                 struct sockaddr_in s_in4;
1498                 struct sockaddr_in6 s_in6;
1499         } sa_rsvr = { 0 };
1500         void *so;
1501         struct sockaddr_in6     t_addr;
1502         socklen_t               t_addrlen;
1503 
1504         bzero(&t_addr, sizeof (struct sockaddr_in6));
1505         t_addrlen = sizeof (struct sockaddr_in6);
1506         if (isns_server_addr->a_addr.i_insize == sizeof (struct in_addr)) {
1507                 /* IPv4 */
1508                 sa_rsvr.s_in4.sin_family = AF_INET;
1509                 sa_rsvr.s_in4.sin_port = htons(isns_server_addr->a_port);
1510                 sa_rsvr.s_in4.sin_addr.s_addr =
1511                     isns_server_addr->a_addr.i_addr.in4.s_addr;
1512 
1513                 /* Create socket */
1514                 so = iscsi_net->socket(AF_INET, SOCK_STREAM, 0);
1515         } else {
1516                 /* IPv6 */
1517                 sa_rsvr.s_in6.sin6_family = AF_INET6;
1518                 bcopy(&(isns_server_addr->a_addr.i_addr.in6),
1519                     sa_rsvr.s_in6.sin6_addr.s6_addr,
1520                     sizeof (struct in6_addr));
1521                 sa_rsvr.s_in6.sin6_port = htons(isns_server_addr->a_port);
1522                 /* Create socket */
1523                 so = iscsi_net->socket(AF_INET6, SOCK_STREAM, 0);
1524         }
1525 
1526         if (so == NULL) {
1527                 return (NULL);
1528         }
1529 
1530         rval = iscsi_net->connect(so, &sa_rsvr.sin,
1531             (isns_server_addr->a_addr.i_insize == sizeof (struct in_addr)) ?
1532             sizeof (struct sockaddr_in) :
1533             sizeof (struct sockaddr_in6), 0, 0);
1534 
1535         if (rval != 0) {
1536                 /* Flag value 2 indicates both cantsend and cantrecv */
1537                 iscsi_net->shutdown(so, 2);
1538                 iscsi_net->close(so);
1539                 return (NULL);
1540         }
1541 
1542         (void) iscsi_net->getsockname(so, (struct sockaddr *)&t_addr,
1543             &t_addrlen);
1544 
1545         return (so);
1546 }
1547 
1548 static ssize_t
1549 isns_send_pdu(void *socket, isns_pdu_t *pdu)
1550 {
1551         int             iovlen = 0;
1552         iovec_t         iovec[ISNS_MAX_IOVEC];
1553         struct msghdr   msg;
1554         size_t          send_len;
1555         size_t          total_len = 0;
1556 
1557         ASSERT(iovlen < ISNS_MAX_IOVEC);
1558         iovec[iovlen].iov_base = (void *)pdu;
1559         iovec[iovlen].iov_len = (ISNSP_HEADER_SIZE);
1560         total_len += (ISNSP_HEADER_SIZE);
1561         iovlen++;
1562 
1563         ASSERT(iovlen < ISNS_MAX_IOVEC);
1564         iovec[iovlen].iov_base = (void *)pdu->payload;
1565         iovec[iovlen].iov_len = ntohs(pdu->payload_len);
1566         total_len += ntohs(pdu->payload_len);
1567         iovlen++;
1568 
1569         /* Initialization of the message header. */
1570         bzero(&msg, sizeof (msg));
1571         msg.msg_iov = &iovec[0];
1572         msg.msg_flags   = MSG_WAITALL;
1573         msg.msg_iovlen  = iovlen;
1574 
1575         send_len = iscsi_net->sendmsg(socket, &msg);
1576         return (send_len == total_len ? 0 : -1);
1577 }
1578 
1579 static
1580 size_t
1581 isns_rcv_pdu(void *socket, isns_pdu_t **pdu, size_t *pdu_size)
1582 {
1583         int poll_cnt;
1584         iovec_t iovec[ISNS_MAX_IOVEC];
1585         isns_pdu_t *tmp_pdu_hdr;
1586         size_t bytes_received, total_bytes_received = 0, payload_len = 0;
1587         struct msghdr msg;
1588         uint8_t *tmp_pdu_data;
1589 
1590         /* Receive the header first */
1591         tmp_pdu_hdr = (isns_pdu_t *)kmem_zalloc(ISNSP_HEADER_SIZE, KM_SLEEP);
1592         (void) memset((char *)&iovec[0], 0, sizeof (iovec_t));
1593         iovec[0].iov_base = (void *)tmp_pdu_hdr;
1594         iovec[0].iov_len = ISNSP_HEADER_SIZE;
1595 
1596         /* Initialization of the message header. */
1597         bzero(&msg, sizeof (msg));
1598         msg.msg_iov = &iovec[0];
1599         msg.msg_flags = MSG_WAITALL;
1600         msg.msg_iovlen = 1;
1601 
1602         /* Poll and receive the packets. */
1603         poll_cnt = 0;
1604         do {
1605                 bytes_received = iscsi_net->recvmsg(socket, &msg,
1606                     ISNS_RCV_TIMEOUT);
1607                 if (bytes_received == 0) {
1608                         /* Not yet. Increase poll count and try again. */
1609                         poll_cnt++;
1610                         continue;
1611                 } else {
1612                         /* OK data received. */
1613                         break;
1614                 }
1615         } while (poll_cnt < ISNS_RCV_RETRY_MAX);
1616 
1617         DTRACE_PROBE2(isns_rcv_pdu_hdr_summary,
1618             int, poll_cnt, int, bytes_received);
1619         if (poll_cnt >= ISNS_RCV_RETRY_MAX) {
1620                 kmem_free(tmp_pdu_hdr, ISNSP_HEADER_SIZE);
1621                 *pdu = NULL;
1622                 *pdu_size = 0;
1623                 return (0);
1624         }
1625         if (bytes_received == 0 || bytes_received != ISNSP_HEADER_SIZE) {
1626                 kmem_free(tmp_pdu_hdr, ISNSP_HEADER_SIZE);
1627                 *pdu = NULL;
1628                 *pdu_size = 0;
1629                 return (0);
1630         }
1631         total_bytes_received += bytes_received;
1632 
1633         payload_len = ntohs(tmp_pdu_hdr->payload_len);
1634         DTRACE_PROBE1(isns_rcv_pdu_probe1, int, payload_len);
1635         /* Verify the received payload len is within limit */
1636         if (payload_len > ISNSP_MAX_PAYLOAD_SIZE) {
1637                 kmem_free(tmp_pdu_hdr, ISNSP_HEADER_SIZE);
1638                 *pdu = NULL;
1639                 *pdu_size = 0;
1640                 return (0);
1641         }
1642 
1643         /* Proceed to receive additional data. */
1644         tmp_pdu_data = kmem_zalloc(payload_len, KM_SLEEP);
1645         (void) memset((char *)&iovec[0], 0, sizeof (iovec_t));
1646         iovec[0].iov_base = (void *)tmp_pdu_data;
1647         iovec[0].iov_len = payload_len;
1648 
1649         /* Initialization of the message header. */
1650         bzero(&msg, sizeof (msg));
1651         msg.msg_iov = &iovec[0];
1652         msg.msg_flags   = MSG_WAITALL;
1653         msg.msg_iovlen  = 1;
1654 
1655         /* Poll and receive the rest of the PDU. */
1656         poll_cnt = 0;
1657         do {
1658                 bytes_received = iscsi_net->recvmsg(socket, &msg,
1659                     ISNS_RCV_TIMEOUT);
1660                 if (bytes_received == 0) {
1661                         /* Not yet. Increase poll count and try again. */
1662                         poll_cnt++;
1663                         continue;
1664                 } else {
1665                         /* OK data received. */
1666                         break;
1667                 }
1668         } while (poll_cnt < ISNS_RCV_RETRY_MAX);
1669 
1670         DTRACE_PROBE2(isns_rcv_pdu_data_summary,
1671             int, poll_cnt, int, bytes_received);
1672 
1673         if (poll_cnt >= ISNS_RCV_RETRY_MAX) {
1674                 kmem_free(tmp_pdu_data, payload_len);
1675                 kmem_free(tmp_pdu_hdr, ISNSP_HEADER_SIZE);
1676                 *pdu = NULL;
1677                 *pdu_size = 0;
1678                 return (0);
1679         }
1680         if (bytes_received == 0 || bytes_received != payload_len) {
1681                 kmem_free(tmp_pdu_data, payload_len);
1682                 kmem_free(tmp_pdu_hdr, ISNSP_HEADER_SIZE);
1683                 *pdu = NULL;
1684                 *pdu_size = 0;
1685                 return (0);
1686         }
1687         total_bytes_received += bytes_received;
1688 
1689         *pdu_size = ISNSP_HEADER_SIZE + payload_len;
1690         (*pdu) = (isns_pdu_t *)kmem_zalloc((*pdu_size), KM_SLEEP);
1691         (*pdu)->version = ntohs(tmp_pdu_hdr->version);
1692         (*pdu)->func_id = ntohs(tmp_pdu_hdr->func_id);
1693         (*pdu)->payload_len = payload_len;
1694         (*pdu)->flags = ntohs(tmp_pdu_hdr->flags);
1695         (*pdu)->xid = ntohs(tmp_pdu_hdr->xid);
1696         (*pdu)->seq = ntohs(tmp_pdu_hdr->seq);
1697         bcopy(tmp_pdu_data, &((*pdu)->payload), payload_len);
1698 
1699         kmem_free(tmp_pdu_data, payload_len);
1700         tmp_pdu_data = NULL;
1701         kmem_free(tmp_pdu_hdr, ISNSP_HEADER_SIZE);
1702         tmp_pdu_hdr = NULL;
1703 
1704         return (total_bytes_received);
1705 }
1706 
1707 
1708 /*
1709  * isns_create_dev_attr_reg_pdu - isns client registration pdu
1710  */
1711 static size_t
1712 isns_create_dev_attr_reg_pdu(
1713         uint8_t *node_name,
1714         uint8_t *node_alias,
1715         uint32_t node_type,
1716         uint16_t *xid_p,
1717         isns_pdu_t **out_pdu)
1718 {
1719         in_port_t local_port;
1720         isns_pdu_t *pdu;
1721         size_t pdu_size, node_name_len, node_alias_len;
1722         uint16_t flags;
1723         boolean_t       rval        = B_FALSE;
1724         iscsi_addr_t    local_addr;
1725 
1726         ASSERT(node_name != NULL);
1727         ASSERT(node_alias != NULL);
1728 
1729         /* RFC 4171 section 6.1 - NULLs included in the length. */
1730         node_name_len = strlen((char *)node_name) + 1;
1731         node_alias_len = strlen((char *)node_alias) + 1;
1732 
1733         if (node_name_len == 1) {
1734                 *out_pdu = NULL;
1735                 return (0);
1736         }
1737 
1738         /*
1739          * Create DevAttrReg Message
1740          *
1741          * Enable the replace bit so that we can update
1742          * existing registration
1743          */
1744         flags = ISNS_FLAG_FIRST_PDU |
1745             ISNS_FLAG_LAST_PDU |
1746             ISNS_FLAG_REPLACE_REG;
1747         pdu_size = isns_create_pdu_header(ISNS_DEV_ATTR_REG, flags, &pdu);
1748         *xid_p = pdu->xid;
1749 
1750         /* Source attribute */
1751         if (isns_add_attr(pdu, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
1752             node_name_len, node_name, 0) != 0) {
1753                 kmem_free(pdu, pdu_size);
1754                 *out_pdu = NULL;
1755                 return (0);
1756         }
1757 
1758         /*
1759          * Message Key Attributes
1760          *
1761          * EID attribute - Section 6.2.1
1762          * This is required for re-registrations or Replace
1763          * Bit is ignored - Section 5.6.5.1
1764          */
1765         if (isns_add_attr(pdu, pdu_size, ISNS_EID_ATTR_ID,
1766             node_name_len, node_name, 0) != 0) {
1767                 kmem_free(pdu, pdu_size);
1768                 *out_pdu = NULL;
1769                 return (0);
1770         }
1771 
1772         /* Delimiter */
1773         if (isns_add_attr(pdu, pdu_size, ISNS_DELIMITER_ATTR_ID, 0, 0, 0)
1774             != 0) {
1775                 kmem_free(pdu, pdu_size);
1776                 *out_pdu = NULL;
1777                 return (0);
1778         }
1779 
1780         /* EID attribute - Section 6.2.1 */
1781         if (isns_add_attr(pdu, pdu_size, ISNS_EID_ATTR_ID,
1782             node_name_len, node_name, 0) != 0) {
1783                 kmem_free(pdu, pdu_size);
1784                 *out_pdu = NULL;
1785                 return (0);
1786         }
1787 
1788         /* ENTITY Protocol - Section 6.2.2 */
1789         if (isns_add_attr(pdu, pdu_size, ISNS_ENTITY_PROTOCOL_ATTR_ID, 4,
1790             0, ISNS_ENTITY_PROTOCOL_ISCSI) != 0) {
1791                 kmem_free(pdu, pdu_size);
1792                 *out_pdu = NULL;
1793                 return (0);
1794         }
1795 
1796         /* iSCSI Name - Section 6.4.1 */
1797         if (isns_add_attr(pdu, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
1798             node_name_len, node_name, 0) != 0) {
1799                 kmem_free(pdu, pdu_size);
1800                 *out_pdu = NULL;
1801                 return (0);
1802         }
1803 
1804         /* iSCSI Alias - Section 6.4.3 Optional */
1805         if (node_alias_len > 1) {
1806                 if (isns_add_attr(pdu, pdu_size, ISNS_ISCSI_ALIAS_ATTR_ID,
1807                     node_alias_len, node_alias, 0) != 0) {
1808                         kmem_free(pdu, pdu_size);
1809                         *out_pdu = NULL;
1810                         return (0);
1811                 }
1812         }
1813 
1814         /* iSCSI Node Type - Section 6.4.2 */
1815         if (isns_add_attr(pdu, pdu_size, ISNS_ISCSI_NODE_TYPE_ATTR_ID, 4,
1816             0, node_type) != 0) {
1817                 kmem_free(pdu, pdu_size);
1818                 *out_pdu = NULL;
1819                 return (0);
1820         }
1821 
1822         mutex_enter(&esi_scn_thr_mutex);
1823         if (instance_listening_so != NULL) {
1824                 rval = find_listening_addr(&local_addr, instance_listening_so);
1825                 if (rval == B_FALSE) {
1826                         kmem_free(pdu, pdu_size);
1827                         *out_pdu = NULL;
1828                         mutex_exit(&esi_scn_thr_mutex);
1829                         return (0);
1830                 }
1831         } else {
1832                 kmem_free(pdu, pdu_size);
1833                 *out_pdu = NULL;
1834                 mutex_exit(&esi_scn_thr_mutex);
1835                 return (0);
1836         }
1837         local_port = local_addr.a_port;
1838         /* Portal IP Address - Section 6.5.2 */
1839         if (isns_add_attr(pdu, pdu_size, ISNS_PORTAL_IP_ADDR_ATTR_ID, 16,
1840             &(local_addr.a_addr.i_addr.in4),
1841             local_addr.a_addr.i_insize) != 0) {
1842                 kmem_free(pdu, pdu_size);
1843                 *out_pdu = NULL;
1844                 mutex_exit(&esi_scn_thr_mutex);
1845                 return (0);
1846         }
1847         mutex_exit(&esi_scn_thr_mutex);
1848 
1849         /* Portal Port  - Section 6.5.3 */
1850         if (isns_add_attr(pdu, pdu_size, ISNS_PORTAL_PORT_ATTR_ID, 4, 0,
1851             local_port) != 0) {
1852                 kmem_free(pdu, pdu_size);
1853                 *out_pdu = NULL;
1854                 return (0);
1855         }
1856 
1857         /* SCN Port  - Section 6.3.7 */
1858         if (isns_add_attr(pdu, pdu_size, ISNS_SCN_PORT_ATTR_ID, 4, 0,
1859             local_port) != 0) {
1860                 kmem_free(pdu, pdu_size);
1861                 *out_pdu = NULL;
1862                 return (0);
1863         }
1864 
1865         /* ESI Port - Section 6.3.5 */
1866         if (isns_add_attr(pdu, pdu_size, ISNS_ESI_PORT_ATTR_ID, 4, 0,
1867             local_port) != 0) {
1868                 kmem_free(pdu, pdu_size);
1869                 *out_pdu = NULL;
1870                 return (0);
1871         }
1872 
1873         *out_pdu = pdu;
1874         return (pdu_size);
1875 }
1876 
1877 /*
1878  * isns_create_dev_dereg_pdu - Create an iSNS PDU for deregistration.
1879  */
1880 static size_t
1881 isns_create_dev_dereg_pdu(
1882         uint8_t *node_name,
1883         uint16_t *xid_p,
1884         isns_pdu_t **out_pdu)
1885 {
1886         isns_pdu_t *pdu;
1887         size_t pdu_size, node_name_len;
1888         uint16_t flags;
1889 
1890         ASSERT(node_name != NULL);
1891 
1892         /* RFC 4171 section 6.1 - NULLs included in the length. */
1893         node_name_len = strlen((char *)node_name) + 1;
1894 
1895         if (node_name_len == 1) {
1896                 *out_pdu = NULL;
1897                 return (0);
1898         }
1899 
1900         /*
1901          * Create DevDeReg Message
1902          */
1903         flags = ISNS_FLAG_FIRST_PDU |
1904             ISNS_FLAG_LAST_PDU;
1905         pdu_size = isns_create_pdu_header(ISNS_DEV_DEREG, flags, &pdu);
1906         *xid_p = pdu->xid;
1907 
1908         /* Source attribute */
1909         if (isns_add_attr(pdu, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
1910             node_name_len, node_name, 0) != 0) {
1911                 kmem_free(pdu, pdu_size);
1912                 *out_pdu = NULL;
1913                 return (0);
1914         }
1915 
1916         /* Delimiter */
1917         if (isns_add_attr(pdu, pdu_size, ISNS_DELIMITER_ATTR_ID, 0, 0, 0)
1918             != 0) {
1919                 kmem_free(pdu, pdu_size);
1920                 *out_pdu = NULL;
1921                 return (0);
1922         }
1923 
1924         /* Entity Identifier */
1925         if (isns_add_attr(pdu, pdu_size, ISNS_EID_ATTR_ID,
1926             node_name_len, node_name, 0) != 0) {
1927                 kmem_free(pdu, pdu_size);
1928                 *out_pdu = NULL;
1929                 return (0);
1930         }
1931 
1932         *out_pdu = pdu;
1933         return (pdu_size);
1934 }
1935 
1936 /*
1937  * isns_create_dev_attr_target_nodes_pdu - get all accessible targets
1938  *
1939  * Querys for a list of all accessible target nodes for this
1940  * initiator.  Requests all required login information (name,
1941  * ip, port, tpgt).
1942  */
1943 static size_t
1944 isns_create_dev_attr_qry_target_nodes_pdu(
1945         uint8_t *node_name,
1946         uint8_t *node_alias,
1947         uint16_t *xid_p, isns_pdu_t **out_pdu)
1948 {
1949         isns_pdu_t *pdu_p;
1950         uint16_t flags;
1951         size_t pdu_size, node_name_len;
1952 
1953         ASSERT(node_name != NULL);
1954         ASSERT(node_alias != NULL);
1955 
1956         /* RFC 4171 section 6.1 - NULLs included in the length. */
1957         node_name_len = strlen((char *)node_name) + 1;
1958 
1959         if (node_name_len == 1) {
1960                 *out_pdu = NULL;
1961                 return (0);
1962         }
1963 
1964         /* Create DevAttrQry Message */
1965         flags = ISNS_FLAG_FIRST_PDU |
1966             ISNS_FLAG_LAST_PDU;
1967         pdu_size = isns_create_pdu_header(ISNS_DEV_ATTR_QRY, flags, &pdu_p);
1968         *xid_p = pdu_p->xid;
1969 
1970         /* Source attribute */
1971         if (isns_add_attr(pdu_p, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
1972             node_name_len, node_name, 0) != 0) {
1973                 kmem_free(pdu_p, pdu_size);
1974                 *out_pdu = NULL;
1975                 return (0);
1976         }
1977 
1978         /*
1979          * Message Key Attribute
1980          *
1981          * iSCSI Node Type
1982          * Query target nodes only
1983          */
1984         if (isns_add_attr(pdu_p, pdu_size, ISNS_ISCSI_NODE_TYPE_ATTR_ID,
1985             4, 0, ISNS_TARGET_NODE_TYPE) != 0) {
1986                 kmem_free(pdu_p, pdu_size);
1987                 *out_pdu = NULL;
1988                 return (0);
1989         }
1990 
1991         /* Delimiter */
1992         if (isns_add_attr(pdu_p, pdu_size,
1993             ISNS_DELIMITER_ATTR_ID, 0, 0, 0) != 0) {
1994                 kmem_free(pdu_p, pdu_size);
1995                 *out_pdu = NULL;
1996                 return (0);
1997         }
1998 
1999         /* PG iSCSI Name - Zero length TLV */
2000         if (isns_add_attr(pdu_p, pdu_size, ISNS_PG_ISCSI_NAME_ATTR_ID,
2001             0, 0, 0) != 0) {
2002                 kmem_free(pdu_p, pdu_size);
2003                 *out_pdu = NULL;
2004                 return (0);
2005         }
2006 
2007         /* PG Portal IP Address - Zero length TLV */
2008         if (isns_add_attr(pdu_p, pdu_size, ISNS_PG_PORTAL_IP_ADDR_ATTR_ID,
2009             0, 0, 0) != 0) {
2010                 kmem_free(pdu_p, pdu_size);
2011                 *out_pdu = NULL;
2012                 return (0);
2013         }
2014 
2015         /* PG Portal Port - Zero length TLV */
2016         if (isns_add_attr(pdu_p, pdu_size, ISNS_PG_PORTAL_PORT_ATTR_ID,
2017             0, 0, 0) != 0) {
2018                 kmem_free(pdu_p, pdu_size);
2019                 *out_pdu = NULL;
2020                 return (0);
2021         }
2022 
2023         /* PG Portal Group Tag - Zero length TLV */
2024         if (isns_add_attr(pdu_p, pdu_size,
2025             ISNS_PG_TAG_ATTR_ID, 0, 0, 0) != 0) {
2026                 kmem_free(pdu_p, pdu_size);
2027                 *out_pdu = NULL;
2028                 return (0);
2029         }
2030 
2031         *out_pdu = pdu_p;
2032         return (pdu_size);
2033 }
2034 
2035 static
2036 size_t
2037 isns_create_dev_attr_qry_one_pg_pdu(
2038         uint8_t *target_node_name,
2039         uint8_t *source_node_name,
2040         uint16_t *xid_p,
2041         isns_pdu_t **out_pdu)
2042 {
2043         isns_pdu_t *pdu_p;
2044         uint16_t flags;
2045         size_t pdu_size, source_node_name_len, target_node_name_len;
2046 
2047         ASSERT(target_node_name != NULL);
2048         ASSERT(source_node_name != NULL);
2049 
2050         /* RFC 4171 section 6.1 - NULLs included in the length. */
2051         source_node_name_len = strlen((char *)source_node_name) + 1;
2052         target_node_name_len = strlen((char *)target_node_name) + 1;
2053 
2054         if (source_node_name_len == 1) {
2055                 *out_pdu = NULL;
2056                 return (0);
2057         }
2058 
2059         /* Create DevAttrQry message scoped to target_node_name */
2060         flags = ISNS_FLAG_FIRST_PDU |
2061             ISNS_FLAG_LAST_PDU;
2062         pdu_size = isns_create_pdu_header(ISNS_DEV_ATTR_QRY, flags, &pdu_p);
2063         *xid_p = pdu_p->xid;
2064 
2065         /* Source attribute */
2066         if (isns_add_attr(pdu_p, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
2067             source_node_name_len, source_node_name, 0) != 0) {
2068                 kmem_free(pdu_p, pdu_size);
2069                 *out_pdu = NULL;
2070                 return (0);
2071         }
2072 
2073         /* Message key attribute */
2074         /* iSCSI Node Name */
2075         if (isns_add_attr(pdu_p, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
2076             target_node_name_len,
2077             target_node_name, 0) != 0) {
2078                 kmem_free(pdu_p, pdu_size);
2079                 *out_pdu = NULL;
2080                 return (0);
2081         }
2082 
2083         /* Delimiter */
2084         if (isns_add_attr(pdu_p, pdu_size,
2085             ISNS_DELIMITER_ATTR_ID, 0, 0, 0) != 0) {
2086                 kmem_free(pdu_p, pdu_size);
2087                 *out_pdu = NULL;
2088                 return (0);
2089         }
2090 
2091         /* PG iSCSI Name - Zero length TLV */
2092         if (isns_add_attr(pdu_p, pdu_size, ISNS_PG_ISCSI_NAME_ATTR_ID,
2093             0, 0, 0) != 0) {
2094                 kmem_free(pdu_p, pdu_size);
2095                 *out_pdu = NULL;
2096                 return (0);
2097         }
2098 
2099         /* PG Portal IP Address - Zero length TLV */
2100         if (isns_add_attr(pdu_p, pdu_size, ISNS_PG_PORTAL_IP_ADDR_ATTR_ID,
2101             0, 0, 0) != 0) {
2102                 kmem_free(pdu_p, pdu_size);
2103                 *out_pdu = NULL;
2104                 return (0);
2105         }
2106 
2107         /* PG Portal Port - Zero length TLV */
2108         if (isns_add_attr(pdu_p, pdu_size, ISNS_PG_PORTAL_PORT_ATTR_ID,
2109             0, 0, 0) != 0) {
2110                 kmem_free(pdu_p, pdu_size);
2111                 *out_pdu = NULL;
2112                 return (0);
2113         }
2114 
2115         /* PG Portal Group Tag - Zero length TLV */
2116         if (isns_add_attr(pdu_p, pdu_size,
2117             ISNS_PG_TAG_ATTR_ID, 0, 0, 0) != 0) {
2118                 kmem_free(pdu_p, pdu_size);
2119                 *out_pdu = NULL;
2120                 return (0);
2121         }
2122 
2123         *out_pdu = pdu_p;
2124         return (pdu_size);
2125 }
2126 
2127 static
2128 size_t
2129 isns_create_scn_reg_pdu(
2130         uint8_t *node_name,
2131         uint8_t *node_alias,
2132         uint16_t *xid_p,
2133         isns_pdu_t **out_pdu)
2134 {
2135         isns_pdu_t *pdu;
2136         size_t pdu_size, node_name_len;
2137         uint16_t flags;
2138 
2139         ASSERT(node_name != NULL);
2140         ASSERT(node_alias != NULL);
2141 
2142         /* RFC 4171 section 6.1 - NULLs included in the length. */
2143         node_name_len = strlen((char *)node_name) + 1;
2144 
2145         if (node_name_len == 1) {
2146                 *out_pdu = NULL;
2147                 return (0);
2148         }
2149 
2150         /* Create SCNReg Message */
2151         flags = ISNS_FLAG_FIRST_PDU |
2152             ISNS_FLAG_LAST_PDU;
2153         pdu_size = isns_create_pdu_header(ISNS_SCN_REG, flags, &pdu);
2154         *xid_p = pdu->xid;
2155 
2156         /* Source attribute */
2157         if (isns_add_attr(pdu, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
2158             node_name_len, node_name, 0) != 0) {
2159                 kmem_free(pdu, pdu_size);
2160                 *out_pdu = NULL;
2161                 return (0);
2162         }
2163 
2164         /* Message attribute */
2165         if (isns_add_attr(pdu, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
2166             node_name_len, node_name, 0) != 0) {
2167                 kmem_free(pdu, pdu_size);
2168                 *out_pdu = NULL;
2169                 return (0);
2170         }
2171 
2172         /* Delimiter */
2173         if (isns_add_attr(pdu, pdu_size, ISNS_DELIMITER_ATTR_ID, 0, 0, 0)
2174             != 0) {
2175                 kmem_free(pdu, pdu_size);
2176                 *out_pdu = NULL;
2177                 return (0);
2178         }
2179 
2180         /* Operating attribute */
2181         if (isns_add_attr(pdu, pdu_size, ISNS_ISCSI_SCN_BITMAP_ATTR_ID,
2182             4,
2183             0,
2184         /*
2185          * Microsoft seems to not differentiate between init and
2186          * target. Hence, it makes no difference to turn on/off
2187          * the initiator/target bit.
2188          */
2189             ISNS_TARGET_SELF_INFO_ONLY |
2190             ISNS_OBJ_REMOVED |
2191             ISNS_OBJ_ADDED |
2192             ISNS_OBJ_UPDATED) != 0) {
2193                 kmem_free(pdu, pdu_size);
2194                 *out_pdu = NULL;
2195                 return (0);
2196         }
2197 
2198         *out_pdu = pdu;
2199         return (pdu_size);
2200 }
2201 
2202 static
2203 size_t
2204 isns_create_scn_dereg_pdu(
2205         uint8_t *node_name,
2206         uint16_t *xid_p,
2207         isns_pdu_t **out_pdu)
2208 {
2209         isns_pdu_t *pdu;
2210         size_t pdu_size, node_name_len;
2211         uint16_t flags;
2212 
2213         ASSERT(node_name != NULL);
2214 
2215         /* RFC 4171 section 6.1 - NULLs included in the length. */
2216         node_name_len = strlen((char *)node_name) + 1;
2217 
2218         if (node_name_len == 1) {
2219                 *out_pdu = NULL;
2220                 return (0);
2221         }
2222 
2223         /* Create SCNReg Message */
2224         flags = ISNS_FLAG_FIRST_PDU |
2225             ISNS_FLAG_LAST_PDU;
2226         pdu_size = isns_create_pdu_header(ISNS_SCN_DEREG, flags, &pdu);
2227         *xid_p = pdu->xid;
2228 
2229         /* Source attribute */
2230         if (isns_add_attr(pdu, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
2231             node_name_len, node_name, 0) != 0) {
2232                 kmem_free(pdu, pdu_size);
2233                 *out_pdu = NULL;
2234                 return (0);
2235         }
2236 
2237         /* Message attribute */
2238         if (isns_add_attr(pdu, pdu_size, ISNS_ISCSI_NAME_ATTR_ID,
2239             node_name_len, node_name, 0) != 0) {
2240                 kmem_free(pdu, pdu_size);
2241                 *out_pdu = NULL;
2242                 return (0);
2243         }
2244 
2245         /* Delimiter */
2246         if (isns_add_attr(pdu, pdu_size, ISNS_DELIMITER_ATTR_ID, 0, 0, 0)
2247             != 0) {
2248                 kmem_free(pdu, pdu_size);
2249                 *out_pdu = NULL;
2250                 return (0);
2251         }
2252 
2253         /* No operating attribute */
2254 
2255         *out_pdu = pdu;
2256         return (pdu_size);
2257 }
2258 
2259 static
2260 size_t
2261 isns_create_esi_rsp_pdu(uint32_t rsp_status_code,
2262         isns_pdu_t *esi_pdu,
2263         uint16_t *xid_p,
2264         isns_pdu_t **out_pdu)
2265 {
2266         isns_pdu_t *pdu_p;
2267         uint16_t flags;
2268         uint8_t *payload_ptr;
2269         uint32_t swapped_status_code = htonl(rsp_status_code);
2270         size_t pdu_size, payload_len = 0;
2271 
2272         /* Create ESIRsp Message */
2273         flags = ISNS_FLAG_FIRST_PDU |
2274             ISNS_FLAG_LAST_PDU;
2275         pdu_size = isns_create_pdu_header(ISNS_ESI_RSP, flags, &pdu_p);
2276         *xid_p = pdu_p->xid;
2277 
2278         payload_len = ntohs(pdu_p->payload_len);
2279 
2280         /* Status Code */
2281         payload_ptr = pdu_p->payload + payload_len;
2282         bcopy(&swapped_status_code, payload_ptr, 4);
2283         payload_len += 4;
2284 
2285         payload_ptr = pdu_p->payload + payload_len;
2286         if ((esi_pdu->payload_len) < ISNSP_MAX_PAYLOAD_SIZE) {
2287                 bcopy(esi_pdu->payload, payload_ptr,
2288                     (esi_pdu->payload_len));
2289                 payload_len += (esi_pdu->payload_len);
2290         } else {
2291                 bcopy(esi_pdu->payload, payload_ptr, ISNSP_MAX_PAYLOAD_SIZE);
2292                 payload_len += ISNSP_MAX_PAYLOAD_SIZE;
2293         }
2294         pdu_p->payload_len = htons(payload_len);
2295 
2296         /* Delimiter */
2297         if (isns_add_attr(pdu_p, pdu_size, ISNS_DELIMITER_ATTR_ID, 0, 0, 0)
2298             != 0) {
2299                 kmem_free(pdu_p, pdu_size);
2300                 *out_pdu = NULL;
2301                 return (0);
2302         }
2303 
2304         *out_pdu = pdu_p;
2305         return (pdu_size);
2306 }
2307 
2308 static
2309 size_t
2310 isns_create_scn_rsp_pdu(uint32_t rsp_status_code,
2311         isns_pdu_t *scn_pdu,
2312         uint16_t *xid_p,
2313         isns_pdu_t **out_pdu)
2314 {
2315         isns_pdu_t *pdu_p;
2316         uint16_t flags;
2317         uint8_t *payload_ptr;
2318         uint32_t swapped_status_code = htonl(rsp_status_code);
2319         size_t pdu_size, payload_len = 0;
2320 
2321         /* Create SCNRsp Message */
2322         flags = ISNS_FLAG_FIRST_PDU |
2323             ISNS_FLAG_LAST_PDU;
2324         pdu_size = isns_create_pdu_header(ISNS_SCN_RSP, flags, &pdu_p);
2325         *xid_p = pdu_p->xid;
2326 
2327         payload_len = ntohs(pdu_p->payload_len);
2328 
2329         /* Status Code */
2330         payload_ptr = pdu_p->payload + payload_len;
2331         bcopy(&swapped_status_code, payload_ptr, 4);
2332         payload_len += 4;
2333 
2334         payload_ptr = pdu_p->payload + payload_len;
2335         if ((scn_pdu->payload_len) < ISNSP_MAX_PAYLOAD_SIZE) {
2336                 bcopy(scn_pdu->payload, payload_ptr,
2337                     (scn_pdu->payload_len));
2338                 payload_len += (scn_pdu->payload_len);
2339         } else {
2340                 bcopy(scn_pdu->payload, payload_ptr, ISNSP_MAX_PAYLOAD_SIZE);
2341                 payload_len += ISNSP_MAX_PAYLOAD_SIZE;
2342         }
2343         pdu_p->payload_len = htons(payload_len);
2344 
2345         /* Delimiter */
2346         if (isns_add_attr(pdu_p, pdu_size, ISNS_DELIMITER_ATTR_ID, 0, 0, 0)
2347             != 0) {
2348                 kmem_free(pdu_p, pdu_size);
2349                 *out_pdu = NULL;
2350                 return (0);
2351         }
2352 
2353         *out_pdu = pdu_p;
2354         return (pdu_size);
2355 }
2356 
2357 static
2358 uint32_t
2359 isns_process_dev_attr_reg_rsp(isns_pdu_t *resp_pdu_p)
2360 {
2361         isns_resp_t *resp_p;
2362 
2363         if (resp_pdu_p->func_id != ISNS_DEV_ATTR_REG_RSP) {
2364                 /* If this happens the iSNS server may have a problem. */
2365                 return (ISNS_RSP_MSG_FORMAT_ERROR);
2366         }
2367 
2368         /* Check response's status code */
2369         resp_p = (isns_resp_t *)resp_pdu_p->payload;
2370         if (ntohl(resp_p->status) != ISNS_RSP_SUCCESSFUL) {
2371                 return (ntohl(resp_p->status));
2372         }
2373 
2374         return (ISNS_RSP_SUCCESSFUL);
2375 }
2376 
2377 static
2378 uint32_t
2379 isns_process_dev_attr_dereg_rsp(isns_pdu_t *resp_pdu_p)
2380 {
2381         isns_resp_t *resp_p;
2382 
2383         if (resp_pdu_p->func_id != ISNS_DEV_DEREG_RSP) {
2384                 /* If this happens the iSNS server may have a problem. */
2385                 return (ISNS_RSP_MSG_FORMAT_ERROR);
2386         }
2387 
2388         /* Check response's status code */
2389         resp_p = (isns_resp_t *)resp_pdu_p->payload;
2390         if (ntohl(resp_p->status) != ISNS_RSP_SUCCESSFUL) {
2391                 return (ntohl(resp_p->status));
2392         }
2393 
2394         return (ISNS_RSP_SUCCESSFUL);
2395 }
2396 
2397 static
2398 uint32_t
2399 isns_process_scn_reg_rsp(isns_pdu_t *resp_pdu_p)
2400 {
2401         isns_resp_t *resp_p;
2402 
2403         ASSERT(resp_pdu_p != NULL);
2404         if (resp_pdu_p->func_id != ISNS_SCN_REG_RSP) {
2405                 /* If this happens the iSNS server may have a problem. */
2406                 return (ISNS_RSP_MSG_FORMAT_ERROR);
2407         }
2408 
2409         /* Check response's status code */
2410         resp_p = (isns_resp_t *)resp_pdu_p->payload;
2411         if (ntohl(resp_p->status) != ISNS_RSP_SUCCESSFUL) {
2412                 return (ntohl(resp_p->status));
2413         }
2414         return (ISNS_RSP_SUCCESSFUL);
2415 }
2416 
2417 static
2418 uint32_t
2419 isns_process_scn_dereg_rsp(isns_pdu_t *resp_pdu_p)
2420 {
2421         isns_resp_t *resp_p;
2422 
2423         ASSERT(resp_pdu_p != NULL);
2424         if (resp_pdu_p->func_id != ISNS_SCN_DEREG_RSP) {
2425                 /* If this happens the iSNS server may have a problem. */
2426                 return (ISNS_RSP_MSG_FORMAT_ERROR);
2427         }
2428 
2429         /* Check response's status code */
2430         resp_p = (isns_resp_t *)resp_pdu_p->payload;
2431         if (ntohl(resp_p->status) != ISNS_RSP_SUCCESSFUL) {
2432                 return (ntohl(resp_p->status));
2433         }
2434         return (ISNS_RSP_SUCCESSFUL);
2435 }
2436 
2437 static
2438 uint32_t
2439 isns_process_dev_attr_qry_target_nodes_pdu(
2440         iscsi_addr_t *isns_server_addr, uint16_t payload_funcId,
2441         isns_resp_t *resp_p, size_t resp_len,
2442         isns_portal_group_list_t **pg_list)
2443 {
2444         boolean_t done_b, found_delimiter_b, target_node_type_b;
2445         int num_of_pgs = 0, pg_sz, idx;
2446         isns_tlv_t *attr_tlv_p;
2447         uint8_t *data_p;
2448         uint32_t len, total_payload_len = 0;
2449         isns_portal_group_t *pg;
2450         uint8_t junk[IPV4_RSVD_BYTES];
2451 
2452         *pg_list = NULL;
2453         bzero(junk, IPV4_RSVD_BYTES);
2454 
2455         if (payload_funcId != ISNS_DEV_ATTR_QRY_RSP) {
2456                 /* If this happens the iSNS server may have a problem. */
2457                 return (ISNS_RSP_MSG_FORMAT_ERROR);
2458         }
2459 
2460         if (ntohl(resp_p->status) != ISNS_RSP_SUCCESSFUL) {
2461                 return (ntohl(resp_p->status));
2462         }
2463 
2464         /*
2465          * If payload is smaller than the length of even 1 attribute
2466          * there is something wrong with the PDU.
2467          */
2468         if (resp_len < (ISNS_TLV_ATTR_ID_LEN +
2469             ISNS_TLV_ATTR_LEN_LEN)) {
2470                 return (ISNS_RSP_MSG_FORMAT_ERROR);
2471         }
2472 
2473         /*
2474          * Expected DevAttrQryRsp message format:
2475          *
2476          * Status Code
2477          * iSCSI Node Type
2478          * Delimiter
2479          * PG iSCSI Name                [Optional]
2480          * PG Portal IP Address         [Optional]
2481          * PG Portal Port               [Optional]
2482          * PG Tag                       [Optional]
2483          * PG iSCSI Name                [Optional]
2484          * PG Portal IP Address         [Optional]
2485          * PG Portal Port               [Optional]
2486          * PG Tag                       [Optional]
2487          * .
2488          * .
2489          * .
2490          */
2491         data_p = resp_p->data;
2492         done_b = B_FALSE;
2493         found_delimiter_b = B_FALSE;
2494         num_of_pgs = 0;
2495         total_payload_len = sizeof (resp_p->status);
2496         /* Find out the number of entries retrieved */
2497         while (!done_b) {
2498                 attr_tlv_p = (isns_tlv_t *)data_p;
2499                 if (ntohl(attr_tlv_p->attr_id) == ISNS_DELIMITER_ATTR_ID) {
2500                         if (found_delimiter_b) {
2501                                 done_b = B_TRUE;
2502                         } else {
2503                                 found_delimiter_b = B_TRUE;
2504                         }
2505                 } else if (ntohl(attr_tlv_p->attr_id) ==
2506                     ISNS_PG_TAG_ATTR_ID) {
2507                         if (ntohl(attr_tlv_p->attr_len) > 0) {
2508                                 /*
2509                                  * Count only those iSCSI node that have a
2510                                  * non-NULL PGT value as valid Entity.
2511                                  * Per rfc4171 section 3.4 - If the PGT value
2512                                  * registered for a specified Portal and iSCSI
2513                                  * Node is NULL, or if no PGT value is
2514                                  * registered, then the Portal does not provide
2515                                  * access to that iSCSI Node in the Entity.
2516                                  */
2517                                 num_of_pgs++;
2518                         }
2519                 }
2520                 len = ntohl(attr_tlv_p->attr_len);
2521 
2522                 total_payload_len += (ISNS_TLV_ATTR_ID_LEN +
2523                     ISNS_TLV_ATTR_LEN_LEN + len);
2524                 if (total_payload_len >= resp_len) {
2525                         done_b = B_TRUE;
2526                 } else {
2527                         data_p += (ISNS_TLV_ATTR_ID_LEN +
2528                             ISNS_TLV_ATTR_LEN_LEN + len);
2529                 }
2530         }
2531 
2532         pg_sz = sizeof (isns_portal_group_list_t);
2533         if (num_of_pgs > 0) {
2534                 pg_sz += (num_of_pgs - 1) * sizeof (isns_portal_group_t);
2535         }
2536         DTRACE_PROBE1(isns_process_dev_attr_qry_target_nodes_pdu_pg_size,
2537             int, pg_sz);
2538         /*
2539          * Once we passed this point, if for any reason we need to return
2540          * because of a failure, we need to free the memory allocated for
2541          * the pg_list and nullify it.
2542          */
2543         *pg_list = (isns_portal_group_list_t *)kmem_zalloc(pg_sz, KM_SLEEP);
2544         (*pg_list)->pg_out_cnt = 0;
2545 
2546         /* Assign the isns_server information to all portal groups */
2547         for (idx = 0; idx < num_of_pgs; idx++) {
2548                 pg = &((*pg_list)->pg_list[idx]);
2549                 bcopy(&isns_server_addr->a_addr, &pg->isns_server_ip,
2550                     sizeof (iscsi_ipaddr_t));
2551                 pg->isns_server_port = isns_server_addr->a_port;
2552         }
2553 
2554         data_p = resp_p->data;
2555         done_b = B_FALSE;
2556         found_delimiter_b = B_FALSE;
2557         total_payload_len = sizeof (resp_p->status);
2558         while (!done_b) {
2559                 attr_tlv_p = (isns_tlv_t *)data_p;
2560                 pg = &((*pg_list)->pg_list[(*pg_list)->pg_out_cnt]);
2561                 switch (ntohl(attr_tlv_p->attr_id)) {
2562                         case ISNS_DELIMITER_ATTR_ID:
2563                                 if (found_delimiter_b) {
2564                                         done_b = B_TRUE;
2565                                 } else {
2566                                         found_delimiter_b = B_TRUE;
2567                                 }
2568                                 break;
2569 
2570                         case ISNS_PG_ISCSI_NAME_ATTR_ID:
2571                                 target_node_type_b = B_TRUE;
2572                                 bcopy(attr_tlv_p->attr_value,
2573                                     (char *)pg->pg_iscsi_name,
2574                                     ntohl(attr_tlv_p->attr_len) <
2575                                     ISCSI_MAX_NAME_LEN ?
2576                                     ntohl(attr_tlv_p->attr_len) :
2577                                     ISCSI_MAX_NAME_LEN);
2578 
2579                                 DTRACE_PROBE1(isns_dev_attr_qry_process1,
2580                                     char *, (char *)pg->pg_iscsi_name);
2581                                 break;
2582 
2583                         case ISNS_PG_PORTAL_IP_ADDR_ATTR_ID:
2584                                 if (target_node_type_b) {
2585                                         /*
2586                                          * Section 6.3.1 - The Portal IP Address
2587                                          * is a 16-byte field that may contain
2588                                          * an IPv4 or IPv6 address. When this
2589                                          * field contains an IPv4 address, it
2590                                          * is stored as an IPv4-mapped IPv6
2591                                          * address
2592                                          */
2593                                         if (ntohl(attr_tlv_p->attr_len) != 16) {
2594 #define STRING_AALR "address attribute length received "
2595 #define STRING_FISE16 "from iSNS server, Expected = 16, "
2596                                                 cmn_err(CE_NOTE, "Wrong IP "
2597                                                     STRING_AALR
2598                                                     STRING_FISE16
2599                                                     "Received = %d",
2600                                                     ntohl(
2601                                                     attr_tlv_p->attr_len));
2602                                                 return (
2603                                                     ISNS_RSP_MSG_FORMAT_ERROR);
2604 #undef STRING_AALR
2605 #undef STRING_FISE16
2606                                         }
2607 
2608                                         /*
2609                                          * Section 6.3.1 and RFC 2373 state
2610                                          * that an IPv4 address will be denoted
2611                                          * by the 10 top bytes as all zero
2612                                          * followed by either 2 bytes of
2613                                          * 0x0000 or 0xFFFF The 0x0000 states
2614                                          * that the address is is IPv6 capable
2615                                          * and 0xFFFF states its not capable.
2616                                          */
2617                                         if ((bcmp(attr_tlv_p->attr_value, junk,
2618                                             IPV4_RSVD_BYTES) == 0) &&
2619                                             (((attr_tlv_p->attr_value[10] ==
2620                                             0x00) &&
2621                                             (attr_tlv_p->attr_value[11] ==
2622                                             0x00)) ||
2623                                             ((attr_tlv_p->attr_value[10] ==
2624                                             0xFF) &&
2625                                             (attr_tlv_p->attr_value[11] ==
2626                                             0xFF)))) {
2627 
2628                                                 /* IPv4 */
2629                                                 bcopy(attr_tlv_p->attr_value +
2630                                                     12, &pg->pg_ip_addr.u_ip4,
2631                                                     sizeof (struct in_addr));
2632                                                 pg->insize =
2633                                                     sizeof (struct in_addr);
2634                                         } else {
2635                                                 /* IPv6 */
2636                                                 bcopy(attr_tlv_p->attr_value,
2637                                                     &pg->pg_ip_addr.u_ip6,
2638                                                     sizeof (struct in6_addr));
2639                                                 pg->insize =
2640                                                     sizeof (struct in6_addr);
2641                                         }
2642                                 }
2643                                 break;
2644 
2645                         case ISNS_PG_PORTAL_PORT_ATTR_ID:
2646                                 if (target_node_type_b) {
2647                                         pg->pg_port =
2648                                             ntohl(*(uint32_t *)
2649                                             (*attr_tlv_p).
2650                                             attr_value);
2651                                 }
2652 
2653                                 break;
2654 
2655                         case ISNS_PG_TAG_ATTR_ID:
2656                                 if (target_node_type_b) {
2657                                         pg->pg_tag =
2658                                             ntohl(*(uint32_t *)
2659                                             (*attr_tlv_p).
2660                                             attr_value);
2661                                 }
2662                                 target_node_type_b = B_FALSE;
2663                                 if (ntohl(attr_tlv_p->attr_len) > 0) {
2664                                         /*
2665                                          * Only the iSCSI node that has a
2666                                          * non-NULL PGT value is an valid
2667                                          * Entity.
2668                                          */
2669                                         (*pg_list)->pg_out_cnt++;
2670                                 }
2671                                 break;
2672 
2673                         default:
2674                                 break;
2675                 }
2676 
2677                 len = ntohl(attr_tlv_p->attr_len);
2678                 total_payload_len += (ISNS_TLV_ATTR_ID_LEN +
2679                     ISNS_TLV_ATTR_LEN_LEN + len);
2680                 if ((total_payload_len >= resp_len) ||
2681                     ((*pg_list)->pg_out_cnt == num_of_pgs)) {
2682                         done_b = B_TRUE;
2683                 } else {
2684                         data_p += (ISNS_TLV_ATTR_ID_LEN +
2685                             ISNS_TLV_ATTR_LEN_LEN + len);
2686                 }
2687         }
2688 
2689         return (ISNS_RSP_SUCCESSFUL);
2690 }
2691 
2692 /* ARGSUSED */
2693 static
2694 uint32_t
2695 isns_process_esi(isns_pdu_t *esi_pdu_p)
2696 {
2697         /* There's nothing particular to process for ESI. */
2698         return (ISNS_RSP_SUCCESSFUL);
2699 }
2700 
2701 static
2702 uint32_t
2703 isns_process_scn(isns_pdu_t *scn_pdu_p, uint8_t *lhba_handle)
2704 {
2705         boolean_t dest_attr_found_b;
2706         boolean_t done_b;
2707         boolean_t scn_type_found_b;
2708         isns_scn_callback_arg_t *scn_args_p;
2709         isns_tlv_t *attr_tlv_p;
2710         uint8_t *data_p;
2711         uint8_t *src_attr;
2712         uint32_t attr_eff_len, normalized_attr_len;
2713         uint32_t scn_type;
2714         uint32_t total_payload_len;
2715         void (*scn_callback_to_use)(void *);
2716 
2717         /* get the lhba_handle to use for the call back */
2718         scn_callback_to_use = scn_callback_lookup(lhba_handle);
2719         if (scn_callback_to_use == NULL) {
2720                 return (ISNS_RSP_INTERNAL_ERROR);
2721         }
2722 
2723         dest_attr_found_b = B_FALSE;
2724         scn_type = 0;
2725         scn_type_found_b = B_FALSE;
2726         data_p = scn_pdu_p->payload;
2727         done_b = B_FALSE;
2728         total_payload_len = 0;
2729         src_attr = (uint8_t *)kmem_zalloc(ISCSI_MAX_NAME_LEN, KM_SLEEP);
2730         /*
2731          * Section 5.6.5.8 states an SCN can have more than one
2732          * source attribute.  Process all attributes until we
2733          * each process all the data or encounter the delimiter.
2734          */
2735         while (!done_b) {
2736                 attr_tlv_p = (isns_tlv_t *)data_p;
2737 
2738                 switch (ntohl(attr_tlv_p->attr_id)) {
2739                 /* ISNS_ISCSI_NAME_ATTR_ID - attribute name */
2740                 case ISNS_ISCSI_NAME_ATTR_ID:
2741                         attr_eff_len = strlen(
2742                             (char *)attr_tlv_p->attr_value) + 1;
2743                         /*
2744                          * The attribute length must be 4-byte aligned.
2745                          * Section 5.1.3, RFC 4171.
2746                          */
2747                         normalized_attr_len = (attr_eff_len % 4) == 0 ?
2748                             (attr_eff_len) :
2749                             (attr_eff_len + (4 - (attr_eff_len % 4)));
2750                         if (normalized_attr_len !=
2751                             ntohl(attr_tlv_p->attr_len)) {
2752                                 /* This SCN is bad. */
2753                                 kmem_free(src_attr, ISCSI_MAX_NAME_LEN);
2754                                 return (ISNS_RSP_MSG_FORMAT_ERROR);
2755                         }
2756 
2757                         /* Check if this was the Destination Attribute */
2758                         if ((dest_attr_found_b == B_TRUE) &&
2759                             (scn_type_found_b == B_TRUE)) {
2760                                 bzero(src_attr, ISCSI_MAX_NAME_LEN);
2761                                 bcopy(attr_tlv_p->attr_value,
2762                                     (char *)src_attr,
2763                                     ntohl(attr_tlv_p->attr_len) <
2764                                     ISCSI_MAX_NAME_LEN ?
2765                                     ntohl(attr_tlv_p->attr_len) :
2766                                     ISCSI_MAX_NAME_LEN);
2767 
2768                                 /* allocate new callback structure */
2769                                 scn_args_p =
2770                                     (isns_scn_callback_arg_t *)kmem_zalloc(
2771                                     sizeof (isns_scn_callback_arg_t),
2772                                     KM_SLEEP);
2773                                 scn_args_p->scn_type = ntohl(scn_type);
2774                                 bcopy(src_attr, scn_args_p->source_key_attr,
2775                                     sizeof (scn_args_p->source_key_attr));
2776 
2777                                 /* Dispatch the callback to process the SCN */
2778                                 mutex_enter(&scn_taskq_mutex);
2779                                 if (scn_taskq != NULL) {
2780                                         (void) ddi_taskq_dispatch(scn_taskq,
2781                                             scn_callback_to_use,
2782                                             scn_args_p, DDI_SLEEP);
2783                                 }
2784                                 mutex_exit(&scn_taskq_mutex);
2785                         } else {
2786                                 /* Skip Destination Attribute */
2787                                 dest_attr_found_b = B_TRUE;
2788                         }
2789                         break;
2790 
2791                 /* ISNS_ISCSI_SCN_BITMAP_ATTR_ID - change type */
2792                 case ISNS_ISCSI_SCN_BITMAP_ATTR_ID:
2793                         /*
2794                          * Determine the type of action to take for this SCN.
2795                          */
2796                         scn_type_found_b = B_TRUE;
2797                         bcopy(&(attr_tlv_p->attr_value), &scn_type, 4);
2798                         break;
2799 
2800                 /* ISNS_DELIMITER_ATTR_ID - end of the payload of a message */
2801                 case ISNS_DELIMITER_ATTR_ID:
2802                         done_b = B_TRUE;
2803                         break;
2804                 }
2805 
2806                 if (done_b == B_FALSE) {
2807                         total_payload_len += ntohl(attr_tlv_p->attr_len) +
2808                             ISNS_TLV_ATTR_ID_LEN + ISNS_TLV_ATTR_LEN_LEN;
2809                         if ((total_payload_len >= scn_pdu_p->payload_len) ||
2810                             (total_payload_len > ISNSP_MAX_PAYLOAD_SIZE)) {
2811                                 /* No more Attributes to process */
2812                                 done_b = B_TRUE;
2813                         } else {
2814                                 if (scn_pdu_p->payload_len -
2815                                     total_payload_len <=
2816                                     ISNS_TLV_ATTR_ID_LEN +
2817                                     ISNS_TLV_ATTR_LEN_LEN) {
2818                                         /*
2819                                          * The rest of the data in the PDU
2820                                          * is less than the size of a valid
2821                                          * iSNS TLV. This next attribute
2822                                          * probably spans across the PDU
2823                                          * boundary. For now, do not
2824                                          * process it further.
2825                                          */
2826                                         done_b = B_TRUE;
2827                                 } else {
2828                                         /* Advance to the next Attribute */
2829                                         data_p += (ISNS_TLV_ATTR_ID_LEN +
2830                                             ISNS_TLV_ATTR_LEN_LEN +
2831                                             ntohl(attr_tlv_p->attr_len));
2832                                 }
2833                         }
2834                 }
2835         }
2836 
2837         kmem_free(src_attr, ISCSI_MAX_NAME_LEN);
2838         return (ISNS_RSP_SUCCESSFUL);
2839 }
2840 
2841 static
2842 size_t
2843 isns_create_pdu_header(uint16_t func_id, uint16_t flags, isns_pdu_t **pdu)
2844 {
2845         /*
2846          * It should be ok to assume ISNSP_MAX_PDU_SIZE is large enough
2847          * since we are creating our own PDU which is fully under our control.
2848          */
2849         size_t pdu_size = ISNSP_MAX_PDU_SIZE;
2850 
2851         *pdu = (isns_pdu_t *)kmem_zalloc(pdu_size, KM_SLEEP);
2852         (void) memset((*pdu), 0, pdu_size);
2853         (*pdu)->version = htons((uint16_t)ISNSP_VERSION);
2854         (*pdu)->func_id = htons((uint16_t)func_id);
2855         (*pdu)->payload_len = htons(0);
2856         (*pdu)->flags = htons((uint16_t)(flags | ISNS_FLAG_CLIENT));
2857         (*pdu)->xid = htons(create_xid());
2858         (*pdu)->seq = htons(0);
2859 
2860         return (pdu_size);
2861 }
2862 
2863 static
2864 int
2865 isns_add_attr(isns_pdu_t *pdu,
2866         size_t max_pdu_size,
2867         uint32_t attr_id,
2868         uint32_t attr_len,
2869         void *attr_data,
2870         uint32_t attr_numeric_data)
2871 {
2872         isns_tlv_t *attr_tlv;
2873         uint8_t *payload_ptr;
2874         uint16_t payload_len;
2875         uint32_t normalized_attr_len;
2876         uint64_t attr_tlv_len;
2877 
2878         /* The attribute length must be 4-byte aligned. Section 5.1.3. */
2879         normalized_attr_len = (attr_len % 4) == 0 ? (attr_len) :
2880             (attr_len + (4 - (attr_len % 4)));
2881         attr_tlv_len = ISNS_TLV_ATTR_ID_LEN
2882             + ISNS_TLV_ATTR_LEN_LEN
2883             + normalized_attr_len;
2884         /* Check if we are going to exceed the maximum PDU length. */
2885         payload_len = ntohs(pdu->payload_len);
2886         if ((payload_len + attr_tlv_len) > max_pdu_size) {
2887                 return (1);
2888         }
2889 
2890         attr_tlv = (isns_tlv_t *)kmem_zalloc(attr_tlv_len, KM_SLEEP);
2891 
2892         attr_tlv->attr_id = htonl(attr_id);
2893 
2894         switch (attr_id) {
2895                 case ISNS_DELIMITER_ATTR_ID:
2896                 break;
2897 
2898                 case ISNS_PORTAL_IP_ADDR_ATTR_ID:
2899                 case ISNS_PG_PORTAL_IP_ADDR_ATTR_ID:
2900                         if (attr_numeric_data == sizeof (in_addr_t)) {
2901                                 /* IPv4 */
2902                                 attr_tlv->attr_value[10] = 0xFF;
2903                                 attr_tlv->attr_value[11] = 0xFF;
2904                                 bcopy(attr_data, ((attr_tlv->attr_value) + 12),
2905                                     sizeof (in_addr_t));
2906                         } else if (attr_numeric_data == sizeof (in6_addr_t)) {
2907                                 /* IPv6 */
2908                                 bcopy(attr_data, attr_tlv->attr_value,
2909                                     sizeof (in6_addr_t));
2910                         } else if (attr_numeric_data == 0) {
2911                                 /* EMPTY */
2912                                 /* Do nothing */
2913                         } else {
2914                                 kmem_free(attr_tlv, attr_tlv_len);
2915                                 attr_tlv = NULL;
2916                                 return (1);
2917                         }
2918                 break;
2919 
2920                 case ISNS_EID_ATTR_ID:
2921                 case ISNS_ISCSI_NAME_ATTR_ID:
2922                 case ISNS_ISCSI_ALIAS_ATTR_ID:
2923                 case ISNS_PG_ISCSI_NAME_ATTR_ID:
2924                         bcopy((char *)attr_data,
2925                             attr_tlv->attr_value,
2926                             attr_len);
2927                 break;
2928 
2929                 default:
2930                         switch (normalized_attr_len) {
2931                                 case 0:
2932                                 break;
2933 
2934                                 case 4:
2935                                         *(uint32_t *)attr_tlv->attr_value =
2936                                             htonl(attr_numeric_data);
2937                                 break;
2938 
2939                                 case 8:
2940                                         *(uint64_t *)attr_tlv->attr_value =
2941                                             BE_64((uint64_t)
2942                                             attr_numeric_data);
2943                                 break;
2944                         }
2945         }
2946 
2947         attr_tlv->attr_len = htonl(normalized_attr_len);
2948         /*
2949          * Convert the network byte ordered payload length to host byte
2950          * ordered for local address calculation.
2951          */
2952         payload_len = ntohs(pdu->payload_len);
2953         payload_ptr = pdu->payload + payload_len;
2954         bcopy(attr_tlv, payload_ptr, attr_tlv_len);
2955         payload_len += attr_tlv_len;
2956 
2957         /*
2958          * Convert the host byte ordered payload length back to network
2959          * byte ordered - it's now ready to be sent on the wire.
2960          */
2961         pdu->payload_len = htons(payload_len);
2962 
2963         kmem_free(attr_tlv, attr_tlv_len);
2964         attr_tlv = NULL;
2965 
2966         return (0);
2967 }
2968 
2969 /* ARGSUSED */
2970 static
2971 void
2972 isns_service_esi_scn(iscsi_thread_t *thread, void *arg)
2973 {
2974         int clnt_len;
2975         isns_async_thread_arg_t *larg;
2976         isns_pdu_t *in_pdu;
2977         size_t bytes_received, in_pdu_size = 0;
2978         uint8_t *lhba_handle;
2979         struct sockaddr_in6      t_addr;
2980         socklen_t               t_addrlen;
2981         union {
2982                 struct sockaddr sin;
2983                 struct sockaddr_in s_in4;
2984                 struct sockaddr_in6 s_in6;
2985         } clnt_addr = { 0 };
2986         union {
2987                 struct sockaddr_in      soa4;
2988                 struct sockaddr_in6     soa6;
2989         } local_conn_prop;
2990         void *listening_so, *connecting_so;
2991 
2992         larg = (isns_async_thread_arg_t *)arg;
2993         listening_so = larg->listening_so;
2994         lhba_handle = larg->lhba_handle;
2995 
2996         /* Done using the argument - free it */
2997         kmem_free(larg, sizeof (*larg));
2998         bzero(&t_addr, sizeof (struct sockaddr_in6));
2999         t_addrlen = sizeof (struct sockaddr_in6);
3000 
3001         (void) iscsi_net->getsockname(listening_so,
3002             (struct sockaddr *)&t_addr, &t_addrlen);
3003         if (t_addrlen <= sizeof (local_conn_prop)) {
3004                 bcopy(&t_addr, &local_conn_prop, t_addrlen);
3005         }
3006 
3007         if (iscsi_net->listen(listening_so, 5) < 0) {
3008                 iscsi_net->close(listening_so);
3009         }
3010 
3011         for (;;) {
3012                 int rval;
3013                 isns_pdu_t *out_pdu;
3014                 size_t out_pdu_size;
3015 
3016                 clnt_len = sizeof (clnt_addr);
3017 
3018                 /* Blocking call */
3019                 connecting_so = iscsi_net->accept(
3020                     listening_so, &clnt_addr.sin, &clnt_len);
3021 
3022                 mutex_enter(&esi_scn_thr_mutex);
3023                 if (esi_scn_thr_to_shutdown == B_TRUE) {
3024                         /* Terminate the thread if instructed to do so. */
3025                         mutex_exit(&esi_scn_thr_mutex);
3026                         return;
3027                 }
3028                 mutex_exit(&esi_scn_thr_mutex);
3029 
3030                 if (connecting_so == NULL) {
3031                         iscsi_net->close(listening_so);
3032                         continue;
3033                 }
3034 
3035                 bytes_received = isns_rcv_pdu(connecting_so, &in_pdu,
3036                     &in_pdu_size);
3037                 if (in_pdu == NULL) {
3038                         continue;
3039                 }
3040                 if (bytes_received == 0) {
3041                         continue;
3042                 }
3043 
3044                 switch (in_pdu->func_id) {
3045                 case ISNS_ESI:
3046                 case ISNS_SCN:
3047                         if (in_pdu->func_id == ISNS_ESI) {
3048                                 rval = isns_process_esi(in_pdu);
3049                                 out_pdu_size = isns_create_esi_rsp_pdu(
3050                                     rval,
3051                                     in_pdu,
3052                                     &xid,
3053                                     &out_pdu);
3054                         } else if (in_pdu->func_id == ISNS_SCN) {
3055                                 rval = isns_process_scn(in_pdu,
3056                                     lhba_handle);
3057                                 out_pdu_size = isns_create_scn_rsp_pdu(
3058                                     rval,
3059                                     in_pdu,
3060                                     &xid,
3061                                     &out_pdu);
3062                         } else {
3063                                 /*
3064                                  * Ignore all traffics other than
3065                                  * ESI and SCN.
3066                                  */
3067                                 kmem_free(in_pdu, in_pdu_size);
3068                                 in_pdu = NULL;
3069                                 continue;
3070                         }
3071 
3072                         if (out_pdu_size == 0) {
3073                                 kmem_free(in_pdu, in_pdu_size);
3074                                 in_pdu = NULL;
3075                                 continue;
3076                         }
3077 
3078                         (void) isns_send_pdu(connecting_so, out_pdu);
3079 
3080                         kmem_free(out_pdu, out_pdu_size);
3081                         out_pdu = NULL;
3082                         kmem_free(in_pdu, in_pdu_size);
3083                         in_pdu = NULL;
3084 
3085                         iscsi_net->close(connecting_so);
3086                         break;
3087 
3088                 default:
3089                         kmem_free(in_pdu, in_pdu_size);
3090                         in_pdu = NULL;
3091                         continue;
3092                 }
3093         }
3094 }
3095 
3096 static
3097 boolean_t
3098 find_listening_addr(iscsi_addr_t *local_addr, void *listening_so)
3099 {
3100         union {
3101                 struct sockaddr_in      soa4;
3102                 struct sockaddr_in6     soa6;
3103         } local_conn_prop = { 0 };
3104 
3105         struct sockaddr_in6     t_addr;
3106         socklen_t               t_addrlen;
3107 
3108         if (local_addr == NULL || listening_so == NULL) {
3109                 return (B_FALSE);
3110         }
3111 
3112         bzero(&t_addr, sizeof (struct sockaddr_in6));
3113         t_addrlen = sizeof (struct sockaddr_in6);
3114 
3115         (void) iscsi_net->getsockname(listening_so, (struct sockaddr *)&t_addr,
3116             &t_addrlen);
3117         if (t_addrlen > sizeof (local_conn_prop)) {
3118                 return (B_FALSE);
3119         }
3120         bcopy(&t_addr, &local_conn_prop, t_addrlen);
3121         if (local_conn_prop.soa4.sin_family == AF_INET) {
3122                 local_addr->a_addr.i_addr.in4.s_addr =
3123                     local_conn_prop.soa4.sin_addr.s_addr;
3124                 local_addr->a_addr.i_insize = sizeof (in_addr_t);
3125         } else if (local_conn_prop.soa4.sin_family == AF_INET6) {
3126                 /* Currently, IPv6 is not supported */
3127                 return (B_FALSE);
3128         } else {
3129                 return (B_FALSE);
3130         }
3131 
3132         local_addr->a_port = ntohs(local_conn_prop.soa4.sin_port);
3133 
3134         return (B_TRUE);
3135 }
3136 
3137 static
3138 boolean_t
3139 find_local_portal(iscsi_addr_t *isns_server_addr,
3140     iscsi_addr_t **local_addr, void **listening_so)
3141 {
3142         union {
3143                 struct sockaddr_in      soa4;
3144                 struct sockaddr_in6     soa6;
3145         } local_conn_prop = { 0 };
3146         union {
3147                 struct sockaddr sin;
3148                 struct sockaddr_in s_in4;
3149                 struct sockaddr_in6 s_in6;
3150         } serv_addr = { 0 };
3151         void *so;
3152         struct sockaddr_in6     t_addr;
3153         socklen_t               t_addrlen;
3154 
3155         if (listening_so == NULL) {
3156                 return (B_FALSE);
3157         }
3158 
3159         if (local_addr != NULL) {
3160                 *local_addr = NULL;
3161         }
3162 
3163         *listening_so = NULL;
3164         bzero(&t_addr, sizeof (struct sockaddr_in6));
3165         t_addrlen = sizeof (struct sockaddr_in6);
3166 
3167         /*
3168          * Determine the local IP address.
3169          */
3170         if (local_addr != NULL) {
3171                 so = isns_open(isns_server_addr);
3172                 if (so == NULL) {
3173                         return (B_FALSE);
3174                 }
3175 
3176                 iscsi_net->getsockname(so,
3177                     (struct sockaddr *)&t_addr, &t_addrlen);
3178                 if (t_addrlen > sizeof (local_conn_prop)) {
3179                         iscsi_net->close(so);
3180                         return (B_FALSE);
3181                 }
3182 
3183                 bcopy(&t_addr, &local_conn_prop, t_addrlen);
3184                 t_addrlen = sizeof (struct sockaddr_in6);
3185                 if (local_conn_prop.soa4.sin_family == AF_INET) {
3186                         *local_addr =
3187                             (iscsi_addr_t *)kmem_zalloc(sizeof (iscsi_addr_t),
3188                             KM_SLEEP);
3189                         (*local_addr)->a_addr.i_addr.in4.s_addr =
3190                             local_conn_prop.soa4.sin_addr.s_addr;
3191                         (*local_addr)->a_addr.i_insize = sizeof (in_addr_t);
3192                 } else if (local_conn_prop.soa4.sin_family == AF_INET6) {
3193                         /* Currently, IPv6 is not supported */
3194                         return (B_FALSE);
3195                 } else {
3196                         iscsi_net->close(so);
3197                         return (B_FALSE);
3198                 }
3199 
3200                 iscsi_net->close(so);
3201         }
3202         /*
3203          * Determine the local IP address. (End)
3204          */
3205 
3206         serv_addr.s_in4.sin_family = AF_INET;
3207         /*
3208          * Use INADDR_ANY to accept connections from any of the connected
3209          * networks.
3210          */
3211         serv_addr.s_in4.sin_addr.s_addr = htonl(INADDR_ANY);
3212         /*
3213          * Use port number 0 to allow the system to assign a unique unused
3214          * port.
3215          */
3216         serv_addr.s_in4.sin_port = htons(0);
3217 
3218         so = iscsi_net->socket(AF_INET, SOCK_STREAM, 0);
3219         if (so == NULL) {
3220                 if (local_addr != NULL && (*local_addr != NULL)) {
3221                         kmem_free((*local_addr), sizeof (iscsi_addr_t));
3222                         *local_addr = NULL;
3223                 }
3224                 return (B_FALSE);
3225         }
3226 
3227         if (iscsi_net->bind(so, &serv_addr.sin,
3228                 sizeof (struct sockaddr), 0, 0) < 0) {
3229                 if (local_addr != NULL && (*local_addr != NULL)) {
3230                         kmem_free((*local_addr), sizeof (iscsi_addr_t));
3231                         *local_addr = NULL;
3232                 }
3233                 iscsi_net->close(so);
3234                 return (B_FALSE);
3235         }
3236 
3237         if (local_addr != NULL && (*local_addr != NULL)) {
3238                 (void) iscsi_net->getsockname(so, (struct sockaddr *)&t_addr,
3239                     &t_addrlen);
3240                 if (t_addrlen <= sizeof (local_conn_prop)) {
3241                         bcopy(&t_addr, &local_conn_prop, t_addrlen);
3242                         (*local_addr)->a_port =
3243                             ntohs(local_conn_prop.soa4.sin_port);
3244                 } else {
3245                         (*local_addr)->a_port = ISNS_DEFAULT_ESI_SCN_PORT;
3246                 }
3247         }
3248 
3249         *listening_so = so;
3250 
3251         return (B_TRUE);
3252 }
3253 
3254 /* ARGSUSED */
3255 static
3256 void
3257 (*scn_callback_lookup(uint8_t *lhba_handle))(void *)
3258 {
3259         /*
3260          * When we support multiple HBA instance we will use lhba_handle
3261          * to look up the associated SCN callback. For now, we only support
3262          * one HBA instance therefore we always return the same SCN callback.
3263          */
3264         return (scn_callback_p);
3265 }
3266 
3267 static
3268 uint16_t
3269 create_xid()
3270 {
3271         return (xid++ % MAX_XID);
3272 }
3273 
3274 static
3275 void
3276 esi_scn_thr_cleanup()
3277 {
3278         boolean_t       unblock_esi_scn_thr_b           = B_FALSE;
3279         iscsi_addr_t    local_addr;
3280 
3281         mutex_enter(&esi_scn_thr_mutex);
3282         if (esi_scn_thr_to_shutdown == B_FALSE) {
3283 
3284                 /* Instruct the ESI/SCN to shut itself down. */
3285                 esi_scn_thr_to_shutdown = B_TRUE;
3286                 if (instance_listening_so != NULL &&
3287                     (find_listening_addr(&local_addr,
3288                     instance_listening_so) == B_TRUE)) {
3289                         isns_pdu_t *out_pdu;
3290                         size_t out_pdu_size;
3291                         void *connecting_so;
3292 
3293                         /*
3294                          * Open a connection to the local address and send
3295                          * a dummy header to unblock the accept call so that
3296                          * the ESI/SCN thread has a chance to terminate
3297                          * itself.
3298                          */
3299                         connecting_so = isns_open(&local_addr);
3300                         if (connecting_so == NULL) {
3301                                 unblock_esi_scn_thr_b = B_FALSE;
3302                                 esi_scn_thr_to_shutdown = B_FALSE;
3303                         } else {
3304                                 out_pdu_size = isns_create_pdu_header(0,
3305                                     ISNS_FLAG_FIRST_PDU |
3306                                     ISNS_FLAG_LAST_PDU,
3307                                     &out_pdu);
3308                                 if (isns_send_pdu(connecting_so,
3309                                     out_pdu) != 0) {
3310                                         unblock_esi_scn_thr_b = B_FALSE;
3311                                         esi_scn_thr_to_shutdown = B_FALSE;
3312                                 } else {
3313                                         unblock_esi_scn_thr_b = B_TRUE;
3314                                 }
3315                                 iscsi_net->close(connecting_so);
3316                                 kmem_free(out_pdu, out_pdu_size);
3317                                 out_pdu = NULL;
3318                         }
3319                 }
3320 
3321                 if (unblock_esi_scn_thr_b == B_TRUE) {
3322                         mutex_exit(&esi_scn_thr_mutex);
3323                         (void) iscsi_thread_stop(esi_scn_thr_id);
3324                         iscsi_thread_destroy(esi_scn_thr_id);
3325                         mutex_enter(&esi_scn_thr_mutex);
3326                         esi_scn_thr_id = NULL;
3327 
3328                         /*
3329                          * Shutdown and close the listening socket.
3330                          */
3331                         iscsi_net->shutdown(instance_listening_so, 2);
3332                         iscsi_net->close(instance_listening_so);
3333                         instance_listening_so = NULL;
3334                 }
3335         }
3336         mutex_exit(&esi_scn_thr_mutex);
3337 }