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