isns_pdu.c revision 1.1 1 /* $NetBSD: isns_pdu.c,v 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 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(>i);
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