xref: /netbsd-src/lib/libisns/isns_pdu.c (revision c2f76ff004a2cb67efe5b12d97bd3ef7fe89e18d)
1 /*	$NetBSD: isns_pdu.c,v 1.1.1.1 2011/01/16 01:22:50 agc 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.1.1.1 2011/01/16 01:22:50 agc 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()
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, (int)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, int data_len, const void *data_p)
588 {
589 	struct isns_trans_s *trans_p;
590 	uint8_t tlv_buf[ISNS_TLV_HDR_SIZE];
591 	int rval;
592 
593 	DBG("isns_add_tlv: trans=%p, tag=%d, data_len=%d, data_p=%p\n",
594 	    trans, tag, data_len, data_p);
595 
596 	if (trans == ISNS_INVALID_TRANS) {
597 		DBG("isns_add_tlv: error - trans=%p\n", trans);
598 		return EINVAL;
599 	}
600 
601 	if ((data_len > 0) && (data_p == NULL)) {
602 		DBG("isns_add_tlv: error data_len=%d, data_p=%p\n",
603 		    data_len, data_p);
604 		return EINVAL;
605 	}
606 
607 	/* Set tag, length in header buffer and add to PDU payload data. */
608 	trans_p = (struct isns_trans_s *)trans;
609 	ISNS_TLV_SET_TAG(tlv_buf, tag);
610 	ISNS_TLV_SET_LEN(tlv_buf, ISNS_PAD4_LEN(data_len));
611 	rval = isns_add_pdu_payload_data(trans_p, tlv_buf, ISNS_TLV_HDR_SIZE);
612 
613 	/* If header added okay, add value portion to PDU payload data. */
614 	if ((rval == 0) && (data_len > 0))
615 		rval = isns_add_pdu_payload_data(trans_p, data_p, data_len);
616 
617 	return rval;
618 }
619 
620 
621 /*
622  * isns_get_tlv - get TLV value from response PDU for transaction
623  *
624  * returns:
625  *   0 - success
626  *   ENOENT - no (more) TLVs in this transaction
627  *   EINVAL - invalid arg
628  *   EPERM  - operation not permitted - transaction not complete
629  *   ENOMEM - could not allocate storage for spanning TLV data
630  */
631 int
632 isns_get_tlv(ISNS_TRANS trans, int which_tlv, uint32_t *tag_p, int *data_len_p,
633     void **data_pp)
634 {
635 	struct isns_trans_s *trans_p;
636 	struct isns_get_tlv_info_s *info_p;
637 	struct isns_pdu_s *pdu_p;
638 	int rval;
639 
640 	if (trans == ISNS_INVALID_TRANS) {
641 		DBG("isns_get_tlv: error - trans=%p\n", trans);
642 		return EINVAL;
643 	}
644 
645 	trans_p = (struct isns_trans_s *)trans;
646 	if ((isns_get_trans_flags(trans_p) & ISNS_TRANSF_COMPLETE) == 0) {
647 		DBG("isns_get_tlv: error - trans not complete\n");
648 		return EPERM;
649 	}
650 
651 	/* Get response PDU for this transaction. */
652 	pdu_p = isns_get_pdu_response(trans_p);
653 	if (pdu_p == NULL) {
654 		DBG("isns_get_tlv: error - no response PDU in transaction\n");
655 		return EINVAL;
656 	}
657 
658 	if (pdu_p->payload_p->cur_len == 0) {
659 		DBG("isns_get_tlv: error - zero length PDU payload\n");
660 		return EINVAL;
661 	}
662 
663 	/* If get_tlv_info unset, treat ISNS_TLV_NEXT as ISNS_TLV_FIRST. */
664 	info_p = &trans_p->get_tlv_info;
665 	if ((which_tlv == ISNS_TLV_NEXT) && (info_p->pdu_p == NULL))
666 		which_tlv = ISNS_TLV_FIRST;
667 
668 	/*!!! make sure PDU uses TLVs here */
669 
670 	switch (which_tlv) {
671 	case ISNS_TLV_NEXT:
672 		/* For next TLV, nothing to do here - use get_tlv_info as-is. */
673 		break;
674 
675 	case ISNS_TLV_FIRST:
676 		/* For first TLV, reset get_tlv_info. */
677 		info_p->pdu_p = pdu_p;
678 		info_p->buf_p = isns_get_pdu_head_buffer(pdu_p);
679 		info_p->buf_ofs = 4;
680 		break;
681 
682 	default:
683 		DBG("isns_get_tlv: invalid arg (which_tlv=%d)\n", which_tlv);
684 		return EINVAL;
685 	}
686 
687 	/*
688 	 * Get the type, length, and data (value) for the TLV.  The get calls
689 	 * below will advance the pointers in get_tlv_info_s *info_p.  Note that
690 	 * if the get of the TAG type fails, ENOENT is returned indicating that
691 	 * no more TLVs exist for this request PDU.
692 	 */
693 	if ((rval = isns_get_tlv_uint32(info_p, tag_p)) != 0) {
694 		DBG("isns_get_tlv: error on isns_get_tlv_uint32() tag\n");
695 		return ENOENT;
696 	}
697 
698 	if ((rval = isns_get_tlv_uint32(info_p, (uint32_t *)data_len_p)) != 0) {
699 		DBG("isns_get_tlv: error on isns_get_tlv_uint32() data len\n");
700 		return rval;
701 	}
702 
703 	rval = isns_get_tlv_data(info_p, *data_len_p, data_pp);
704 	if (rval != 0) {
705 		DBG("isns_get_tlv: error on isns_get_tlv_data()\n");
706 		return rval;
707 	}
708 
709 	return 0;
710 }
711 
712 
713 /*
714  * isns_set_trans_flags - sets flag bit(s) in transaction flags member
715  */
716 uint32_t
717 isns_set_trans_flags(struct isns_trans_s *trans_p, uint32_t flags)
718 {
719 	pthread_mutex_lock(&trans_p->cfg_p->trans_mutex);
720 	trans_p->flags |= flags;
721 	flags = trans_p->flags;
722 	pthread_mutex_unlock(&trans_p->cfg_p->trans_mutex);
723 
724 	return flags;
725 }
726 
727 
728 /*
729  * isns_add_pdu_request - adds PDU to transaction request PDU list
730  */
731 void
732 isns_add_pdu_request(struct isns_trans_s *trans_p, struct isns_pdu_s *pdu_p)
733 {
734 	DBG("isns_add_pdu_request: trans_p=%p, pdu_p=%p\n", trans_p, pdu_p);
735 
736 	isns_add_pdu_list(&trans_p->pdu_req_list, pdu_p);
737 }
738 
739 
740 /*
741  * isns_add_pdu_response - adds PDU to transaction response PDU list
742  */
743 void
744 isns_add_pdu_response(struct isns_trans_s *trans_p, struct isns_pdu_s *pdu_p)
745 {
746 	DBG("isns_add_pdu_response: trans_p=%p, pdu_p=%p\n", trans_p, pdu_p);
747 
748 	isns_add_pdu_list(&trans_p->pdu_rsp_list, pdu_p);
749 }
750 
751 
752 /*
753  * isns_get_pdu_request_tail - returns last PDU in request PDU chain
754  */
755 struct isns_pdu_s *
756 isns_get_pdu_request_tail(struct isns_trans_s *trans_p)
757 {
758 	struct isns_pdu_s *pdu_p;
759 
760 	if ((pdu_p = isns_get_pdu_request(trans_p)) != NULL) {
761 		while (pdu_p->next != NULL)
762 			pdu_p = pdu_p->next;
763 	}
764 
765 	return pdu_p;
766 }
767 
768 
769 /*
770  * isns_new_pdu - allocates a new PDU and assigns funtion ID and flags
771  */
772 struct isns_pdu_s *
773 isns_new_pdu(struct isns_config_s *cfg_p, uint16_t trans_id, uint16_t func_id,
774     uint16_t flags)
775 {
776 	struct isns_buffer_s *buf_p;
777 
778 	/*
779 	 * Allocate a buffer at least large enough for our isns_pdu_s struct
780 	 * and the embedded isns_buffer_s struct for the PDU payload data and 4
781 	 * bytes of actual payload data.
782 	 */
783 	buf_p = isns_new_buffer(/* CONSTCOND */(int)MAX(ISNS_BUF_SIZE,
784 	    sizeof(struct isns_pdu_s) + sizeof(struct isns_buffer_s) + 4));
785 	if (buf_p == NULL) {
786 		DBG("isns_new_pdu: error on isns_new_buffer()\n");
787 		return NULL;
788 	}
789 
790 	return isns_init_pdu(buf_p, cfg_p, trans_id, func_id, flags);
791 }
792 
793 
794 /*
795  * isns_free_pdu - frees a PDU and all associated buffers
796  */
797 void
798 isns_free_pdu(struct isns_pdu_s *pdu_p)
799 {
800 	struct isns_buffer_s *buf_p, *free_buf_p;
801 
802 	DBG("isns_free_pdu: %p\n", pdu_p);
803 
804 	if (pdu_p != NULL) {
805 		/* Free all payload buffers. */
806 		buf_p = pdu_p->payload_p;
807 		while (buf_p != NULL) {
808 			free_buf_p = buf_p;
809 			buf_p = buf_p->next;
810 			isns_free_buffer(free_buf_p);
811 		}
812 		/*
813 		 * Get a pointer to the ISNS buffer in which this PDU is
814 		 * contained, and then free it.
815 		 */
816 		buf_p = ((struct isns_buffer_s *)(void *)(pdu_p))-1 ;
817 		isns_free_buffer(buf_p);
818 	}
819 }
820 
821 
822 /*
823  * isns_send_pdu - initiates the send PDU task
824  */
825 int
826 isns_send_pdu(ISNS_TRANS trans, struct isns_pdu_s *pdu_p,
827     const struct timespec *timeout_p)
828 {
829 	struct isns_trans_s *trans_p;
830 	struct isns_task_s* task_p;
831 	int rval;
832 
833 	if (trans == ISNS_INVALID_TRANS) {
834 		DBG("isns_send_pdu: error - trans=%p\n", trans);
835 		return EINVAL;
836 	}
837 
838 	if (pdu_p == NULL) {
839 		DBG("isns_send_pdu: error - pdu_p=%p\n", pdu_p);
840 		return EINVAL;
841 	}
842 
843 	trans_p = (struct isns_trans_s *)trans;
844 
845 	/* Build SEND_PDU task, insert on queue and issue command. */
846 	task_p = isns_new_task(pdu_p->cfg_p, ISNS_TASK_SEND_PDU,
847 	    (timeout_p != NULL));
848 	task_p->var.send_pdu.trans_p = trans_p;
849 	task_p->var.send_pdu.pdu_p = pdu_p;
850 
851 	isns_taskq_insert_tail(pdu_p->cfg_p, task_p);
852 
853 	isns_issue_cmd(pdu_p->cfg_p, ISNS_CMD_PROCESS_TASKQ);
854 
855 	if (timeout_p == NULL)
856 		rval = 0;
857 	else {
858 		rval = isns_wait_task(task_p, timeout_p);
859 		if (rval == ETIMEDOUT) {
860 			DBG("isns_send_pdu: "
861 			     "timeout on isns_wait_task() trans_id=%d\n",
862 			     trans_p->id);
863 
864 			isns_issue_cmd_with_data(task_p->cfg_p,
865 			    ISNS_CMD_ABORT_TRANS, (void *)&trans_p->id,
866 			    (int)sizeof(trans_p->id));
867 		}
868 	}
869 
870 	return rval;
871 }
872 
873 
874 /*
875  * isns_init_pdu - initialize ISNS buffer to be a PDU
876  */
877 static struct isns_pdu_s *
878 isns_init_pdu(struct isns_buffer_s *buf_p, struct isns_config_s *cfg_p,
879     uint16_t trans_id, uint16_t func_id, uint16_t flags)
880 {
881 	struct isns_pdu_s *pdu_p;
882 
883 	/* The config and buffer pointers must be valid here. */
884 	assert(cfg_p != NULL);
885 	assert(buf_p != NULL);
886 
887 	/* The PDU starts at offset 0 for the ISNS buffer data. */
888 	pdu_p = isns_buffer_data(buf_p, 0);
889 	buf_p->cur_len = sizeof(struct isns_pdu_s);
890 
891 	/* Assign PDU members. */
892 	pdu_p->cfg_p = cfg_p;
893 	pdu_p->hdr.isnsp_version = ISNSP_VERSION;
894 	pdu_p->hdr.func_id = func_id;
895 	pdu_p->hdr.payload_len = 0;
896 	pdu_p->hdr.flags = flags;
897 	pdu_p->hdr.trans_id = trans_id;
898 	pdu_p->hdr.seq_id = 0;
899 	pdu_p->byteorder_host = 1;
900 	pdu_p->next = NULL;
901 
902 	/*
903 	 * The PDU payload buffer starts after the PDU struct portion in the
904 	 * ISNS buffer passed in to this function.  So, assign the payload_p
905 	 * pointer accordingly, and then init the buffer with the proper length
906 	 * and ISNS_BUF_STATIC type.
907 	 */
908 	pdu_p->payload_p = (struct isns_buffer_s *)
909 	    isns_buffer_data(buf_p, buf_p->cur_len);
910 	ISNS_INIT_BUFFER(pdu_p->payload_p, (unsigned)(buf_p->alloc_len -
911 	    sizeof(struct isns_pdu_s) - sizeof(struct isns_buffer_s)),
912 	    ISNS_BUF_STATIC);
913 
914 	DBG("isns_init_pdu: %p\n", pdu_p);
915 
916 	return pdu_p;
917 }
918 
919 
920 /*
921  * isns_add_pdu_payload_data - add data to PDU payload
922  */
923 static int
924 isns_add_pdu_payload_data(struct isns_trans_s *trans_p, const void *data_p,
925     int len)
926 {
927 	struct isns_pdu_s *pdu_p, *new_pdu_p;
928 	struct isns_buffer_s *buf_p, *new_buf_p;
929 	const uint8_t *src_p;
930 	uint8_t *dst_p;
931 	int pad_bytes;
932 
933 	/* Get the request PDU for this transaction. */
934 	if ((pdu_p = isns_get_pdu_request_tail(trans_p)) == NULL) {
935 		DBG("isns_add_pdu_payload_data: no request PDU\n");
936 		return EINVAL;
937 	}
938 	/* Get the active buffer for this PDU (where data should be copied). */
939 	buf_p = isns_get_pdu_active_buffer(pdu_p);
940 
941 	/* Set up source and destination pointers.  Calculate pad bytes. */
942 	src_p = data_p;
943 	dst_p = isns_buffer_data(buf_p, buf_p->cur_len);
944 	pad_bytes = ISNS_PAD4_BYTES(len);
945 
946 	/*
947 	 * Move data from source to PDU buffer(s), allocated new pdus and
948 	 * buffers as necessary to accomodate the data.
949 	 */
950 	while (len--) {
951 		/* If at max for one PDU payload, add a new PDU. */
952 		if (pdu_p->hdr.payload_len == ISNS_MAX_PDU_PAYLOAD) {
953 			new_pdu_p = isns_new_pdu(trans_p->cfg_p,
954 			    trans_p->id, trans_p->func_id, pdu_p->hdr.flags);
955 			if (new_pdu_p == NULL)
956 				return ENOMEM;
957 			isns_add_pdu_request(trans_p, new_pdu_p);
958 			pdu_p = new_pdu_p;
959 			buf_p = pdu_p->payload_p;
960 			dst_p = isns_buffer_data(buf_p, 0);
961 		}
962 		/* If at end of current buffer, add a new buffer. */
963 		if (buf_p->cur_len == buf_p->alloc_len) {
964 			if (buf_p->next != NULL)
965 				buf_p = buf_p->next;
966 			else {
967 				new_buf_p = isns_new_buffer(0);
968 				if (new_buf_p == NULL)
969 					return ENOMEM;
970 				buf_p->next = new_buf_p;
971 				buf_p = new_buf_p;
972 			}
973 			dst_p = isns_buffer_data(buf_p, 0);
974 		}
975 		pdu_p->hdr.payload_len++;
976 		buf_p->cur_len++;
977 		*dst_p++ = *src_p++;
978 	}
979 
980 	/*
981 	 * Since the buffer alloc length is always a multiple of 4, we are
982 	 * guaranteed to have enough room for the pad bytes.
983 	 */
984 	if (pad_bytes > 0) {
985 		pdu_p->hdr.payload_len += pad_bytes;
986 		buf_p->cur_len += pad_bytes;
987 		while (pad_bytes--)
988 			*dst_p++ = 0;
989 	}
990 
991 	return 0;
992 }
993 
994 
995 /*
996  * isns_get_tlv_info_advance - advances pdu/buffer pointers if at end of
997  *			       current buffer.
998  */
999 static void
1000 isns_get_tlv_info_advance(struct isns_get_tlv_info_s *info_p)
1001 {
1002 	if ((info_p->buf_p != NULL) &&
1003 	    (info_p->buf_ofs == (int)info_p->buf_p->cur_len)) {
1004 		info_p->buf_p = info_p->buf_p->next;
1005 		info_p->buf_ofs = 0;
1006 	}
1007 
1008 	if ((info_p->buf_p == NULL) && (info_p->pdu_p->next != NULL)) {
1009 		info_p->pdu_p = info_p->pdu_p->next;
1010 		info_p->buf_p = isns_get_pdu_head_buffer(info_p->pdu_p);
1011 		info_p->buf_ofs = 0;
1012 	}
1013 }
1014 
1015 
1016 /*
1017  * isns_get_tlv_uint32 - retrieve host-ordered uint32_t from PDU buffer at
1018  *			 starting offset and adjusts isns_get_tlv_info
1019  *			 pointers accordingly.
1020  */
1021 static int
1022 isns_get_tlv_uint32(struct isns_get_tlv_info_s *info_p, uint32_t *uint32_p)
1023 {
1024 	/* Advance to next buffer/pdu (if necessary). */
1025 	isns_get_tlv_info_advance(info_p);
1026 
1027 	if ((info_p->buf_p == NULL) ||
1028 	    ((info_p->buf_ofs + 4) > (int)info_p->buf_p->cur_len)) {
1029 		DBG("isns_get_tlv_uint32: end of buffer reached\n");
1030 		return EFAULT;
1031 	}
1032 
1033 	*uint32_p = ntohl(*(uint32_t *)isns_buffer_data(info_p->buf_p,
1034 	    info_p->buf_ofs));
1035 	info_p->buf_ofs += 4;
1036 
1037 	return 0;
1038 }
1039 
1040 
1041 /*
1042  * isns_get_tlv_data - retrieves data from PDU buffer at starting offset
1043  *		       for TLV data contained in specified isns_get_tlv_info.
1044  */
1045 static int
1046 isns_get_tlv_data(struct isns_get_tlv_info_s *info_p, int data_len,
1047     void **data_pp)
1048 {
1049 	struct isns_buffer_s *extra_buf_p;
1050 	struct isns_get_tlv_info_s gti;
1051 	uint8_t *data_p, *extra_data_p;
1052 	int bytes_remaining, cbytes;
1053 
1054 	/* First, NULL return data pointer. */
1055 	*data_pp = NULL;
1056 
1057 	/* Advance to next buffer/pdu (if necessary). */
1058 	isns_get_tlv_info_advance(info_p);
1059 
1060 	/* Make sure we have a current get tlv info buffer. */
1061 	if (info_p->buf_p == NULL) {
1062 		DBG("isns_get_tlv_data: no next buffer\n");
1063 		return EFAULT;
1064 	}
1065 
1066 	/* Get pointer into buffer where desired TLV resides. */
1067 	data_p = isns_buffer_data(info_p->buf_p, info_p->buf_ofs);
1068 
1069 	/* TLV data completely resides in current buffer. */
1070 	if ((info_p->buf_ofs + data_len) <= (int)info_p->buf_p->cur_len) {
1071 		info_p->buf_ofs += data_len;
1072 		*data_pp = data_p;
1073 		return 0;
1074 	}
1075 
1076 	/*
1077 	 * TLV data extends into next buffer so an "extra" buffer is allocated
1078 	 * that is large enough to hold the entire data value.  The extra buffer
1079 	 * is added to the transaction's extra buffer list so we can free it
1080 	 * when the transaction is freed.
1081 	 */
1082 
1083 	if ((extra_buf_p = isns_new_buffer(data_len)) == NULL) {
1084 		DBG("isns_get_tlv_data: error on isns_new_buffer()\n");
1085 		return ENOMEM;
1086 	}
1087 	if (info_p->extra_buf_list == NULL)
1088 		info_p->extra_buf_list = extra_buf_p;
1089 	else {
1090 		extra_buf_p->next = info_p->extra_buf_list;
1091 		info_p->extra_buf_list = extra_buf_p;
1092 	}
1093 
1094 	/* Setup to copy data bytes out to extra buffer. */
1095 	gti = *info_p;
1096 	extra_data_p = isns_buffer_data(extra_buf_p, 0);
1097 	bytes_remaining = data_len;
1098 
1099 	while (bytes_remaining > 0) {
1100 		/*
1101 		 * Advance to next buffer/pdu (if necessary), using local copy
1102 		 * of the get_tlv_info structure.
1103 		 */
1104 		isns_get_tlv_info_advance(&gti);
1105 		if (gti.buf_p == NULL) {
1106 			DBG("isns_get_tlv_data: no next buffer\n");
1107 			return EFAULT;
1108 		}
1109 
1110 		data_p = isns_buffer_data(gti.buf_p, gti.buf_ofs);
1111 
1112 		cbytes = MIN(bytes_remaining, ((int)gti.buf_p->cur_len - gti.buf_ofs));
1113 		bytes_remaining -= cbytes;
1114 		gti.buf_ofs += cbytes;
1115 		while (cbytes--)
1116 			*extra_data_p++ = *data_p++;
1117 	}
1118 
1119 	/* Update isns_get_tlv_info with our local copy. */
1120 	*info_p = gti;
1121 
1122 	/* Assign return data pointer. */
1123 	*data_pp = isns_buffer_data(extra_buf_p, 0);
1124 
1125 	return 0;
1126 }
1127 
1128 
1129 /*
1130  * isns_get_pdu_response_status - returns status in PDU response
1131  *
1132  * Returns: 0 - success
1133  *	    EPERM - operation not permitted, trans not complete
1134  *	    EINVAL - invalid trans PDU response/payload
1135  */
1136 int
1137 isns_get_pdu_response_status(struct isns_trans_s *trans_p, uint32_t *status_p)
1138 {
1139 	struct isns_pdu_s *pdu_p;
1140 
1141 	if ((isns_get_trans_flags(trans_p) & ISNS_TRANSF_COMPLETE) == 0)
1142 		return EPERM;
1143 
1144 	pdu_p = isns_get_pdu_response(trans_p);
1145 	if ((pdu_p == NULL)
1146 	    || (pdu_p->payload_p == NULL)
1147 	    || (pdu_p->payload_p->cur_len < 4))
1148 	    	return EINVAL;
1149 
1150 	*status_p = htonl(*(uint32_t *)isns_buffer_data(pdu_p->payload_p, 0));
1151 
1152 	return 0;
1153 }
1154 
1155 
1156 /*
1157  * isns_add_pdu_list - adds pdu to specified pdu list
1158  */
1159 static void
1160 isns_add_pdu_list(struct isns_pdu_s **list_pp, struct isns_pdu_s *pdu_p)
1161 {
1162 	struct isns_pdu_s *p, *p_prev;
1163 
1164 
1165 	if (*list_pp == NULL) {
1166 		*list_pp = pdu_p;
1167 		pdu_p->next = NULL;
1168 		return;
1169 	}
1170 
1171 	p = *list_pp;
1172 	while (p != NULL) {
1173 		if (pdu_p->hdr.seq_id < p->hdr.seq_id) {
1174 			if (p == *list_pp) {
1175 				*list_pp = pdu_p;
1176 				pdu_p->next = p;
1177 			} else {
1178 				p_prev = *list_pp;
1179 				while (p_prev->next != p)
1180 					p_prev = p_prev->next;
1181 				p_prev->next = pdu_p;
1182 				pdu_p->next = p;
1183 			}
1184 
1185 			return;
1186 		}
1187 		p = p->next;
1188 	}
1189 
1190 	/* pdu_p->hdr.seq_id > hdr.seq_id of all list elements */
1191 	p = *list_pp;
1192 	while (p->next != NULL)
1193 		p = p->next;
1194 	p->next = pdu_p;
1195 	pdu_p->next = NULL;
1196 }
1197 
1198 
1199 /*
1200  * isns_get_pdu_head_buffer - returns PDU payload head buffer
1201  */
1202 static struct isns_buffer_s *
1203 isns_get_pdu_head_buffer(struct isns_pdu_s *pdu_p)
1204 {
1205 	return pdu_p->payload_p;
1206 }
1207 
1208 
1209 #if 0
1210 /*
1211  * isns_get_pdu_tail_buffer - returns PDU payload tail buffer
1212  */
1213 static struct isns_buffer_s *
1214 isns_get_pdu_tail_buffer(struct isns_pdu_s *pdu_p)
1215 {
1216 	struct isns_buffer_s *buf_p;
1217 
1218 	buf_p = pdu_p->payload_p;
1219 	if (buf_p != NULL)
1220 		while (buf_p->next != NULL) buf_p = buf_p->next;
1221 
1222 	return buf_p;
1223 }
1224 #endif
1225 
1226 
1227 /*
1228  * isns_get_pdu_active_buffer - returns PDU payload "active buffer where the
1229  *				next TLV/data should be written
1230  */
1231 static struct isns_buffer_s *
1232 isns_get_pdu_active_buffer(struct isns_pdu_s *pdu_p)
1233 {
1234 	struct isns_buffer_s *buf_p;
1235 
1236 	buf_p = pdu_p->payload_p;
1237 	while ((buf_p->next != NULL) && (buf_p->cur_len == buf_p->alloc_len)) {
1238 		buf_p = buf_p->next;
1239 	}
1240 
1241 	return buf_p;
1242 }
1243 
1244 
1245 /*
1246  * isns_get_next_trans_id - returns next ISNS transaction ID to use
1247  */
1248 static uint32_t
1249 isns_get_next_trans_id(void)
1250 {
1251 	static int	trans_id = 1;
1252 
1253 	return trans_id++;
1254 }
1255 
1256 
1257 #ifdef ISNS_DEBUG
1258 /*
1259  * isns_dump_pdu - dumps PDU contents
1260  */
1261 void
1262 isns_dump_pdu(struct isns_pdu_s *pdu_p)
1263 {
1264 	int n, pos;
1265 	struct isns_buffer_s *buf_p;
1266 	uint8_t *p;
1267 	char text[17];
1268 
1269 	if (pdu_p == NULL) {
1270 		DBG("isns_dump_pdu: pdu_p is NULL\n");
1271 		return;
1272 	}
1273 
1274 	DBG("pdu header:\n");
1275 	if (pdu_p->byteorder_host) {
1276 	    DBG("ver=0x%04X, func=%d(%s), len=%d, flags=0x%04X, trans=%d, "
1277 	        "seq=%d\n",
1278 		pdu_p->hdr.isnsp_version,
1279 		pdu_p->hdr.func_id & ~0x8000,
1280 		(pdu_p->hdr.func_id & 0x8000 ? "rsp" : "req"),
1281 		pdu_p->hdr.payload_len,
1282 		pdu_p->hdr.flags,
1283 		pdu_p->hdr.trans_id,
1284 		pdu_p->hdr.seq_id);
1285 	} else {
1286 	    DBG("ver=0x%04X, func=%d(%s), len=%d, flags=0x%04X, trans=%d, "
1287 	        "seq=%d\n",
1288 		isns_ntohs(pdu_p->hdr.isnsp_version),
1289 		isns_ntohs(pdu_p->hdr.func_id) & ~0x8000,
1290 		(pdu_p->hdr.func_id & 0x0080 ? "rsp" : "req"),
1291 		isns_ntohs(pdu_p->hdr.payload_len),
1292 		isns_ntohs(pdu_p->hdr.flags),
1293 		isns_ntohs(pdu_p->hdr.trans_id),
1294 		isns_ntohs(pdu_p->hdr.seq_id));
1295 	}
1296 
1297 	DBG("pdu buffers:\n");
1298 	buf_p = pdu_p->payload_p;
1299 	while (buf_p != NULL) {
1300 		DBG("[%p]: alloc_len=%d, cur_len=%d\n",
1301 		    buf_p, buf_p->alloc_len, buf_p->cur_len);
1302 		buf_p = buf_p->next;
1303 	}
1304 
1305 	DBG("pdu payload:\n");
1306 	buf_p = pdu_p->payload_p;
1307 	if (buf_p == NULL) {
1308 		DBG("<none>\n");
1309 		return;
1310 	}
1311 
1312 	pos = 0;
1313 	memset(text, 0, 17);
1314 	while (buf_p != NULL) {
1315 		p = isns_buffer_data(buf_p, 0);
1316 		for (n = 0; n < buf_p->cur_len; n++) {
1317 			DBG("%02X ", *p);
1318 			text[pos] = (isprint(*p) ? *p : '.');
1319 			pos++;
1320  			p++;
1321 
1322 			if ((pos % 16) == 0) {
1323 				DBG("   %s\n", text);
1324 				memset(text, 0, 17);
1325 				pos = 0;
1326 			}
1327 		}
1328 		buf_p = buf_p->next;
1329 	}
1330 
1331 	if ((pos % 16) != 0)
1332 		DBG("%*c   %s\n", (16 - (pos % 16)) * 3, ' ', text);
1333 }
1334 #endif /* ISNS_DEBUG */
1335