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
49typedef 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
59typedef 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. */
65static ddi_taskq_t *reg_query_taskq;
66static kmutex_t reg_query_taskq_mutex;
67
68/* One global queue to serve all LHBA instances. */
69static ddi_taskq_t *scn_taskq;
70static kmutex_t scn_taskq_mutex;
71
72/* One globally maintained transaction ID. */
73static 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 */
80void (*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 */
87static boolean_t esi_scn_thr_to_shutdown = B_FALSE;
88static iscsi_thread_t *esi_scn_thr_id = NULL;
89static 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 */
94static 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
102static int discover_isns_server(uint8_t *lhba_handle,
103    iscsi_addr_list_t **isns_server_addrs);
104static int create_esi_scn_thr(uint8_t *lhba_handle,
105    iscsi_addr_t *isns_server_addr);
106static void esi_scn_thr_cleanup(void);
107static void register_isns_client(void *arg);
108static 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);
110static 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 */
118static 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 */
129static 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 */
139static 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
144static void isns_service_esi_scn(iscsi_thread_t *thread, void* arg);
145static void (*scn_callback_lookup(uint8_t *lhba_handle))(void *);
146
147/* Transport related helpers */
148static void *isns_open(iscsi_addr_t *isns_server_addr);
149static ssize_t isns_send_pdu(void *socket, isns_pdu_t *pdu);
150static size_t isns_rcv_pdu(void *so, isns_pdu_t **pdu, size_t *pdu_size);
151static boolean_t find_listening_addr(iscsi_addr_t *local_addr,
152    void *listening_so);
153static 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 */
157static size_t isns_create_pdu_header(uint16_t func_id,
158    uint16_t flags, isns_pdu_t **pdu);
159static 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);
162static uint16_t create_xid(void);
163static 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);
166static size_t isns_create_dev_dereg_pdu(uint8_t *node_name,
167    uint16_t *xid_p, isns_pdu_t **out_pdu);
168static 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);
171static 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);
174static 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);
176static size_t isns_create_scn_reg_pdu(uint8_t *node_name,
177    uint8_t *node_alias, uint16_t *xid, isns_pdu_t **out_pdu);
178static size_t isns_create_scn_dereg_pdu(uint8_t *node_name,
179    uint16_t *xid_p, isns_pdu_t **out_pdu);
180static 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);
182static uint32_t isns_process_dev_attr_reg_rsp(isns_pdu_t *resp_pdu_p);
183static 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 */
190static 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);
194static uint32_t isns_process_scn_reg_rsp(isns_pdu_t *resp_pdu_p);
195static uint32_t isns_process_scn_dereg_rsp(isns_pdu_t *resp_pdu_p);
196static uint32_t isns_process_esi(isns_pdu_t *esi_pdu_p);
197static uint32_t isns_process_scn(isns_pdu_t *scn_pdu_p, uint8_t *lhba_handle);
198
199void
200isns_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
224void
225isns_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
249isns_status_t
250isns_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
333isns_status_t
334isns_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
393isns_status_t
394isns_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
447isns_status_t
448isns_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
484isns_status_t
485isns_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 */
501isns_status_t
502isns_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
515isns_status_t
516isns_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 */
533isns_status_t
534isns_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 */
547static
548int
549discover_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
606static
607int
608create_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
693static
694void
695register_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
736static
737isns_status_t
738do_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
890static
891isns_status_t
892do_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
1038static
1039isns_status_t
1040do_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
1178static
1179isns_status_t
1180do_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 */
1398static
1399isns_status_t
1400do_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
1490static
1491void
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
1548static ssize_t
1549isns_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
1579static
1580size_t
1581isns_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 */
1711static size_t
1712isns_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 */
1880static size_t
1881isns_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 */
1943static size_t
1944isns_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
2035static
2036size_t
2037isns_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
2127static
2128size_t
2129isns_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
2202static
2203size_t
2204isns_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
2259static
2260size_t
2261isns_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
2308static
2309size_t
2310isns_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
2357static
2358uint32_t
2359isns_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
2377static
2378uint32_t
2379isns_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
2397static
2398uint32_t
2399isns_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
2417static
2418uint32_t
2419isns_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
2437static
2438uint32_t
2439isns_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 */
2693static
2694uint32_t
2695isns_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
2701static
2702uint32_t
2703isns_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
2841static
2842size_t
2843isns_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
2863static
2864int
2865isns_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 */
2970static
2971void
2972isns_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
3096static
3097boolean_t
3098find_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
3137static
3138boolean_t
3139find_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 */
3255static
3256void
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
3267static
3268uint16_t
3269create_xid()
3270{
3271	return (xid++ % MAX_XID);
3272}
3273
3274static
3275void
3276esi_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}
3338