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 */
55static struct isns_buffer_list_s *isns_lookup_buffer_list(int);
56
57static struct isns_pdu_s *isns_init_pdu(struct isns_buffer_s *,
58    struct isns_config_s *, uint16_t, uint16_t, uint16_t);
59static int isns_add_pdu_payload_data(struct isns_trans_s *, const void *, int);
60static void isns_get_tlv_info_advance(struct isns_get_tlv_info_s *);
61static int isns_get_tlv_uint32(struct isns_get_tlv_info_s *, uint32_t *);
62static int isns_get_tlv_data(struct isns_get_tlv_info_s *, int, void **);
63
64static void isns_add_pdu_list(struct isns_pdu_s **, struct isns_pdu_s *);
65static struct isns_buffer_s *isns_get_pdu_head_buffer(struct isns_pdu_s *);
66#if 0
67static struct isns_buffer_s *isns_get_pdu_tail_buffer(struct isns_pdu_s *);
68#endif
69static struct isns_buffer_s *isns_get_pdu_active_buffer(struct isns_pdu_s *);
70
71static uint32_t isns_get_next_trans_id(void);
72
73/*
74 * Buffer pool structures and global (static) var.
75 */
76struct 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
83struct isns_buffer_pool_s {
84	int active;
85	struct isns_buffer_list_s *list_p;
86	pthread_mutex_t mutex;
87};
88
89static struct isns_buffer_pool_s G_buffer_pool;
90
91
92/*
93 * isns_init_buffer_pool - initialize buffer pool for use
94 */
95void
96isns_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 */
119static struct isns_buffer_list_s *
120isns_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 */
145int
146isns_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 */
227void
228isns_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 */
279struct isns_buffer_s *
280isns_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 */
324void
325isns_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 */
360ISNS_TRANS
361isns_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 */
427void
428isns_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 */
484int
485isns_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
531void
532isns_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
545int
546isns_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 */
575int
576isns_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 */
586int
587isns_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 */
632int
633isns_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 */
717uint32_t
718isns_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 */
732void
733isns_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 */
744void
745isns_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 */
756struct isns_pdu_s *
757isns_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 */
773struct isns_pdu_s *
774isns_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 */
798void
799isns_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 */
826int
827isns_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 */
878static struct isns_pdu_s *
879isns_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 */
924static int
925isns_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 */
1000static void
1001isns_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 */
1022static int
1023isns_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 */
1046static int
1047isns_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 */
1137int
1138isns_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 */
1160static void
1161isns_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 */
1203static struct isns_buffer_s *
1204isns_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 */
1214static struct isns_buffer_s *
1215isns_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 */
1232static struct isns_buffer_s *
1233isns_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 */
1249static uint32_t
1250isns_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 */
1262void
1263isns_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