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(>i); 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