Home | History | Annotate | Line # | Download | only in libisns
      1 /*	$NetBSD: isns_pdu.c,v 1.5 2021/08/21 23:00:30 andvar Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2004,2009 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Wasabi Systems, Inc.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 /*
     33  * isns_pdu.c
     34  */
     35 
     36 
     37 #include <sys/cdefs.h>
     38 __RCSID("$NetBSD: isns_pdu.c,v 1.5 2021/08/21 23:00:30 andvar Exp $");
     39 
     40 
     41 #include <sys/types.h>
     42 #include <sys/param.h>
     43 
     44 #include <assert.h>
     45 #include <errno.h>
     46 #include <string.h>
     47 
     48 #include "isns.h"
     49 #include "isns_config.h"
     50 
     51 
     52 /*
     53  * Local function prototypes.
     54  */
     55 static struct isns_buffer_list_s *isns_lookup_buffer_list(int);
     56 
     57 static struct isns_pdu_s *isns_init_pdu(struct isns_buffer_s *,
     58     struct isns_config_s *, uint16_t, uint16_t, uint16_t);
     59 static int isns_add_pdu_payload_data(struct isns_trans_s *, const void *, int);
     60 static void isns_get_tlv_info_advance(struct isns_get_tlv_info_s *);
     61 static int isns_get_tlv_uint32(struct isns_get_tlv_info_s *, uint32_t *);
     62 static int isns_get_tlv_data(struct isns_get_tlv_info_s *, int, void **);
     63 
     64 static void isns_add_pdu_list(struct isns_pdu_s **, struct isns_pdu_s *);
     65 static struct isns_buffer_s *isns_get_pdu_head_buffer(struct isns_pdu_s *);
     66 #if 0
     67 static struct isns_buffer_s *isns_get_pdu_tail_buffer(struct isns_pdu_s *);
     68 #endif
     69 static struct isns_buffer_s *isns_get_pdu_active_buffer(struct isns_pdu_s *);
     70 
     71 static uint32_t isns_get_next_trans_id(void);
     72 
     73 /*
     74  * Buffer pool structures and global (static) var.
     75  */
     76 struct isns_buffer_list_s {
     77 	int buf_size;
     78 	int alloc_count;
     79 	struct isns_buffer_s *head;
     80 	struct isns_buffer_list_s *next;
     81 };
     82 
     83 struct isns_buffer_pool_s {
     84 	int active;
     85 	struct isns_buffer_list_s *list_p;
     86 	pthread_mutex_t mutex;
     87 };
     88 
     89 static struct isns_buffer_pool_s G_buffer_pool;
     90 
     91 
     92 /*
     93  * isns_init_buffer_pool - initialize buffer pool for use
     94  */
     95 void
     96 isns_init_buffer_pool(void)
     97 {
     98 	pthread_mutexattr_t mutexattr;
     99 
    100 	DBG("isns_init_buffer_pool: entered\n");
    101 
    102 	assert(!G_buffer_pool.active);
    103 
    104 	pthread_mutexattr_init(&mutexattr);
    105 	pthread_mutexattr_settype(&mutexattr, ISNS_MUTEX_TYPE_NORMAL);
    106 	pthread_mutex_init(&G_buffer_pool.mutex, &mutexattr);
    107 
    108 	G_buffer_pool.active = 1;
    109 }
    110 
    111 
    112 /*
    113  * isns_lookup_buffer_list - locates the pool buffer list for the buf_size
    114  *			     specified.
    115  *
    116  * Returns: pointer to list in pool containing buf_size buffers
    117  *	    NULL if no list for size indicated exists
    118  */
    119 static struct isns_buffer_list_s *
    120 isns_lookup_buffer_list(int buf_size)
    121 {
    122 	struct isns_buffer_list_s *list_p;
    123 
    124 	/*
    125 	 * WARNING: G_buffer_pool.mutex MUST already be locked.
    126 	 */
    127 
    128 	list_p = G_buffer_pool.list_p;
    129 	while (list_p != NULL) {
    130 		if (list_p->buf_size == buf_size)
    131 			break;
    132 		list_p = list_p->next;
    133 	}
    134 
    135 	return list_p;
    136 }
    137 
    138 
    139 /*
    140  * isns_add_buffer_pool - allocates buffers of in pool
    141  *
    142  * If a list containing buf_size buffers already exists in pool, additional
    143  * buffers are added (allocated) to the list.
    144  */
    145 int
    146 isns_add_buffer_pool(int buf_size, int count)
    147 {
    148 	struct isns_buffer_list_s *list_p, *p, *p_next;
    149 	struct isns_buffer_s *buf_p;
    150 	int n;
    151 
    152 	DBG("isns_add_buffer_pool: buf_size=%d, count=%d\n", buf_size, count);
    153 
    154 	assert(G_buffer_pool.active);
    155 
    156 	/* Make our buffer lengths always a multiple of 4. */
    157 	buf_size = (buf_size + 0x03) & ~0x03;
    158 
    159 	/*
    160 	 * Lookup existing list for size specified.  If no exists, allocate
    161 	 * a new list and initialize.
    162 	 */
    163 	pthread_mutex_lock(&G_buffer_pool.mutex);
    164 	list_p = isns_lookup_buffer_list(buf_size);
    165 	if (list_p == NULL) {
    166 		pthread_mutex_unlock(&G_buffer_pool.mutex);
    167 		list_p = (struct isns_buffer_list_s *)
    168 		    isns_malloc(sizeof(struct isns_buffer_list_s));
    169 		if (list_p == NULL) {
    170 			DBG("isns_add_buffer_pool: error on isns_malloc()\n");
    171 			return ENOMEM;
    172 		}
    173 		list_p->buf_size = buf_size;
    174 		list_p->alloc_count = 0;
    175 		list_p->head = NULL;
    176 	}
    177 
    178 	/* If this is a new list, insert into pool in buf_size order. */
    179 	if (list_p->alloc_count == 0) {
    180 		pthread_mutex_lock(&G_buffer_pool.mutex);
    181 		if (G_buffer_pool.list_p == NULL) {
    182 			G_buffer_pool.list_p = list_p;
    183 			list_p->next = NULL;
    184 		} else if (G_buffer_pool.list_p->buf_size > buf_size) {
    185 			list_p->next = G_buffer_pool.list_p;
    186 			G_buffer_pool.list_p = list_p;
    187 		} else {
    188 			p = G_buffer_pool.list_p;
    189 			while (p->next != NULL) {
    190 				p_next = p->next;
    191 				if (p_next->buf_size > buf_size) {
    192 					p->next = list_p;
    193 					list_p->next = p_next;
    194 					break;
    195 				}
    196 				p = p->next;
    197 			}
    198 			if (p->next == NULL) {
    199 				p->next = list_p;
    200 				list_p->next = NULL;
    201 			}
    202 		}
    203 	}
    204 
    205 	/* Allocate (possibly additional) buffers for list. */
    206 	for (n = 0; n < count; n++) {
    207 		buf_p = (struct isns_buffer_s *)
    208 		    isns_malloc(buf_size + sizeof(struct isns_buffer_s));
    209 		if (buf_p == NULL)
    210 			break;
    211 		buf_p->next = list_p->head;
    212 		list_p->head = buf_p;
    213 	}
    214 	list_p->alloc_count += n;
    215 	pthread_mutex_unlock(&G_buffer_pool.mutex);
    216 
    217 	DBG("isns_init_buffer_pool: %d %d-byte buffers allocated\n",
    218 	    n, buf_size);
    219 
    220 	return (n > 0 ? 0 : ENOMEM);
    221 }
    222 
    223 
    224 /*
    225  * isns_destroy_buffer_pool - destroys previously allocated buffer pool
    226  */
    227 void
    228 isns_destroy_buffer_pool(void)
    229 {
    230 	struct isns_buffer_list_s *list_p;
    231 	struct isns_buffer_s *buf_p;
    232 #ifdef ISNS_DEBUG
    233 	char dbg_buffer[1024] = { 0 };
    234 #endif
    235 
    236 	DBG("isns_destroy_buffer_pool: entered\n");
    237 
    238 	assert(G_buffer_pool.active);
    239 
    240 	pthread_mutex_lock(&G_buffer_pool.mutex);
    241 	while (G_buffer_pool.list_p != NULL) {
    242 		list_p = G_buffer_pool.list_p;
    243 		while (list_p->head != NULL) {
    244 			buf_p = list_p->head;
    245 			list_p->head = buf_p->next;
    246 			list_p->alloc_count--;
    247 			isns_free(buf_p);
    248 		}
    249 #ifdef ISNS_DEBUG
    250 		if (list_p->alloc_count > 0) {
    251 			snprintf(&dbg_buffer[(int) strlen(dbg_buffer)],
    252 			    (sizeof(dbg_buffer) - strlen(dbg_buffer)),
    253 			    "isns_destroy_buffer_pool: "
    254 			    "%d %d-byte buffer(s) not freed\n",
    255 			    list_p->alloc_count, list_p->buf_size);
    256 		}
    257 #endif
    258 		G_buffer_pool.list_p = list_p->next;
    259 		isns_free(list_p);
    260 	}
    261 	G_buffer_pool.active = 0;
    262 
    263 	pthread_mutex_unlock(&G_buffer_pool.mutex);
    264 	pthread_mutex_destroy(&G_buffer_pool.mutex);
    265 
    266 	DBG(dbg_buffer);
    267 }
    268 
    269 
    270 /*
    271  * isns_new_buffer - allocates a new ISNS buffer
    272  *
    273  * Typically, the buffer is returned from the pool, but if no free buffers
    274  * are available in the pool, or a buf size larger than the largest pool buffer
    275  * size is requested, a normal malloc is used to allocate the buffer.  The
    276  * buffer type is recorded so that a subsequent isns_free_buffer will correctly
    277  * free the buffer or return it to the pool.
    278  */
    279 struct isns_buffer_s *
    280 isns_new_buffer(int buf_size)
    281 {
    282 	struct isns_buffer_list_s *list_p;
    283 	struct isns_buffer_s *buf_p;
    284 	int buf_type;
    285 
    286 	if (buf_size == 0)
    287 		buf_size = ISNS_BUF_SIZE;
    288 	buf_p = NULL;
    289 
    290 	pthread_mutex_lock(&G_buffer_pool.mutex);
    291 	list_p = G_buffer_pool.list_p;
    292 	while (list_p != NULL) {
    293 		if ((list_p->head != NULL)
    294 		    && (list_p->buf_size >= buf_size)) {
    295 			buf_p = list_p->head;
    296 			list_p->head = buf_p->next;
    297 			buf_size = list_p->buf_size;
    298 			buf_type = ISNS_BUF_POOL;
    299 			break;
    300 		}
    301 		list_p = list_p->next;
    302 	}
    303 	pthread_mutex_unlock(&G_buffer_pool.mutex);
    304 
    305 	if (buf_p == NULL) {
    306 		buf_p = (struct isns_buffer_s *)isns_malloc(
    307 		    buf_size + sizeof(struct isns_buffer_s));
    308 		buf_type = ISNS_BUF_MALLOC;
    309 	}
    310 
    311 	if (buf_p != NULL)
    312 		ISNS_INIT_BUFFER(buf_p, buf_size, buf_type);
    313 
    314 	DBG("isns_new_buffer: %p (buf_size=%d, type=%d)\n", buf_p, buf_size,
    315 	    buf_type);
    316 
    317 	return buf_p;
    318 }
    319 
    320 
    321 /*
    322  * isns_free_buffer - free a ISNS buffer
    323  */
    324 void
    325 isns_free_buffer(struct isns_buffer_s *buf_p)
    326 {
    327 	struct isns_buffer_list_s *list_p;
    328 
    329 	DBG("isns_free_buffer: %p (type=%d, alloc_len=%d)\n",
    330 	    buf_p, (buf_p == NULL ? 0 : buf_p->buf_type),
    331 	    (buf_p == NULL ? 0 : buf_p->alloc_len));
    332 
    333 	if (buf_p != NULL) {
    334 		switch (buf_p->buf_type) {
    335 		case ISNS_BUF_POOL:
    336 			/* Return buffer to proper pool list. */
    337 			pthread_mutex_lock(&G_buffer_pool.mutex);
    338 			list_p = isns_lookup_buffer_list((int)buf_p->alloc_len);
    339 			if (list_p != NULL) {
    340 				buf_p->next = list_p->head;
    341 				list_p->head = buf_p;
    342 			}
    343 			pthread_mutex_unlock(&G_buffer_pool.mutex);
    344 			break;
    345 		case ISNS_BUF_MALLOC:
    346 			/* Malloc allocated buf, so free normally. */
    347 			isns_free(buf_p);
    348 			break;
    349 		case ISNS_BUF_STATIC:
    350 			/* Static buf with no allocation, so do nothing here. */
    351 			break;
    352 		}
    353 	}
    354 }
    355 
    356 
    357 /*
    358  * isns_new_trans - create a new ISNS transaction
    359  */
    360 ISNS_TRANS
    361 isns_new_trans(ISNS_HANDLE isns_handle, uint16_t func_id, uint16_t pdu_flags)
    362 {
    363 	struct isns_trans_s *trans_p;
    364 	struct isns_pdu_s *pdu_p;
    365 	struct isns_buffer_s *buf_p;
    366 
    367 	if (isns_handle == ISNS_INVALID_HANDLE) {
    368 		DBG("isns_new_trans: error - handle=%p\n", isns_handle);
    369 		return ISNS_INVALID_TRANS;
    370 	}
    371 
    372 	buf_p = isns_new_buffer((int)sizeof(struct isns_trans_s));
    373 	if (buf_p == NULL) {
    374 		DBG("isns_new_trans: error on isns_new_buffer()\n");
    375 		return ISNS_INVALID_TRANS;
    376 	}
    377 
    378 	trans_p = (struct isns_trans_s *)isns_buffer_data(buf_p, 0);
    379 	trans_p->id = isns_get_next_trans_id();
    380 	trans_p->func_id = func_id;
    381 	trans_p->flags = 0;
    382 	trans_p->cfg_p = (struct isns_config_s *)isns_handle;
    383 	trans_p->pdu_req_list = NULL;
    384 	trans_p->pdu_rsp_list = NULL;
    385 	trans_p->disconnect_cnt = 0;
    386 
    387 	trans_p->get_tlv_info.pdu_p = NULL;
    388 	trans_p->get_tlv_info.buf_p = NULL;
    389 	trans_p->get_tlv_info.extra_buf_list = NULL;
    390 	trans_p->get_tlv_info.buf_ofs = 0;
    391 
    392 	buf_p->cur_len = sizeof(struct isns_trans_s);
    393 
    394 	/*
    395 	 * Mask off all but the AUTH and possibly REPLACE_REG pdu flags.  Then,
    396 	 * set the appropriate server/client sender flag.  The first/last PDU
    397 	 * flags will be set when the PDU is sent.
    398 	 */
    399 	if (func_id == isnsp_DevAttrReg)
    400 		pdu_flags &= (ISNS_FLAG_AUTH | ISNS_FLAG_REPLACE_REG);
    401 	else
    402 		pdu_flags &= ISNS_FLAG_AUTH;
    403 
    404 	if (trans_p->cfg_p->is_server)
    405 		pdu_flags |= ISNS_FLAG_SND_SERVER;
    406 	else
    407 		pdu_flags |= ISNS_FLAG_SND_CLIENT;
    408 
    409 	pdu_p = isns_new_pdu(trans_p->cfg_p, trans_p->id, func_id, pdu_flags);
    410 	if (pdu_p == NULL) {
    411 		DBG("isns_new_trans: error on isns_new_pdu()\n");
    412 		isns_free_buffer(buf_p);
    413 		return ISNS_INVALID_TRANS;
    414 	}
    415 
    416 	isns_add_pdu_request((ISNS_TRANS)trans_p, pdu_p);
    417 
    418 	DBG("isns_new_trans: %p\n", trans_p);
    419 
    420 	return (ISNS_TRANS)trans_p;
    421 }
    422 
    423 
    424 /*
    425  * isns_free_trans - free ISNS transaction created with isns_new_trans
    426  */
    427 void
    428 isns_free_trans(ISNS_TRANS trans)
    429 {
    430 	struct isns_trans_s *trans_p;
    431 	struct isns_pdu_s *pdu_p;
    432 	struct isns_buffer_s *buf_p, *free_buf_p;
    433 	uint32_t trans_flags;
    434 
    435 	DBG("isns_free_trans: %p\n", trans);
    436 
    437 	if (trans != ISNS_INVALID_TRANS) {
    438 		trans_p = (struct isns_trans_s *)trans;
    439 
    440 		trans_flags = isns_set_trans_flags(trans_p,
    441 		    ISNS_TRANSF_FREE_WHEN_COMPLETE);
    442 
    443 		if ((trans_flags & ISNS_TRANSF_COMPLETE) == 0) {
    444 			DBG("isns_free_trans: deferred - trans not complete\n");
    445 			return;
    446 		}
    447 
    448 		DBG("isns_free_trans: pdu_req_list=%p\n",
    449 		    trans_p->pdu_req_list);
    450 		while ((pdu_p = trans_p->pdu_req_list) != NULL) {
    451 			trans_p->pdu_req_list = pdu_p->next;
    452 			isns_free_pdu(pdu_p);
    453 		}
    454 		DBG("isns_free_trans: pdu_rsp_list=%p\n",
    455 		    trans_p->pdu_rsp_list);
    456 		while ((pdu_p = trans_p->pdu_rsp_list) != NULL) {
    457 			trans_p->pdu_rsp_list = pdu_p->next;
    458 			isns_free_pdu(pdu_p);
    459 		}
    460 		DBG("isns_free_trans: extra_buf_list=%p\n",
    461 		    trans_p->get_tlv_info.extra_buf_list);
    462 		buf_p = trans_p->get_tlv_info.extra_buf_list;
    463 		while (buf_p != NULL) {
    464 			free_buf_p = buf_p;
    465 			buf_p = buf_p->next;
    466 			isns_free_buffer(free_buf_p);
    467 		}
    468 
    469 		DBG("isns_free_trans: freeing base trans buffer\n");
    470 		buf_p = ((struct isns_buffer_s *)(void *)(trans_p))-1;
    471 		isns_free_buffer(buf_p);
    472 	}
    473 }
    474 
    475 
    476 /*
    477  * isns_send_trans - send ISNS transaction PDU(s) and optionally wait
    478  *
    479  * If a successful wait occurs (i.e., the transaction completes without
    480  * a timeout), then the response PDU status is place in *status_p.  For
    481  * all other cases, the data returned in *status_p is undefined.
    482  *
    483  */
    484 int
    485 isns_send_trans(ISNS_TRANS trans, const struct timespec *timeout_p,
    486     uint32_t *status_p)
    487 {
    488 	struct isns_trans_s *trans_p;
    489 	struct isns_pdu_s *pdu_p;
    490 	int rval;
    491 
    492 	trans_p = (struct isns_trans_s *)trans;
    493 
    494 	DBG("isns_send_trans: trans_p=%p, timeout_p=%p\n", trans_p, timeout_p);
    495 
    496 	if (status_p != NULL)
    497 		*status_p = 0;
    498 
    499 	if (!isns_is_socket_init_done(trans_p->cfg_p)) {
    500 		DBG("isns_send_trans: socket not initialized\n");
    501 		isns_complete_trans(trans_p);
    502 		return EINVAL;
    503 	}
    504 
    505 	if ((pdu_p = isns_get_pdu_request(trans)) == NULL) {
    506 		DBG("isns_send_trans: no request PDU\n");
    507 		return EINVAL;
    508 	}
    509 
    510 	/* Set the FIRST_PDU flag in the first PDU. */
    511 	pdu_p->hdr.flags |= ISNS_FLAG_FIRST_PDU;
    512 
    513 	/* Set our PDU sequence numbers for the PDU chain. */
    514 	while (pdu_p->next != NULL) {
    515 		pdu_p->next->hdr.seq_id = pdu_p->hdr.seq_id + 1;
    516 		pdu_p = pdu_p->next;
    517 	}
    518 
    519 	/* Set the LAST_PDU flag in the last PDU. */
    520 	pdu_p->hdr.flags |= ISNS_FLAG_LAST_PDU;
    521 
    522 	rval = isns_send_pdu(trans, isns_get_pdu_request(trans), timeout_p);
    523 	if ((rval == 0) && (status_p != NULL))
    524 		isns_get_pdu_response_status(trans, status_p);
    525 
    526 	return rval;
    527 }
    528 
    529 
    530 
    531 void
    532 isns_complete_trans(struct isns_trans_s *trans_p)
    533 {
    534 	uint32_t flags;
    535 
    536 	DBG("isns_complete_trans: trans_p=%p\n", trans_p);
    537 
    538 	flags = isns_set_trans_flags(trans_p, ISNS_TRANSF_COMPLETE);
    539 
    540 	if ((flags & ISNS_TRANSF_FREE_WHEN_COMPLETE) != 0)
    541 		isns_free_trans(trans_p);
    542 }
    543 
    544 
    545 int
    546 isns_abort_trans(struct isns_config_s *cfg_p, uint16_t trans_id)
    547 {
    548 	struct isns_task_s *task_p;
    549 
    550 	/* First, look at current task. */
    551 	if (((task_p = cfg_p->curtask_p) != NULL)
    552 	    && (task_p->task_type == ISNS_TASK_SEND_PDU)
    553 	    && (task_p->var.send_pdu.trans_p->id == trans_id)) {
    554 		isns_complete_trans(task_p->var.send_pdu.trans_p);
    555 		isns_end_task(task_p);
    556 		return 0;
    557 	}
    558 
    559 	/* If not current task, look in task queue. */
    560 	task_p = isns_taskq_remove_trans(cfg_p, trans_id);
    561 	if (task_p) {
    562 		isns_complete_trans(task_p->var.send_pdu.trans_p);
    563 		isns_end_task(task_p);
    564 		return 0;
    565 	}
    566 
    567 	return EINVAL;
    568 }
    569 
    570 /*
    571  * isns_add_string - add a TLV which is a C string
    572  *
    573  * Wrapper around isns_add_tlv()
    574  */
    575 int
    576 isns_add_string(ISNS_TRANS trans, uint32_t tag, const char *s)
    577 {
    578 	/* Add string, including required NULL. */
    579 	return isns_add_tlv(trans, tag, (uint32_t)strlen(s) + 1, s);
    580 }
    581 
    582 
    583 /*
    584  * isns_add_tlv - adds a TLV to an existing transaction
    585  */
    586 int
    587 isns_add_tlv(ISNS_TRANS trans, uint32_t tag, uint32_t data_len,
    588     const void *data_p)
    589 {
    590 	struct isns_trans_s *trans_p;
    591 	uint8_t tlv_buf[ISNS_TLV_HDR_SIZE];
    592 	int rval;
    593 
    594 	DBG("isns_add_tlv: trans=%p, tag=%d, data_len=%d, data_p=%p\n",
    595 	    trans, tag, data_len, data_p);
    596 
    597 	if (trans == ISNS_INVALID_TRANS) {
    598 		DBG("isns_add_tlv: error - trans=%p\n", trans);
    599 		return EINVAL;
    600 	}
    601 
    602 	if ((data_len > 0) && (data_p == NULL)) {
    603 		DBG("isns_add_tlv: error data_len=%d, data_p=%p\n",
    604 		    data_len, data_p);
    605 		return EINVAL;
    606 	}
    607 
    608 	/* Set tag, length in header buffer and add to PDU payload data. */
    609 	trans_p = (struct isns_trans_s *)trans;
    610 	ISNS_TLV_SET_TAG(tlv_buf, tag);
    611 	ISNS_TLV_SET_LEN(tlv_buf, ISNS_PAD4_LEN(data_len));
    612 	rval = isns_add_pdu_payload_data(trans_p, tlv_buf, ISNS_TLV_HDR_SIZE);
    613 
    614 	/* If header added okay, add value portion to PDU payload data. */
    615 	if ((rval == 0) && (data_len > 0))
    616 		rval = isns_add_pdu_payload_data(trans_p, data_p, data_len);
    617 
    618 	return rval;
    619 }
    620 
    621 
    622 /*
    623  * isns_get_tlv - get TLV value from response PDU for transaction
    624  *
    625  * returns:
    626  *   0 - success
    627  *   ENOENT - no (more) TLVs in this transaction
    628  *   EINVAL - invalid arg
    629  *   EPERM  - operation not permitted - transaction not complete
    630  *   ENOMEM - could not allocate storage for spanning TLV data
    631  */
    632 int
    633 isns_get_tlv(ISNS_TRANS trans, int which_tlv, uint32_t *tag_p,
    634     uint32_t *data_len_p, void **data_pp)
    635 {
    636 	struct isns_trans_s *trans_p;
    637 	struct isns_get_tlv_info_s *info_p;
    638 	struct isns_pdu_s *pdu_p;
    639 	int rval;
    640 
    641 	if (trans == ISNS_INVALID_TRANS) {
    642 		DBG("isns_get_tlv: error - trans=%p\n", trans);
    643 		return EINVAL;
    644 	}
    645 
    646 	trans_p = (struct isns_trans_s *)trans;
    647 	if ((isns_get_trans_flags(trans_p) & ISNS_TRANSF_COMPLETE) == 0) {
    648 		DBG("isns_get_tlv: error - trans not complete\n");
    649 		return EPERM;
    650 	}
    651 
    652 	/* Get response PDU for this transaction. */
    653 	pdu_p = isns_get_pdu_response(trans_p);
    654 	if (pdu_p == NULL) {
    655 		DBG("isns_get_tlv: error - no response PDU in transaction\n");
    656 		return EINVAL;
    657 	}
    658 
    659 	if (pdu_p->payload_p->cur_len == 0) {
    660 		DBG("isns_get_tlv: error - zero length PDU payload\n");
    661 		return EINVAL;
    662 	}
    663 
    664 	/* If get_tlv_info unset, treat ISNS_TLV_NEXT as ISNS_TLV_FIRST. */
    665 	info_p = &trans_p->get_tlv_info;
    666 	if ((which_tlv == ISNS_TLV_NEXT) && (info_p->pdu_p == NULL))
    667 		which_tlv = ISNS_TLV_FIRST;
    668 
    669 	/*!!! make sure PDU uses TLVs here */
    670 
    671 	switch (which_tlv) {
    672 	case ISNS_TLV_NEXT:
    673 		/* For next TLV, nothing to do here - use get_tlv_info as-is. */
    674 		break;
    675 
    676 	case ISNS_TLV_FIRST:
    677 		/* For first TLV, reset get_tlv_info. */
    678 		info_p->pdu_p = pdu_p;
    679 		info_p->buf_p = isns_get_pdu_head_buffer(pdu_p);
    680 		info_p->buf_ofs = 4;
    681 		break;
    682 
    683 	default:
    684 		DBG("isns_get_tlv: invalid arg (which_tlv=%d)\n", which_tlv);
    685 		return EINVAL;
    686 	}
    687 
    688 	/*
    689 	 * Get the type, length, and data (value) for the TLV.  The get calls
    690 	 * below will advance the pointers in get_tlv_info_s *info_p.  Note that
    691 	 * if the get of the TAG type fails, ENOENT is returned indicating that
    692 	 * no more TLVs exist for this request PDU.
    693 	 */
    694 	if ((rval = isns_get_tlv_uint32(info_p, tag_p)) != 0) {
    695 		DBG("isns_get_tlv: error on isns_get_tlv_uint32() tag\n");
    696 		return ENOENT;
    697 	}
    698 
    699 	if ((rval = isns_get_tlv_uint32(info_p, (uint32_t *)data_len_p)) != 0) {
    700 		DBG("isns_get_tlv: error on isns_get_tlv_uint32() data len\n");
    701 		return rval;
    702 	}
    703 
    704 	rval = isns_get_tlv_data(info_p, *data_len_p, data_pp);
    705 	if (rval != 0) {
    706 		DBG("isns_get_tlv: error on isns_get_tlv_data()\n");
    707 		return rval;
    708 	}
    709 
    710 	return 0;
    711 }
    712 
    713 
    714 /*
    715  * isns_set_trans_flags - sets flag bit(s) in transaction flags member
    716  */
    717 uint32_t
    718 isns_set_trans_flags(struct isns_trans_s *trans_p, uint32_t flags)
    719 {
    720 	pthread_mutex_lock(&trans_p->cfg_p->trans_mutex);
    721 	trans_p->flags |= flags;
    722 	flags = trans_p->flags;
    723 	pthread_mutex_unlock(&trans_p->cfg_p->trans_mutex);
    724 
    725 	return flags;
    726 }
    727 
    728 
    729 /*
    730  * isns_add_pdu_request - adds PDU to transaction request PDU list
    731  */
    732 void
    733 isns_add_pdu_request(struct isns_trans_s *trans_p, struct isns_pdu_s *pdu_p)
    734 {
    735 	DBG("isns_add_pdu_request: trans_p=%p, pdu_p=%p\n", trans_p, pdu_p);
    736 
    737 	isns_add_pdu_list(&trans_p->pdu_req_list, pdu_p);
    738 }
    739 
    740 
    741 /*
    742  * isns_add_pdu_response - adds PDU to transaction response PDU list
    743  */
    744 void
    745 isns_add_pdu_response(struct isns_trans_s *trans_p, struct isns_pdu_s *pdu_p)
    746 {
    747 	DBG("isns_add_pdu_response: trans_p=%p, pdu_p=%p\n", trans_p, pdu_p);
    748 
    749 	isns_add_pdu_list(&trans_p->pdu_rsp_list, pdu_p);
    750 }
    751 
    752 
    753 /*
    754  * isns_get_pdu_request_tail - returns last PDU in request PDU chain
    755  */
    756 struct isns_pdu_s *
    757 isns_get_pdu_request_tail(struct isns_trans_s *trans_p)
    758 {
    759 	struct isns_pdu_s *pdu_p;
    760 
    761 	if ((pdu_p = isns_get_pdu_request(trans_p)) != NULL) {
    762 		while (pdu_p->next != NULL)
    763 			pdu_p = pdu_p->next;
    764 	}
    765 
    766 	return pdu_p;
    767 }
    768 
    769 
    770 /*
    771  * isns_new_pdu - allocates a new PDU and assigns function ID and flags
    772  */
    773 struct isns_pdu_s *
    774 isns_new_pdu(struct isns_config_s *cfg_p, uint16_t trans_id, uint16_t func_id,
    775     uint16_t flags)
    776 {
    777 	struct isns_buffer_s *buf_p;
    778 
    779 	/*
    780 	 * Allocate a buffer at least large enough for our isns_pdu_s struct
    781 	 * and the embedded isns_buffer_s struct for the PDU payload data and 4
    782 	 * bytes of actual payload data.
    783 	 */
    784 	buf_p = isns_new_buffer(/* CONSTCOND */(int)MAX(ISNS_BUF_SIZE,
    785 	    sizeof(struct isns_pdu_s) + sizeof(struct isns_buffer_s) + 4));
    786 	if (buf_p == NULL) {
    787 		DBG("isns_new_pdu: error on isns_new_buffer()\n");
    788 		return NULL;
    789 	}
    790 
    791 	return isns_init_pdu(buf_p, cfg_p, trans_id, func_id, flags);
    792 }
    793 
    794 
    795 /*
    796  * isns_free_pdu - frees a PDU and all associated buffers
    797  */
    798 void
    799 isns_free_pdu(struct isns_pdu_s *pdu_p)
    800 {
    801 	struct isns_buffer_s *buf_p, *free_buf_p;
    802 
    803 	DBG("isns_free_pdu: %p\n", pdu_p);
    804 
    805 	if (pdu_p != NULL) {
    806 		/* Free all payload buffers. */
    807 		buf_p = pdu_p->payload_p;
    808 		while (buf_p != NULL) {
    809 			free_buf_p = buf_p;
    810 			buf_p = buf_p->next;
    811 			isns_free_buffer(free_buf_p);
    812 		}
    813 		/*
    814 		 * Get a pointer to the ISNS buffer in which this PDU is
    815 		 * contained, and then free it.
    816 		 */
    817 		buf_p = ((struct isns_buffer_s *)(void *)(pdu_p))-1 ;
    818 		isns_free_buffer(buf_p);
    819 	}
    820 }
    821 
    822 
    823 /*
    824  * isns_send_pdu - initiates the send PDU task
    825  */
    826 int
    827 isns_send_pdu(ISNS_TRANS trans, struct isns_pdu_s *pdu_p,
    828     const struct timespec *timeout_p)
    829 {
    830 	struct isns_trans_s *trans_p;
    831 	struct isns_task_s* task_p;
    832 	int rval;
    833 
    834 	if (trans == ISNS_INVALID_TRANS) {
    835 		DBG("isns_send_pdu: error - trans=%p\n", trans);
    836 		return EINVAL;
    837 	}
    838 
    839 	if (pdu_p == NULL) {
    840 		DBG("isns_send_pdu: error - pdu_p=%p\n", pdu_p);
    841 		return EINVAL;
    842 	}
    843 
    844 	trans_p = (struct isns_trans_s *)trans;
    845 
    846 	/* Build SEND_PDU task, insert on queue and issue command. */
    847 	task_p = isns_new_task(pdu_p->cfg_p, ISNS_TASK_SEND_PDU,
    848 	    (timeout_p != NULL));
    849 	task_p->var.send_pdu.trans_p = trans_p;
    850 	task_p->var.send_pdu.pdu_p = pdu_p;
    851 
    852 	isns_taskq_insert_tail(pdu_p->cfg_p, task_p);
    853 
    854 	isns_issue_cmd(pdu_p->cfg_p, ISNS_CMD_PROCESS_TASKQ);
    855 
    856 	if (timeout_p == NULL)
    857 		rval = 0;
    858 	else {
    859 		rval = isns_wait_task(task_p, timeout_p);
    860 		if (rval == ETIMEDOUT) {
    861 			DBG("isns_send_pdu: "
    862 			     "timeout on isns_wait_task() trans_id=%d\n",
    863 			     trans_p->id);
    864 
    865 			isns_issue_cmd_with_data(task_p->cfg_p,
    866 			    ISNS_CMD_ABORT_TRANS, (void *)&trans_p->id,
    867 			    (int)sizeof(trans_p->id));
    868 		}
    869 	}
    870 
    871 	return rval;
    872 }
    873 
    874 
    875 /*
    876  * isns_init_pdu - initialize ISNS buffer to be a PDU
    877  */
    878 static struct isns_pdu_s *
    879 isns_init_pdu(struct isns_buffer_s *buf_p, struct isns_config_s *cfg_p,
    880     uint16_t trans_id, uint16_t func_id, uint16_t flags)
    881 {
    882 	struct isns_pdu_s *pdu_p;
    883 
    884 	/* The config and buffer pointers must be valid here. */
    885 	assert(cfg_p != NULL);
    886 	assert(buf_p != NULL);
    887 
    888 	/* The PDU starts at offset 0 for the ISNS buffer data. */
    889 	pdu_p = isns_buffer_data(buf_p, 0);
    890 	buf_p->cur_len = sizeof(struct isns_pdu_s);
    891 
    892 	/* Assign PDU members. */
    893 	pdu_p->cfg_p = cfg_p;
    894 	pdu_p->hdr.isnsp_version = ISNSP_VERSION;
    895 	pdu_p->hdr.func_id = func_id;
    896 	pdu_p->hdr.payload_len = 0;
    897 	pdu_p->hdr.flags = flags;
    898 	pdu_p->hdr.trans_id = trans_id;
    899 	pdu_p->hdr.seq_id = 0;
    900 	pdu_p->byteorder_host = 1;
    901 	pdu_p->next = NULL;
    902 
    903 	/*
    904 	 * The PDU payload buffer starts after the PDU struct portion in the
    905 	 * ISNS buffer passed in to this function.  So, assign the payload_p
    906 	 * pointer accordingly, and then init the buffer with the proper length
    907 	 * and ISNS_BUF_STATIC type.
    908 	 */
    909 	pdu_p->payload_p = (struct isns_buffer_s *)
    910 	    isns_buffer_data(buf_p, buf_p->cur_len);
    911 	ISNS_INIT_BUFFER(pdu_p->payload_p, (unsigned)(buf_p->alloc_len -
    912 	    sizeof(struct isns_pdu_s) - sizeof(struct isns_buffer_s)),
    913 	    ISNS_BUF_STATIC);
    914 
    915 	DBG("isns_init_pdu: %p\n", pdu_p);
    916 
    917 	return pdu_p;
    918 }
    919 
    920 
    921 /*
    922  * isns_add_pdu_payload_data - add data to PDU payload
    923  */
    924 static int
    925 isns_add_pdu_payload_data(struct isns_trans_s *trans_p, const void *data_p,
    926     int len)
    927 {
    928 	struct isns_pdu_s *pdu_p, *new_pdu_p;
    929 	struct isns_buffer_s *buf_p, *new_buf_p;
    930 	const uint8_t *src_p;
    931 	uint8_t *dst_p;
    932 	int pad_bytes;
    933 
    934 	/* Get the request PDU for this transaction. */
    935 	if ((pdu_p = isns_get_pdu_request_tail(trans_p)) == NULL) {
    936 		DBG("isns_add_pdu_payload_data: no request PDU\n");
    937 		return EINVAL;
    938 	}
    939 	/* Get the active buffer for this PDU (where data should be copied). */
    940 	buf_p = isns_get_pdu_active_buffer(pdu_p);
    941 
    942 	/* Set up source and destination pointers.  Calculate pad bytes. */
    943 	src_p = data_p;
    944 	dst_p = isns_buffer_data(buf_p, buf_p->cur_len);
    945 	pad_bytes = ISNS_PAD4_BYTES(len);
    946 
    947 	/*
    948 	 * Move data from source to PDU buffer(s), allocated new pdus and
    949 	 * buffers as necessary to accommodate the data.
    950 	 */
    951 	while (len--) {
    952 		/* If at max for one PDU payload, add a new PDU. */
    953 		if (pdu_p->hdr.payload_len == ISNS_MAX_PDU_PAYLOAD) {
    954 			new_pdu_p = isns_new_pdu(trans_p->cfg_p,
    955 			    trans_p->id, trans_p->func_id, pdu_p->hdr.flags);
    956 			if (new_pdu_p == NULL)
    957 				return ENOMEM;
    958 			isns_add_pdu_request(trans_p, new_pdu_p);
    959 			pdu_p = new_pdu_p;
    960 			buf_p = pdu_p->payload_p;
    961 			dst_p = isns_buffer_data(buf_p, 0);
    962 		}
    963 		/* If at end of current buffer, add a new buffer. */
    964 		if (buf_p->cur_len == buf_p->alloc_len) {
    965 			if (buf_p->next != NULL)
    966 				buf_p = buf_p->next;
    967 			else {
    968 				new_buf_p = isns_new_buffer(0);
    969 				if (new_buf_p == NULL)
    970 					return ENOMEM;
    971 				buf_p->next = new_buf_p;
    972 				buf_p = new_buf_p;
    973 			}
    974 			dst_p = isns_buffer_data(buf_p, 0);
    975 		}
    976 		pdu_p->hdr.payload_len++;
    977 		buf_p->cur_len++;
    978 		*dst_p++ = *src_p++;
    979 	}
    980 
    981 	/*
    982 	 * Since the buffer alloc length is always a multiple of 4, we are
    983 	 * guaranteed to have enough room for the pad bytes.
    984 	 */
    985 	if (pad_bytes > 0) {
    986 		pdu_p->hdr.payload_len += pad_bytes;
    987 		buf_p->cur_len += pad_bytes;
    988 		while (pad_bytes--)
    989 			*dst_p++ = 0;
    990 	}
    991 
    992 	return 0;
    993 }
    994 
    995 
    996 /*
    997  * isns_get_tlv_info_advance - advances pdu/buffer pointers if at end of
    998  *			       current buffer.
    999  */
   1000 static void
   1001 isns_get_tlv_info_advance(struct isns_get_tlv_info_s *info_p)
   1002 {
   1003 	if ((info_p->buf_p != NULL) &&
   1004 	    (info_p->buf_ofs == (int)info_p->buf_p->cur_len)) {
   1005 		info_p->buf_p = info_p->buf_p->next;
   1006 		info_p->buf_ofs = 0;
   1007 	}
   1008 
   1009 	if ((info_p->buf_p == NULL) && (info_p->pdu_p->next != NULL)) {
   1010 		info_p->pdu_p = info_p->pdu_p->next;
   1011 		info_p->buf_p = isns_get_pdu_head_buffer(info_p->pdu_p);
   1012 		info_p->buf_ofs = 0;
   1013 	}
   1014 }
   1015 
   1016 
   1017 /*
   1018  * isns_get_tlv_uint32 - retrieve host-ordered uint32_t from PDU buffer at
   1019  *			 starting offset and adjusts isns_get_tlv_info
   1020  *			 pointers accordingly.
   1021  */
   1022 static int
   1023 isns_get_tlv_uint32(struct isns_get_tlv_info_s *info_p, uint32_t *uint32_p)
   1024 {
   1025 	/* Advance to next buffer/pdu (if necessary). */
   1026 	isns_get_tlv_info_advance(info_p);
   1027 
   1028 	if ((info_p->buf_p == NULL) ||
   1029 	    ((info_p->buf_ofs + 4) > (int)info_p->buf_p->cur_len)) {
   1030 		DBG("isns_get_tlv_uint32: end of buffer reached\n");
   1031 		return EFAULT;
   1032 	}
   1033 
   1034 	*uint32_p = ntohl(*(uint32_t *)isns_buffer_data(info_p->buf_p,
   1035 	    info_p->buf_ofs));
   1036 	info_p->buf_ofs += 4;
   1037 
   1038 	return 0;
   1039 }
   1040 
   1041 
   1042 /*
   1043  * isns_get_tlv_data - retrieves data from PDU buffer at starting offset
   1044  *		       for TLV data contained in specified isns_get_tlv_info.
   1045  */
   1046 static int
   1047 isns_get_tlv_data(struct isns_get_tlv_info_s *info_p, int data_len,
   1048     void **data_pp)
   1049 {
   1050 	struct isns_buffer_s *extra_buf_p;
   1051 	struct isns_get_tlv_info_s gti;
   1052 	uint8_t *data_p, *extra_data_p;
   1053 	int bytes_remaining, cbytes;
   1054 
   1055 	/* First, NULL return data pointer. */
   1056 	*data_pp = NULL;
   1057 
   1058 	/* Advance to next buffer/pdu (if necessary). */
   1059 	isns_get_tlv_info_advance(info_p);
   1060 
   1061 	/* Make sure we have a current get tlv info buffer. */
   1062 	if (info_p->buf_p == NULL) {
   1063 		DBG("isns_get_tlv_data: no next buffer\n");
   1064 		return EFAULT;
   1065 	}
   1066 
   1067 	/* Get pointer into buffer where desired TLV resides. */
   1068 	data_p = isns_buffer_data(info_p->buf_p, info_p->buf_ofs);
   1069 
   1070 	/* TLV data completely resides in current buffer. */
   1071 	if ((info_p->buf_ofs + data_len) <= (int)info_p->buf_p->cur_len) {
   1072 		info_p->buf_ofs += data_len;
   1073 		*data_pp = data_p;
   1074 		return 0;
   1075 	}
   1076 
   1077 	/*
   1078 	 * TLV data extends into next buffer so an "extra" buffer is allocated
   1079 	 * that is large enough to hold the entire data value.  The extra buffer
   1080 	 * is added to the transaction's extra buffer list so we can free it
   1081 	 * when the transaction is freed.
   1082 	 */
   1083 
   1084 	if ((extra_buf_p = isns_new_buffer(data_len)) == NULL) {
   1085 		DBG("isns_get_tlv_data: error on isns_new_buffer()\n");
   1086 		return ENOMEM;
   1087 	}
   1088 	if (info_p->extra_buf_list == NULL)
   1089 		info_p->extra_buf_list = extra_buf_p;
   1090 	else {
   1091 		extra_buf_p->next = info_p->extra_buf_list;
   1092 		info_p->extra_buf_list = extra_buf_p;
   1093 	}
   1094 
   1095 	/* Setup to copy data bytes out to extra buffer. */
   1096 	gti = *info_p;
   1097 	extra_data_p = isns_buffer_data(extra_buf_p, 0);
   1098 	bytes_remaining = data_len;
   1099 
   1100 	while (bytes_remaining > 0) {
   1101 		/*
   1102 		 * Advance to next buffer/pdu (if necessary), using local copy
   1103 		 * of the get_tlv_info structure.
   1104 		 */
   1105 		isns_get_tlv_info_advance(&gti);
   1106 		if (gti.buf_p == NULL) {
   1107 			DBG("isns_get_tlv_data: no next buffer\n");
   1108 			return EFAULT;
   1109 		}
   1110 
   1111 		data_p = isns_buffer_data(gti.buf_p, gti.buf_ofs);
   1112 
   1113 		cbytes = MIN(bytes_remaining, ((int)gti.buf_p->cur_len - gti.buf_ofs));
   1114 		bytes_remaining -= cbytes;
   1115 		gti.buf_ofs += cbytes;
   1116 		while (cbytes--)
   1117 			*extra_data_p++ = *data_p++;
   1118 	}
   1119 
   1120 	/* Update isns_get_tlv_info with our local copy. */
   1121 	*info_p = gti;
   1122 
   1123 	/* Assign return data pointer. */
   1124 	*data_pp = isns_buffer_data(extra_buf_p, 0);
   1125 
   1126 	return 0;
   1127 }
   1128 
   1129 
   1130 /*
   1131  * isns_get_pdu_response_status - returns status in PDU response
   1132  *
   1133  * Returns: 0 - success
   1134  *	    EPERM - operation not permitted, trans not complete
   1135  *	    EINVAL - invalid trans PDU response/payload
   1136  */
   1137 int
   1138 isns_get_pdu_response_status(struct isns_trans_s *trans_p, uint32_t *status_p)
   1139 {
   1140 	struct isns_pdu_s *pdu_p;
   1141 
   1142 	if ((isns_get_trans_flags(trans_p) & ISNS_TRANSF_COMPLETE) == 0)
   1143 		return EPERM;
   1144 
   1145 	pdu_p = isns_get_pdu_response(trans_p);
   1146 	if ((pdu_p == NULL)
   1147 	    || (pdu_p->payload_p == NULL)
   1148 	    || (pdu_p->payload_p->cur_len < 4))
   1149 	    	return EINVAL;
   1150 
   1151 	*status_p = htonl(*(uint32_t *)isns_buffer_data(pdu_p->payload_p, 0));
   1152 
   1153 	return 0;
   1154 }
   1155 
   1156 
   1157 /*
   1158  * isns_add_pdu_list - adds pdu to specified pdu list
   1159  */
   1160 static void
   1161 isns_add_pdu_list(struct isns_pdu_s **list_pp, struct isns_pdu_s *pdu_p)
   1162 {
   1163 	struct isns_pdu_s *p, *p_prev;
   1164 
   1165 
   1166 	if (*list_pp == NULL) {
   1167 		*list_pp = pdu_p;
   1168 		pdu_p->next = NULL;
   1169 		return;
   1170 	}
   1171 
   1172 	p = *list_pp;
   1173 	while (p != NULL) {
   1174 		if (pdu_p->hdr.seq_id < p->hdr.seq_id) {
   1175 			if (p == *list_pp) {
   1176 				*list_pp = pdu_p;
   1177 				pdu_p->next = p;
   1178 			} else {
   1179 				p_prev = *list_pp;
   1180 				while (p_prev->next != p)
   1181 					p_prev = p_prev->next;
   1182 				p_prev->next = pdu_p;
   1183 				pdu_p->next = p;
   1184 			}
   1185 
   1186 			return;
   1187 		}
   1188 		p = p->next;
   1189 	}
   1190 
   1191 	/* pdu_p->hdr.seq_id > hdr.seq_id of all list elements */
   1192 	p = *list_pp;
   1193 	while (p->next != NULL)
   1194 		p = p->next;
   1195 	p->next = pdu_p;
   1196 	pdu_p->next = NULL;
   1197 }
   1198 
   1199 
   1200 /*
   1201  * isns_get_pdu_head_buffer - returns PDU payload head buffer
   1202  */
   1203 static struct isns_buffer_s *
   1204 isns_get_pdu_head_buffer(struct isns_pdu_s *pdu_p)
   1205 {
   1206 	return pdu_p->payload_p;
   1207 }
   1208 
   1209 
   1210 #if 0
   1211 /*
   1212  * isns_get_pdu_tail_buffer - returns PDU payload tail buffer
   1213  */
   1214 static struct isns_buffer_s *
   1215 isns_get_pdu_tail_buffer(struct isns_pdu_s *pdu_p)
   1216 {
   1217 	struct isns_buffer_s *buf_p;
   1218 
   1219 	buf_p = pdu_p->payload_p;
   1220 	if (buf_p != NULL)
   1221 		while (buf_p->next != NULL) buf_p = buf_p->next;
   1222 
   1223 	return buf_p;
   1224 }
   1225 #endif
   1226 
   1227 
   1228 /*
   1229  * isns_get_pdu_active_buffer - returns PDU payload "active buffer where the
   1230  *				next TLV/data should be written
   1231  */
   1232 static struct isns_buffer_s *
   1233 isns_get_pdu_active_buffer(struct isns_pdu_s *pdu_p)
   1234 {
   1235 	struct isns_buffer_s *buf_p;
   1236 
   1237 	buf_p = pdu_p->payload_p;
   1238 	while ((buf_p->next != NULL) && (buf_p->cur_len == buf_p->alloc_len)) {
   1239 		buf_p = buf_p->next;
   1240 	}
   1241 
   1242 	return buf_p;
   1243 }
   1244 
   1245 
   1246 /*
   1247  * isns_get_next_trans_id - returns next ISNS transaction ID to use
   1248  */
   1249 static uint32_t
   1250 isns_get_next_trans_id(void)
   1251 {
   1252 	static int	trans_id = 1;
   1253 
   1254 	return trans_id++;
   1255 }
   1256 
   1257 
   1258 #ifdef ISNS_DEBUG
   1259 /*
   1260  * isns_dump_pdu - dumps PDU contents
   1261  */
   1262 void
   1263 isns_dump_pdu(struct isns_pdu_s *pdu_p)
   1264 {
   1265 	int n, pos;
   1266 	struct isns_buffer_s *buf_p;
   1267 	uint8_t *p;
   1268 	char text[17];
   1269 
   1270 	if (pdu_p == NULL) {
   1271 		DBG("isns_dump_pdu: pdu_p is NULL\n");
   1272 		return;
   1273 	}
   1274 
   1275 	DBG("pdu header:\n");
   1276 	if (pdu_p->byteorder_host) {
   1277 	    DBG("ver=0x%04X, func=%d(%s), len=%d, flags=0x%04X, trans=%d, "
   1278 	        "seq=%d\n",
   1279 		pdu_p->hdr.isnsp_version,
   1280 		pdu_p->hdr.func_id & ~0x8000,
   1281 		(pdu_p->hdr.func_id & 0x8000 ? "rsp" : "req"),
   1282 		pdu_p->hdr.payload_len,
   1283 		pdu_p->hdr.flags,
   1284 		pdu_p->hdr.trans_id,
   1285 		pdu_p->hdr.seq_id);
   1286 	} else {
   1287 	    DBG("ver=0x%04X, func=%d(%s), len=%d, flags=0x%04X, trans=%d, "
   1288 	        "seq=%d\n",
   1289 		isns_ntohs(pdu_p->hdr.isnsp_version),
   1290 		isns_ntohs(pdu_p->hdr.func_id) & ~0x8000,
   1291 		(pdu_p->hdr.func_id & 0x0080 ? "rsp" : "req"),
   1292 		isns_ntohs(pdu_p->hdr.payload_len),
   1293 		isns_ntohs(pdu_p->hdr.flags),
   1294 		isns_ntohs(pdu_p->hdr.trans_id),
   1295 		isns_ntohs(pdu_p->hdr.seq_id));
   1296 	}
   1297 
   1298 	DBG("pdu buffers:\n");
   1299 	buf_p = pdu_p->payload_p;
   1300 	while (buf_p != NULL) {
   1301 		DBG("[%p]: alloc_len=%d, cur_len=%d\n",
   1302 		    buf_p, buf_p->alloc_len, buf_p->cur_len);
   1303 		buf_p = buf_p->next;
   1304 	}
   1305 
   1306 	DBG("pdu payload:\n");
   1307 	buf_p = pdu_p->payload_p;
   1308 	if (buf_p == NULL) {
   1309 		DBG("<none>\n");
   1310 		return;
   1311 	}
   1312 
   1313 	pos = 0;
   1314 	memset(text, 0, 17);
   1315 	while (buf_p != NULL) {
   1316 		p = isns_buffer_data(buf_p, 0);
   1317 		for (n = 0; n < buf_p->cur_len; n++) {
   1318 			DBG("%02X ", *p);
   1319 			text[pos] = (isprint(*p) ? *p : '.');
   1320 			pos++;
   1321  			p++;
   1322 
   1323 			if ((pos % 16) == 0) {
   1324 				DBG("   %s\n", text);
   1325 				memset(text, 0, 17);
   1326 				pos = 0;
   1327 			}
   1328 		}
   1329 		buf_p = buf_p->next;
   1330 	}
   1331 
   1332 	if ((pos % 16) != 0)
   1333 		DBG("%*c   %s\n", (16 - (pos % 16)) * 3, ' ', text);
   1334 }
   1335 #endif /* ISNS_DEBUG */
   1336