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