1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <strings.h> 31 #include <sys/types.h> 32 #include <sys/socket.h> 33 #include <netinet/in.h> 34 #include <arpa/inet.h> 35 #include <unistd.h> 36 #include <poll.h> 37 #include <errno.h> 38 39 #include "isns_server.h" 40 #include "isns_log.h" 41 #include "isns_pdu.h" 42 43 #define ISNS_MAX_IOVEC 5 44 #define MAX_XID (2^16) 45 #define MAX_RCV_RSP_COUNT 10 /* Maximum number of unmatched xid */ 46 #define ISNS_RCV_RETRY_MAX 2 47 #define IPV4_RSVD_BYTES 10 48 49 /* externs */ 50 #ifdef DEBUG 51 extern void dump_pdu2(isns_pdu_t *); 52 #endif 53 54 /* 55 * local functions. 56 */ 57 58 size_t 59 isns_rcv_pdu( 60 int fd, 61 isns_pdu_t **pdu, 62 size_t *pdu_size, 63 int rcv_timeout 64 ) 65 { 66 int poll_cnt; 67 struct pollfd fds; 68 iovec_t iovec[ISNS_MAX_IOVEC]; 69 isns_pdu_t *tmp_pdu_hdr; 70 ssize_t bytes_received, total_bytes_received = 0; 71 struct msghdr msg; 72 uint8_t *tmp_pdu_data; 73 74 uint16_t payload_len = 0; 75 76 /* initialize to zero */ 77 *pdu = NULL; 78 *pdu_size = 0; 79 80 fds.fd = fd; 81 fds.events = (POLLIN | POLLRDNORM); 82 fds.revents = 0; 83 84 /* Receive the header first */ 85 tmp_pdu_hdr = (isns_pdu_t *)malloc(ISNSP_HEADER_SIZE); 86 if (tmp_pdu_hdr == NULL) { 87 return (0); 88 } 89 (void) memset((void *)&tmp_pdu_hdr[0], 0, ISNSP_HEADER_SIZE); 90 (void) memset((void *)&iovec[0], 0, sizeof (iovec_t)); 91 iovec[0].iov_base = (void *)tmp_pdu_hdr; 92 iovec[0].iov_len = ISNSP_HEADER_SIZE; 93 94 /* Initialization of the message header. */ 95 bzero(&msg, sizeof (msg)); 96 msg.msg_iov = &iovec[0]; 97 /* msg.msg_flags = MSG_WAITALL, */ 98 msg.msg_iovlen = 1; 99 100 /* Poll and receive the pdu header */ 101 poll_cnt = 0; 102 do { 103 int err = poll(&fds, 1, rcv_timeout * 1000); 104 if (err <= 0) { 105 poll_cnt ++; 106 } else { 107 bytes_received = recvmsg(fd, &msg, MSG_WAITALL); 108 break; 109 } 110 } while (poll_cnt < ISNS_RCV_RETRY_MAX); 111 112 if (poll_cnt >= ISNS_RCV_RETRY_MAX) { 113 free(tmp_pdu_hdr); 114 return (0); 115 } 116 117 if (bytes_received <= 0) { 118 free(tmp_pdu_hdr); 119 return (0); 120 } 121 122 total_bytes_received += bytes_received; 123 124 payload_len = ntohs(tmp_pdu_hdr->payload_len); 125 /* Verify the received payload len is within limit */ 126 if (payload_len > ISNSP_MAX_PAYLOAD_SIZE) { 127 free(tmp_pdu_hdr); 128 return (0); 129 } 130 131 /* Proceed to receive additional data. */ 132 tmp_pdu_data = malloc(payload_len); 133 if (tmp_pdu_data == NULL) { 134 free(tmp_pdu_hdr); 135 return (0); 136 } 137 (void) memset((void *)&iovec[0], 0, sizeof (iovec_t)); 138 iovec[0].iov_base = (void *)tmp_pdu_data; 139 iovec[0].iov_len = payload_len; 140 141 /* Initialization of the message header. */ 142 bzero(&msg, sizeof (msg)); 143 msg.msg_iov = &iovec[0]; 144 /* msg.msg_flags = MSG_WAITALL, */ 145 msg.msg_iovlen = 1; 146 147 /* poll and receive the pdu payload */ 148 poll_cnt = 0; 149 do { 150 int err = poll(&fds, 1, rcv_timeout * 1000); 151 if (err <= 0) { 152 poll_cnt ++; 153 } else { 154 bytes_received = recvmsg(fd, &msg, MSG_WAITALL); 155 break; 156 } 157 } while (poll_cnt < ISNS_RCV_RETRY_MAX); 158 159 if (poll_cnt >= ISNS_RCV_RETRY_MAX) { 160 free(tmp_pdu_data); 161 free(tmp_pdu_hdr); 162 return (0); 163 } 164 165 if (bytes_received <= 0) { 166 free(tmp_pdu_data); 167 free(tmp_pdu_hdr); 168 return (0); 169 } 170 171 total_bytes_received += bytes_received; 172 173 *pdu_size = ISNSP_HEADER_SIZE + payload_len; 174 (*pdu) = (isns_pdu_t *)malloc(*pdu_size); 175 if (*pdu == NULL) { 176 *pdu_size = 0; 177 free(tmp_pdu_data); 178 free(tmp_pdu_hdr); 179 return (0); 180 } 181 (*pdu)->version = ntohs(tmp_pdu_hdr->version); 182 (*pdu)->func_id = ntohs(tmp_pdu_hdr->func_id); 183 (*pdu)->payload_len = payload_len; 184 (*pdu)->flags = ntohs(tmp_pdu_hdr->flags); 185 (*pdu)->xid = ntohs(tmp_pdu_hdr->xid); 186 (*pdu)->seq = ntohs(tmp_pdu_hdr->seq); 187 (void) memcpy(&((*pdu)->payload), tmp_pdu_data, payload_len); 188 189 free(tmp_pdu_data); 190 tmp_pdu_data = NULL; 191 free(tmp_pdu_hdr); 192 tmp_pdu_hdr = NULL; 193 194 return (total_bytes_received); 195 } 196 197 int 198 isns_send_pdu( 199 int fd, 200 isns_pdu_t *pdu, 201 size_t pl 202 ) 203 { 204 uint8_t *payload; 205 uint16_t flags; 206 uint16_t seq; 207 iovec_t iovec[ISNS_MAX_IOVEC]; 208 struct msghdr msg = { 0 }; 209 210 size_t send_len; 211 ssize_t bytes_sent; 212 213 214 /* Initialization of the message header. */ 215 msg.msg_iov = &iovec[0]; 216 /* msg.msg_flags = MSG_WAITALL, */ 217 msg.msg_iovlen = 2; 218 219 /* 220 * Initialize the pdu flags. 221 */ 222 flags = ISNS_FLAG_SERVER; 223 flags |= ISNS_FLAG_FIRST_PDU; 224 225 /* 226 * Initialize the pdu sequence id. 227 */ 228 seq = 0; 229 230 iovec[0].iov_base = (void *)pdu; 231 iovec[0].iov_len = (ISNSP_HEADER_SIZE); 232 233 payload = pdu->payload; 234 235 #ifdef DEBUG 236 pdu->flags = htons(flags); 237 pdu->seq = htons(0); 238 pdu->payload_len = htons(pl); 239 dump_pdu2(pdu); 240 #endif 241 242 do { 243 /* set the payload for sending */ 244 iovec[1].iov_base = (void *)payload; 245 246 if (pl > ISNSP_MAX_PAYLOAD_SIZE) { 247 send_len = ISNSP_MAX_PAYLOAD_SIZE; 248 } else { 249 send_len = pl; 250 /* set the last pdu flag */ 251 flags |= ISNS_FLAG_LAST_PDU; 252 } 253 iovec[1].iov_len = send_len; 254 pdu->payload_len = htons(send_len); 255 256 /* set the pdu flags */ 257 pdu->flags = htons(flags); 258 /* set the pdu sequence id */ 259 pdu->seq = htons(seq); 260 261 /* send the packet */ 262 bytes_sent = sendmsg(fd, &msg, 0); 263 264 /* get rid of the first pdu flag */ 265 flags &= ~(ISNS_FLAG_FIRST_PDU); 266 267 /* next part of payload */ 268 payload += send_len; 269 pl -= send_len; 270 271 /* add the length of header for verification */ 272 send_len += ISNSP_HEADER_SIZE; 273 274 /* increase the sequence id by one */ 275 seq ++; 276 } while (bytes_sent == send_len && pl > 0); 277 278 if (bytes_sent == send_len) { 279 return (0); 280 } else { 281 isnslog(LOG_DEBUG, "isns_send_pdu", "sending pdu failed."); 282 return (-1); 283 } 284 } 285 286 #define RSP_PDU_FRAG_SZ (ISNSP_MAX_PDU_SIZE / 10) 287 static int 288 pdu_reset( 289 isns_pdu_t **rsp, 290 size_t *sz 291 ) 292 { 293 int ec = 0; 294 295 if (*rsp == NULL) { 296 *rsp = (isns_pdu_t *)malloc(RSP_PDU_FRAG_SZ); 297 if (*rsp != NULL) { 298 *sz = RSP_PDU_FRAG_SZ; 299 } else { 300 ec = ISNS_RSP_INTERNAL_ERROR; 301 } 302 } 303 304 return (ec); 305 } 306 307 int 308 pdu_reset_rsp( 309 isns_pdu_t **rsp, 310 size_t *pl, 311 size_t *sz 312 ) 313 { 314 int ec = pdu_reset(rsp, sz); 315 316 if (ec == 0) { 317 /* leave space for status code */ 318 *pl = 4; 319 } 320 321 return (ec); 322 } 323 324 int 325 pdu_reset_scn( 326 isns_pdu_t **pdu, 327 size_t *pl, 328 size_t *sz 329 ) 330 { 331 int ec = pdu_reset(pdu, sz); 332 333 if (ec == 0) { 334 *pl = 0; 335 } 336 337 return (ec); 338 } 339 340 int 341 pdu_reset_esi( 342 isns_pdu_t **pdu, 343 size_t *pl, 344 size_t *sz 345 ) 346 { 347 return (pdu_reset_scn(pdu, pl, sz)); 348 } 349 350 int 351 pdu_update_code( 352 isns_pdu_t *pdu, 353 size_t *pl, 354 int code 355 ) 356 { 357 isns_resp_t *resp; 358 359 resp = (isns_resp_t *)pdu->payload; 360 361 /* reset the payload length */ 362 if (code != ISNS_RSP_SUCCESSFUL || *pl == 0) { 363 *pl = 4; 364 } 365 366 resp->status = htonl(code); 367 368 return (0); 369 } 370 371 int 372 pdu_add_tlv( 373 isns_pdu_t **pdu, 374 size_t *pl, 375 size_t *sz, 376 uint32_t attr_id, 377 uint32_t attr_len, 378 void *attr_data, 379 int pflag 380 ) 381 { 382 int ec = 0; 383 384 isns_pdu_t *new_pdu; 385 size_t new_sz; 386 387 isns_tlv_t *attr_tlv; 388 uint8_t *payload_ptr; 389 uint32_t normalized_attr_len; 390 uint64_t attr_tlv_len; 391 392 /* The attribute length must be 4-byte aligned. Section 5.1.3. */ 393 normalized_attr_len = (attr_len % 4) == 0 ? (attr_len) : 394 (attr_len + (4 - (attr_len % 4))); 395 attr_tlv_len = ISNS_TLV_ATTR_ID_LEN 396 + ISNS_TLV_ATTR_LEN_LEN 397 + normalized_attr_len; 398 /* Check if we are going to exceed the maximum PDU length. */ 399 if ((ISNSP_HEADER_SIZE + *pl + attr_tlv_len) > *sz) { 400 new_sz = *sz + RSP_PDU_FRAG_SZ; 401 new_pdu = (isns_pdu_t *)realloc(*pdu, new_sz); 402 if (new_pdu != NULL) { 403 *sz = new_sz; 404 *pdu = new_pdu; 405 } else { 406 ec = ISNS_RSP_INTERNAL_ERROR; 407 return (ec); 408 } 409 } 410 411 attr_tlv = (isns_tlv_t *)malloc(attr_tlv_len); 412 (void) memset((void *)attr_tlv, 0, attr_tlv_len); 413 414 attr_tlv->attr_id = htonl(attr_id); 415 416 switch (attr_id) { 417 case ISNS_DELIMITER_ATTR_ID: 418 break; 419 420 case ISNS_PORTAL_IP_ADDR_ATTR_ID: 421 case ISNS_PG_PORTAL_IP_ADDR_ATTR_ID: 422 /* IPv6 */ 423 ASSERT(attr_len == sizeof (in6_addr_t)); 424 (void) memcpy(attr_tlv->attr_value, attr_data, 425 sizeof (in6_addr_t)); 426 break; 427 428 case ISNS_EID_ATTR_ID: 429 case ISNS_ISCSI_NAME_ATTR_ID: 430 case ISNS_ISCSI_ALIAS_ATTR_ID: 431 case ISNS_PG_ISCSI_NAME_ATTR_ID: 432 (void) memcpy(attr_tlv->attr_value, (char *)attr_data, 433 attr_len); 434 break; 435 436 default: 437 if (attr_len == 8) { 438 if (pflag == 0) { 439 /* 440 * In the iSNS protocol, there is only one 441 * attribute ISNS_TIMESTAMP_ATTR_ID which has 442 * 8 bytes length integer value and when the 443 * function "pdu_add_tlv" is called for adding 444 * the timestamp attribute, the value of 445 * the attribute is always passed in as its 446 * address, i.e. the pflag sets to 1. 447 * So it is an error when we get to this code 448 * path. 449 */ 450 ec = ISNS_RSP_INTERNAL_ERROR; 451 return (ec); 452 } else { 453 *(uint64_t *)attr_tlv->attr_value = 454 *(uint64_t *)attr_data; 455 } 456 } else if (attr_len == 4) { 457 if (pflag == 0) { 458 *(uint32_t *)attr_tlv->attr_value = 459 htonl((uint32_t)attr_data); 460 } else { 461 *(uint32_t *)attr_tlv->attr_value = 462 *(uint32_t *)attr_data; 463 } 464 } 465 break; 466 } 467 468 attr_tlv->attr_len = htonl(normalized_attr_len); 469 /* 470 * Convert the network byte ordered payload length to host byte 471 * ordered for local address calculation. 472 */ 473 payload_ptr = (*pdu)->payload + *pl; 474 (void) memcpy(payload_ptr, attr_tlv, attr_tlv_len); 475 *pl += attr_tlv_len; 476 477 /* 478 * The payload length might exceed the maximum length of a 479 * payload that isnsp allows, we will split the payload and 480 * set the size of each payload before they are sent. 481 */ 482 483 free(attr_tlv); 484 attr_tlv = NULL; 485 486 return (ec); 487 } 488 489 isns_tlv_t * 490 pdu_get_source( 491 isns_pdu_t *pdu 492 ) 493 { 494 uint8_t *payload = &pdu->payload[0]; 495 uint16_t payload_len = pdu->payload_len; 496 isns_tlv_t *tlv = NULL; 497 498 /* response code */ 499 if (pdu->func_id & ISNS_RSP_MASK) { 500 if (payload_len < 4) { 501 return (NULL); 502 } 503 payload += 4; 504 payload_len -= 4; 505 } 506 507 if (payload_len > 8) { 508 tlv = (isns_tlv_t *)payload; 509 tlv->attr_id = ntohl(tlv->attr_id); 510 tlv->attr_len = ntohl(tlv->attr_len); 511 } 512 513 return (tlv); 514 } 515 516 isns_tlv_t * 517 pdu_get_key( 518 isns_pdu_t *pdu, 519 size_t *key_len 520 ) 521 { 522 uint8_t *payload = &pdu->payload[0]; 523 uint16_t payload_len = pdu->payload_len; 524 isns_tlv_t *tlv, *key; 525 526 /* reset */ 527 *key_len = 0; 528 529 /* response code */ 530 if (pdu->func_id & ISNS_RSP_MASK) { 531 if (payload_len <= 4) { 532 return (NULL); 533 } 534 payload += 4; 535 payload_len -= 4; 536 } 537 538 /* skip the soure */ 539 if (payload_len >= 8) { 540 tlv = (isns_tlv_t *)payload; 541 payload += (8 + tlv->attr_len); 542 payload_len -= (8 + tlv->attr_len); 543 key = (isns_tlv_t *)payload; 544 while (payload_len >= 8) { 545 tlv = (isns_tlv_t *)payload; 546 tlv->attr_id = ntohl(tlv->attr_id); 547 tlv->attr_len = ntohl(tlv->attr_len); 548 if (tlv->attr_id == ISNS_DELIMITER_ATTR_ID) { 549 break; 550 } 551 *key_len += (8 + tlv->attr_len); 552 payload += (8 + tlv->attr_len); 553 payload_len -= (8 + tlv->attr_len); 554 } 555 } 556 557 if (*key_len >= 8) { 558 return (key); 559 } 560 561 return (NULL); 562 } 563 564 isns_tlv_t * 565 pdu_get_operand( 566 isns_pdu_t *pdu, 567 size_t *op_len 568 ) 569 { 570 uint8_t *payload = &pdu->payload[0]; 571 uint16_t payload_len = pdu->payload_len; 572 isns_tlv_t *tlv, *op = NULL; 573 int found_op = 0; 574 575 /* reset */ 576 *op_len = 0; 577 578 /* response code */ 579 if (pdu->func_id & ISNS_RSP_MASK) { 580 if (payload_len < 4) { 581 return (NULL); 582 } 583 payload += 4; 584 payload_len -= 4; 585 } 586 587 /* tlvs */ 588 while (payload_len >= 8) { 589 tlv = (isns_tlv_t *)payload; 590 if (found_op != 0) { 591 tlv->attr_id = ntohl(tlv->attr_id); 592 tlv->attr_len = ntohl(tlv->attr_len); 593 payload += (8 + tlv->attr_len); 594 payload_len -= (8 + tlv->attr_len); 595 } else { 596 payload += (8 + tlv->attr_len); 597 payload_len -= (8 + tlv->attr_len); 598 if (tlv->attr_id == ISNS_DELIMITER_ATTR_ID) { 599 /* found it */ 600 op = (isns_tlv_t *)payload; 601 *op_len = payload_len; 602 found_op = 1; 603 } 604 } 605 } 606 607 if (*op_len >= 8) { 608 return (op); 609 } 610 611 return (NULL); 612 } 613