1 /* 2 * IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. 3 * By downloading, copying, installing or using the software you agree 4 * to this license. If you do not agree to this license, do not 5 * download, install, copy or use the software. 6 * 7 * Intel License Agreement 8 * 9 * Copyright (c) 2000, Intel Corporation 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 16 * -Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 19 * -Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the 22 * distribution. 23 * 24 * -The name of Intel Corporation may not be used to endorse or 25 * promote products derived from this software without specific prior 26 * written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 31 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL 32 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 35 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 36 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 37 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 38 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39 * SUCH DAMAGE. 40 */ 41 #include "config.h" 42 43 #include <sys/types.h> 44 #include <sys/param.h> 45 46 #include <assert.h> 47 #include <stdlib.h> 48 49 #ifdef HAVE_NETINET_TCP_H 50 #include <netinet/tcp.h> 51 #endif 52 53 #ifdef HAVE_SYS_UIO_H 54 #include <sys/uio.h> 55 #endif 56 57 #ifdef HAVE_SYS_SOCKET_H 58 #include <sys/socket.h> 59 #endif 60 61 #ifdef HAVE_NETINET_IN_H 62 #include <netinet/in.h> 63 #endif 64 65 #ifdef HAVE_STRING_H 66 #include <string.h> 67 #endif 68 69 #ifdef HAVE_SIGNAL_H 70 #include <signal.h> 71 #endif 72 73 #ifdef HAVE_SYSLOG_H 74 #include <syslog.h> 75 #endif 76 77 #ifdef HAVE_ERRNO_H 78 #include <errno.h> 79 #endif 80 81 #ifdef HAVE_NETDB_H 82 #include <netdb.h> 83 #endif 84 85 #ifdef HAVE_ARPA_INET_H 86 #include <arpa/inet.h> 87 #endif 88 89 #ifdef HAVE_INTTYPES_H 90 #include <inttypes.h> 91 #endif 92 93 94 #include "iscsiprotocol.h" 95 #include "conffile.h" 96 #include "storage.h" 97 #include "target.h" 98 #include "device.h" 99 #include "iscsi-md5.h" 100 #include "parameters.h" 101 #include "iscsi.h" 102 103 enum { 104 TARGET_SHUT_DOWN = 0, 105 TARGET_INITIALIZING = 1, 106 TARGET_INITIALIZED = 2, 107 TARGET_SHUTTING_DOWN = 3 108 }; 109 110 /*********** 111 * Private * 112 ***********/ 113 114 static target_session_t *g_session; 115 static iscsi_queue_t g_session_q; 116 static iscsi_mutex_t g_session_q_mutex; 117 118 /********************* 119 * Private Functions * 120 *********************/ 121 122 static char * 123 get_iqn(target_session_t *sess, uint32_t t, char *buf, size_t size) 124 { 125 targv_t *targv; 126 127 targv = sess->target->lunv; 128 if (targv->v[t].iqn != NULL) { 129 (void) strlcpy(buf, targv->v[t].iqn, size); 130 return buf; 131 } 132 (void) snprintf(buf, size, "%s:%s", 133 iscsi_target_getvar(sess->target, "iqn"), 134 targv->v[t].target); 135 return buf; 136 } 137 138 static int 139 reject_t(target_session_t * sess, uint8_t *header, uint8_t reason) 140 { 141 iscsi_reject_t reject; 142 uint8_t rsp_header[ISCSI_HEADER_LEN]; 143 144 iscsi_err(__FILE__, __LINE__, "reject %x\n", reason); 145 reject.reason = reason; 146 reject.length = ISCSI_HEADER_LEN; 147 reject.StatSN = ++(sess->StatSN); 148 reject.ExpCmdSN = sess->ExpCmdSN; 149 reject.MaxCmdSN = sess->MaxCmdSN; 150 reject.DataSN = 0; /* SNACK not yet implemented */ 151 152 if (iscsi_reject_encap(rsp_header, &reject) != 0) { 153 iscsi_err(__FILE__, __LINE__, 154 "iscsi_reject_encap() failed\n"); 155 return -1; 156 } 157 if (iscsi_sock_send_header_and_data(sess->sock, rsp_header, 158 ISCSI_HEADER_LEN, header, ISCSI_HEADER_LEN, 0) != 159 2 * ISCSI_HEADER_LEN) { 160 iscsi_err(__FILE__, __LINE__, 161 "iscsi_sock_send_header_and_data() failed\n"); 162 return -1; 163 } 164 return 0; 165 } 166 167 static int 168 scsi_command_t(target_session_t *sess, uint8_t *header) 169 { 170 iscsi_scsi_cmd_args_t scsi_cmd; 171 iscsi_read_data_t data; 172 iscsi_scsi_rsp_t scsi_rsp; 173 target_cmd_t cmd; 174 uint32_t DataSN = 0; 175 uint8_t rsp_header[ISCSI_HEADER_LEN]; 176 struct iovec *sg_new = NULL; 177 int result; 178 179 (void) memset(&scsi_cmd, 0x0, sizeof(scsi_cmd)); 180 scsi_cmd.ahs = NULL; 181 if (iscsi_scsi_cmd_decap(header, &scsi_cmd) != 0) { 182 iscsi_err(__FILE__, __LINE__, 183 "iscsi_scsi_cmd_decap() failed\n"); 184 result = -1; 185 goto out; 186 } 187 iscsi_trace(TRACE_ISCSI_DEBUG, 188 "session %d: SCSI Command (CmdSN %u, op %#x)\n", 189 sess->id, scsi_cmd.CmdSN, scsi_cmd.cdb[0]); 190 191 /* For Non-immediate commands, the CmdSN should be between ExpCmdSN */ 192 /* and MaxCmdSN, inclusive of both. Otherwise, ignore the command */ 193 if (!scsi_cmd.immediate && 194 (scsi_cmd.CmdSN < sess->ExpCmdSN || 195 scsi_cmd.CmdSN > sess->MaxCmdSN)) { 196 iscsi_err(__FILE__, __LINE__, 197 "CmdSN(%d) of SCSI Command not valid, " 198 "ExpCmdSN(%d) MaxCmdSN(%d). Ignoring the command\n", 199 scsi_cmd.CmdSN, sess->ExpCmdSN, sess->MaxCmdSN); 200 result = 0; 201 goto out; 202 } 203 /* Arg check. */ 204 scsi_cmd.attr = 0; /* Temp fix FIXME */ 205 /* 206 * RETURN_NOT_EQUAL("ATTR (FIX ME)", scsi_cmd.attr, 0, NO_CLEANUP, 207 * -1); 208 */ 209 210 /* Check Numbering */ 211 212 if (scsi_cmd.CmdSN != sess->ExpCmdSN) { 213 iscsi_warn(__FILE__, __LINE__, 214 "Expected CmdSN %d, got %d. " 215 "(ignoring and resetting expectations)\n", 216 sess->ExpCmdSN, scsi_cmd.CmdSN); 217 sess->ExpCmdSN = scsi_cmd.CmdSN; 218 } 219 /* Check Transfer Lengths */ 220 if (sess->sess_params.first_burst_length 221 && (scsi_cmd.length > sess->sess_params.first_burst_length)) { 222 iscsi_err(__FILE__, __LINE__, 223 "scsi_cmd.length (%u) > FirstBurstLength (%u)\n", 224 scsi_cmd.length, sess->sess_params.first_burst_length); 225 scsi_cmd.status = 0x02; 226 scsi_cmd.length = 0; 227 goto response; 228 } 229 if (sess->sess_params.max_dataseg_len && 230 scsi_cmd.length > sess->sess_params.max_dataseg_len) { 231 iscsi_err(__FILE__, __LINE__, 232 "scsi_cmd.length (%u) > MaxRecvDataSegmentLength " 233 "(%u)\n", 234 scsi_cmd.length, sess->sess_params.max_dataseg_len); 235 result = -1; 236 goto out; 237 } 238 239 #if 0 240 /* commented out in original Intel reference code */ 241 if (scsi_cmd.final && scsi_cmd.output) { 242 RETURN_NOT_EQUAL("Length", scsi_cmd.length, 243 scsi_cmd.trans_len, NO_CLEANUP, -1); 244 } 245 #endif 246 247 /* Read AHS. Need to optimize/clean this. */ 248 /* We should not be calling malloc(). */ 249 /* We need to check for properly formated AHS segments. */ 250 251 if (scsi_cmd.ahs_len) { 252 uint32_t ahs_len; 253 uint8_t *ahs_ptr; 254 uint8_t ahs_type; 255 256 iscsi_trace(TRACE_ISCSI_DEBUG, 257 "reading %u bytes AHS\n", scsi_cmd.ahs_len); 258 scsi_cmd.ahs = iscsi_malloc_atomic((unsigned)scsi_cmd.ahs_len); 259 if (scsi_cmd.ahs == NULL) { 260 iscsi_err(__FILE__, __LINE__, 261 "iscsi_malloc_atomic() failed\n"); 262 result = -1; 263 goto out; 264 } 265 if (iscsi_sock_msg(sess->sock, 0, (unsigned)scsi_cmd.ahs_len, 266 scsi_cmd.ahs, 0) != scsi_cmd.ahs_len) { 267 iscsi_err(__FILE__, __LINE__, 268 "iscsi_sock_msg() failed\n"); 269 result = -1; 270 goto out; 271 } 272 iscsi_trace(TRACE_ISCSI_DEBUG, 273 "read %u bytes AHS\n", scsi_cmd.ahs_len); 274 for (ahs_ptr = scsi_cmd.ahs; 275 ahs_ptr < (scsi_cmd.ahs + scsi_cmd.ahs_len - 1) ; 276 ahs_ptr += ahs_len) { 277 ahs_len = ISCSI_NTOHS(*((uint16_t *) (void *)ahs_ptr)); 278 if (ahs_len == 0) { 279 iscsi_err(__FILE__, __LINE__, 280 "Zero ahs_len\n"); 281 result = -1; 282 goto out; 283 } 284 switch (ahs_type = *(ahs_ptr + 2)) { 285 case ISCSI_AHS_EXTENDED_CDB: 286 iscsi_trace(TRACE_ISCSI_DEBUG, 287 "Got ExtendedCDB AHS - %u bytes extra " 288 "CDB)\n", ahs_len - 1); 289 scsi_cmd.ext_cdb = ahs_ptr + 4; 290 break; 291 case ISCSI_AHS_BIDI_READ: 292 scsi_cmd.bidi_trans_len = 293 ISCSI_NTOHL(*((uint32_t *)(void *) 294 (ahs_ptr + 4))); 295 *((uint32_t *)(void *)(ahs_ptr + 4)) = 296 scsi_cmd.bidi_trans_len; 297 iscsi_trace(TRACE_ISCSI_DEBUG, 298 "Got Bidirectional Read AHS " 299 "(expected read length %u)\n", 300 scsi_cmd.bidi_trans_len); 301 break; 302 default: 303 iscsi_err(__FILE__, __LINE__, 304 "unknown AHS type %x\n", ahs_type); 305 result = -1; 306 goto out; 307 } 308 } 309 iscsi_trace(TRACE_ISCSI_DEBUG, 310 "done parsing %u bytes AHS\n", scsi_cmd.ahs_len); 311 } else { 312 iscsi_trace(TRACE_ISCSI_DEBUG, "no AHS to read\n"); 313 scsi_cmd.ahs = NULL; 314 } 315 316 sess->ExpCmdSN++; 317 sess->MaxCmdSN++; 318 319 /* Execute cdb. device_command() will set scsi_cmd.input if 320 * there is input data and set the length of the input to 321 * either scsi_cmd.trans_len or scsi_cmd.bidi_trans_len, 322 * depending on whether scsi_cmd.output was set. */ 323 if (scsi_cmd.input) { 324 scsi_cmd.send_data = sess->buff; 325 } 326 scsi_cmd.input = 0; 327 cmd.scsi_cmd = &scsi_cmd; 328 cmd.callback = NULL; 329 if (device_command(sess, &cmd) != 0) { 330 iscsi_err(__FILE__, __LINE__, 331 "device_command() failed\n"); 332 result = -1; 333 goto out; 334 } 335 /* Send any input data */ 336 337 scsi_cmd.bytes_sent = 0; 338 if (!scsi_cmd.status && scsi_cmd.input) { 339 struct iovec sg_singleton; 340 struct iovec *sg, *sg_orig; 341 int sg_len_orig, sg_len; 342 uint32_t offset, trans_len; 343 int fragment_flag = 0; 344 int offset_inc; 345 346 if (scsi_cmd.output) { 347 iscsi_trace(TRACE_ISCSI_DEBUG, 348 "sending %u bytes bi-directional input data\n", 349 scsi_cmd.bidi_trans_len); 350 trans_len = scsi_cmd.bidi_trans_len; 351 } else { 352 trans_len = scsi_cmd.trans_len; 353 } 354 iscsi_trace(TRACE_ISCSI_DEBUG, 355 "sending %u bytes input data as separate PDUs\n", 356 trans_len); 357 358 if (scsi_cmd.send_sg_len) { 359 sg_orig = (struct iovec *)(void *)scsi_cmd.send_data; 360 sg_len_orig = scsi_cmd.send_sg_len; 361 } else { 362 sg_len_orig = 1; 363 sg_singleton.iov_base = scsi_cmd.send_data; 364 sg_singleton.iov_len = trans_len; 365 sg_orig = &sg_singleton; 366 } 367 sg = sg_orig; 368 sg_len = sg_len_orig; 369 370 offset_inc = (sess->sess_params.max_dataseg_len) ? 371 sess->sess_params.max_dataseg_len : trans_len; 372 373 for (offset = 0; offset < trans_len; offset += offset_inc) { 374 (void) memset(&data, 0x0, sizeof(data)); 375 data.length = (sess->sess_params.max_dataseg_len) ? 376 MIN(trans_len - offset, 377 sess->sess_params.max_dataseg_len) : 378 trans_len - offset; 379 if (data.length != trans_len) { 380 if (!fragment_flag) { 381 sg_new = iscsi_malloc_atomic(sizeof(struct iovec) * sg_len_orig); 382 if (sg_new == NULL) { 383 iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n"); 384 result = -1; 385 goto out; 386 } 387 fragment_flag++; 388 } 389 sg = sg_new; 390 sg_len = sg_len_orig; 391 (void) memcpy(sg, sg_orig, sizeof(struct iovec) * sg_len_orig); 392 if (modify_iov(&sg, &sg_len, offset, data.length) != 0) { 393 iscsi_err(__FILE__, __LINE__, "modify_iov() failed\n"); 394 result = -1; 395 goto out; 396 } 397 } 398 iscsi_trace(TRACE_ISCSI_DEBUG, "sending read data PDU (offset %u, len %u)\n", offset, data.length); 399 if (offset + data.length == trans_len) { 400 data.final = 1; 401 402 if (sess->UsePhaseCollapsedRead) { 403 data.status = 1; 404 data.status = scsi_cmd.status; 405 data.StatSN = ++(sess->StatSN); 406 iscsi_trace(TRACE_ISCSI_DEBUG, "status %#x collapsed into last data PDU\n", data.status); 407 } else { 408 iscsi_trace(TRACE_ISCSI_DEBUG, "NOT collapsing status with last data PDU\n"); 409 } 410 } else if (offset + data.length > trans_len) { 411 iscsi_err(__FILE__, __LINE__, "offset+data.length > trans_len??\n"); 412 result = -1; 413 goto out; 414 } 415 data.task_tag = scsi_cmd.tag; 416 data.ExpCmdSN = sess->ExpCmdSN; 417 data.MaxCmdSN = sess->MaxCmdSN; 418 data.DataSN = DataSN++; 419 data.offset = offset; 420 if (iscsi_read_data_encap(rsp_header, &data) != 0) { 421 iscsi_err(__FILE__, __LINE__, "iscsi_read_data_encap() failed\n"); 422 result = -1; 423 goto out; 424 } 425 if ((uint32_t)iscsi_sock_send_header_and_data(sess->sock, rsp_header, ISCSI_HEADER_LEN, sg, data.length, sg_len) 426 != ISCSI_HEADER_LEN + data.length) { 427 iscsi_err(__FILE__, __LINE__, "iscsi_sock_send_header_and_data() failed\n"); 428 result = -1; 429 goto out; 430 } 431 scsi_cmd.bytes_sent += data.length; 432 iscsi_trace(TRACE_ISCSI_DEBUG, "sent read data PDU ok (offset %u, len %u)\n", data.offset, data.length); 433 } 434 iscsi_trace(TRACE_ISCSI_DEBUG, "successfully sent %u bytes read data\n", trans_len); 435 } 436 /* 437 * Send a response PDU if 438 * 439 * 1) we're not using phase collapsed input (and status was good) 440 * 2) we are using phase collapsed input, but there was no input data (e.g., TEST UNIT READY) 441 * 3) command had non-zero status and possible sense data 442 */ 443 response: 444 if (!sess->UsePhaseCollapsedRead || !scsi_cmd.length || scsi_cmd.status) { 445 iscsi_trace(TRACE_ISCSI_DEBUG, "sending SCSI response PDU\n"); 446 (void) memset(&scsi_rsp, 0x0, sizeof(scsi_rsp)); 447 scsi_rsp.length = scsi_cmd.status ? scsi_cmd.length : 0; 448 scsi_rsp.tag = scsi_cmd.tag; 449 /* If r2t send, then the StatSN is already incremented */ 450 if (sess->StatSN < scsi_cmd.ExpStatSN) { 451 ++sess->StatSN; 452 } 453 scsi_rsp.StatSN = sess->StatSN; 454 scsi_rsp.ExpCmdSN = sess->ExpCmdSN; 455 scsi_rsp.MaxCmdSN = sess->MaxCmdSN; 456 scsi_rsp.ExpDataSN = (!scsi_cmd.status && scsi_cmd.input) ? DataSN : 0; 457 scsi_rsp.response = 0x00; /* iSCSI response */ 458 scsi_rsp.status = scsi_cmd.status; /* SCSI status */ 459 if (iscsi_scsi_rsp_encap(rsp_header, &scsi_rsp) != 0) { 460 iscsi_err(__FILE__, __LINE__, "iscsi_scsi_rsp_encap() failed\n"); 461 result = -1; 462 goto out; 463 } 464 if ((uint32_t)iscsi_sock_send_header_and_data(sess->sock, rsp_header, ISCSI_HEADER_LEN, 465 scsi_cmd.send_data, scsi_rsp.length, scsi_cmd.send_sg_len) 466 != ISCSI_HEADER_LEN + scsi_rsp.length) { 467 iscsi_err(__FILE__, __LINE__, 468 "iscsi_sock_send_header_and_data() failed\n"); 469 result = -1; 470 goto out; 471 } 472 /* Make sure all data was transferred */ 473 474 if (scsi_cmd.output) { 475 if (scsi_cmd.bytes_recv != scsi_cmd.trans_len) { 476 iscsi_err(__FILE__, __LINE__, 477 "scsi_cmd.bytes_recv"); 478 result = -1; 479 goto out; 480 } 481 if (scsi_cmd.input) { 482 if (scsi_cmd.bytes_sent != 483 scsi_cmd.bidi_trans_len) { 484 iscsi_err(__FILE__, __LINE__, 485 "scsi_cmd.bytes_sent"); 486 result = -1; 487 goto out; 488 } 489 } 490 } else { 491 if (scsi_cmd.input) { 492 if (scsi_cmd.bytes_sent != scsi_cmd.trans_len) { 493 iscsi_err(__FILE__, __LINE__, 494 "scsi_cmd.bytes_sent"); 495 result = -1; 496 goto out; 497 } 498 } 499 } 500 } 501 502 /* Device callback after command has completed */ 503 if (cmd.callback) { 504 iscsi_trace(TRACE_ISCSI_DEBUG, "issuing device callback\n"); 505 if ((*cmd.callback)(cmd.callback_arg) != 0) { 506 iscsi_err(__FILE__, __LINE__, 507 "device callback failed\n"); 508 result = -1; 509 goto out; 510 } 511 } 512 result = 0; 513 out: 514 if (scsi_cmd.ahs != NULL) { \ 515 iscsi_free_atomic(scsi_cmd.ahs); \ 516 } \ 517 if (sg_new != NULL) { 518 iscsi_free_atomic(sg_new); 519 } 520 return result; 521 } 522 523 static int 524 task_command_t(target_session_t * sess, uint8_t *header) 525 { 526 iscsi_task_cmd_t cmd; 527 iscsi_task_rsp_t rsp; 528 uint8_t rsp_header[ISCSI_HEADER_LEN]; 529 530 /* Get & check args */ 531 532 if (iscsi_task_cmd_decap(header, &cmd) != 0) { 533 iscsi_err(__FILE__, __LINE__, 534 "iscsi_task_cmd_decap() failed\n"); 535 return -1; 536 } 537 if (cmd.CmdSN != sess->ExpCmdSN) { 538 iscsi_warn(__FILE__, __LINE__, 539 "Expected CmdSN %d, got %d. " 540 "(ignoring and resetting expectations)\n", 541 cmd.CmdSN, sess->ExpCmdSN); 542 sess->ExpCmdSN = cmd.CmdSN; 543 } 544 sess->MaxCmdSN++; 545 546 (void) memset(&rsp, 0x0, sizeof(rsp)); 547 rsp.response = ISCSI_TASK_RSP_FUNCTION_COMPLETE; 548 549 switch (cmd.function) { 550 case ISCSI_TASK_CMD_ABORT_TASK: 551 printf("ISCSI_TASK_CMD_ABORT_TASK\n"); 552 break; 553 case ISCSI_TASK_CMD_ABORT_TASK_SET: 554 printf("ISCSI_TASK_CMD_ABORT_TASK_SET\n"); 555 break; 556 case ISCSI_TASK_CMD_CLEAR_ACA: 557 printf("ISCSI_TASK_CMD_CLEAR_ACA\n"); 558 break; 559 case ISCSI_TASK_CMD_CLEAR_TASK_SET: 560 printf("ISCSI_TASK_CMD_CLEAR_TASK_SET\n"); 561 break; 562 case ISCSI_TASK_CMD_LOGICAL_UNIT_RESET: 563 printf("ISCSI_TASK_CMD_LOGICAL_UNIT_RESET\n"); 564 break; 565 case ISCSI_TASK_CMD_TARGET_WARM_RESET: 566 printf("ISCSI_TASK_CMD_TARGET_WARM_RESET\n"); 567 break; 568 case ISCSI_TASK_CMD_TARGET_COLD_RESET: 569 printf("ISCSI_TASK_CMD_TARGET_COLD_RESET\n"); 570 break; 571 case ISCSI_TASK_CMD_TARGET_REASSIGN: 572 printf("ISCSI_TASK_CMD_TARGET_REASSIGN\n"); 573 break; 574 default: 575 iscsi_err(__FILE__, __LINE__, "Unknown task function %d\n", cmd.function); 576 rsp.response = ISCSI_TASK_RSP_REJECTED; 577 } 578 579 rsp.tag = cmd.tag; 580 rsp.StatSN = ++(sess->StatSN); 581 rsp.ExpCmdSN = sess->ExpCmdSN; 582 rsp.MaxCmdSN = sess->MaxCmdSN; 583 584 if (iscsi_task_rsp_encap(rsp_header, &rsp) != 0) { 585 iscsi_err(__FILE__, __LINE__, "iscsi_task_cmd_decap() failed\n"); 586 return -1; 587 } 588 if (iscsi_sock_msg(sess->sock, 1, ISCSI_HEADER_LEN, rsp_header, 0) != ISCSI_HEADER_LEN) { 589 iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed\n"); 590 return -1; 591 592 } 593 return 0; 594 } 595 596 static int 597 nop_out_t(target_session_t * sess, uint8_t *header) 598 { 599 iscsi_nop_out_args_t nop_out; 600 char *ping_data = NULL; 601 602 if (iscsi_nop_out_decap(header, &nop_out) != 0) { 603 iscsi_err(__FILE__, __LINE__, "iscsi_nop_out_decap() failed\n"); 604 return -1; 605 } 606 if (nop_out.CmdSN != sess->ExpCmdSN) { 607 iscsi_warn(__FILE__, __LINE__, "Expected CmdSN %d, got %d. (ignoring and resetting expectations)\n", 608 nop_out.CmdSN, sess->ExpCmdSN); 609 sess->ExpCmdSN = nop_out.CmdSN; 610 } 611 /* TODO Clarify whether we need to update the CmdSN */ 612 /* sess->ExpCmdSN++; */ 613 /* sess->MaxCmdSN++; */ 614 615 if (nop_out.length) { 616 iscsi_trace(TRACE_ISCSI_DEBUG, "reading %u bytes ping data\n", nop_out.length); 617 if ((ping_data = iscsi_malloc(nop_out.length)) == NULL) { 618 iscsi_err(__FILE__, __LINE__, "iscsi_malloc() failed\n"); 619 return -1; 620 } 621 if ((uint32_t)iscsi_sock_msg(sess->sock, 0, nop_out.length, ping_data, 0) != nop_out.length) { 622 iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed\n"); 623 if (ping_data) { 624 iscsi_free(ping_data); 625 } 626 return -1; 627 } 628 iscsi_trace(TRACE_ISCSI_DEBUG, "successfully read %u bytes ping data:\n", nop_out.length); 629 iscsi_print_buffer(ping_data, nop_out.length); 630 } 631 if (nop_out.tag != 0xffffffff) { 632 iscsi_nop_in_args_t nop_in; 633 uint8_t rsp_header[ISCSI_HEADER_LEN]; 634 635 iscsi_trace(TRACE_ISCSI_DEBUG, "sending %u bytes ping response\n", nop_out.length); 636 (void) memset(&nop_in, 0x0, sizeof(nop_in)); 637 nop_in.length = nop_out.length; 638 nop_in.lun = nop_out.lun; 639 nop_in.tag = nop_out.tag; 640 nop_in.transfer_tag = 0xffffffff; 641 nop_in.StatSN = ++(sess->StatSN); 642 nop_in.ExpCmdSN = sess->ExpCmdSN; 643 nop_in.MaxCmdSN = sess->MaxCmdSN; 644 645 if (iscsi_nop_in_encap(rsp_header, &nop_in) != 0) { 646 iscsi_err(__FILE__, __LINE__, "iscsi_nop_in_encap() failed\n"); 647 if (ping_data) { 648 iscsi_free(ping_data); 649 } 650 return -1; 651 } 652 if ((uint32_t)iscsi_sock_send_header_and_data(sess->sock, rsp_header, ISCSI_HEADER_LEN, 653 ping_data, nop_in.length, 0) != ISCSI_HEADER_LEN + nop_in.length) { 654 iscsi_err(__FILE__, __LINE__, "iscsi_sock_send_header_and_data() failed\n"); 655 if (ping_data) { 656 iscsi_free(ping_data); 657 } 658 return -1; 659 } 660 iscsi_trace(TRACE_ISCSI_DEBUG, "successfully sent %u bytes ping response\n", nop_out.length); 661 } 662 if (ping_data) { 663 iscsi_free(ping_data); 664 } 665 return 0; 666 } 667 668 /* 669 * text_command_t 670 */ 671 672 static int 673 text_command_t(target_session_t * sess, uint8_t *header) 674 { 675 iscsi_text_cmd_args_t text_cmd; 676 iscsi_text_rsp_args_t text_rsp; 677 unsigned len_in; 678 uint32_t i; 679 uint8_t rsp_header[ISCSI_HEADER_LEN]; 680 targv_t *targv; 681 char *text_in = NULL; 682 char *text_out = NULL; 683 char buf[BUFSIZ]; 684 int len_out = 0; 685 686 #define TC_CLEANUP do { \ 687 if (text_in != NULL) { \ 688 iscsi_free_atomic(text_in); \ 689 } \ 690 if (text_out != NULL) { \ 691 iscsi_free_atomic(text_out); \ 692 } \ 693 } while (/* CONSTCOND */ 0) 694 #define TC_ERROR { \ 695 TC_CLEANUP; \ 696 return -1; \ 697 } 698 /* Get text args */ 699 700 if (iscsi_text_cmd_decap(header, &text_cmd) != 0) { 701 iscsi_err(__FILE__, __LINE__, "iscsi_text_cmd_decap() failed\n"); 702 return -1; 703 } 704 /* Check args & update numbering */ 705 #if 0 706 RETURN_NOT_EQUAL("Continue", text_cmd.cont, 0, NO_CLEANUP, -1); 707 RETURN_NOT_EQUAL("CmdSN", text_cmd.CmdSN, sess->ExpCmdSN, NO_CLEANUP, -1); 708 #else 709 if (text_cmd.cont != 0) { 710 iscsi_err(__FILE__, __LINE__, "Continue"); 711 NO_CLEANUP; 712 return -1; 713 } 714 if (text_cmd.CmdSN != sess->ExpCmdSN) { 715 iscsi_err(__FILE__, __LINE__, "CmdSN"); 716 NO_CLEANUP; 717 return -1; 718 } 719 #endif 720 721 sess->ExpCmdSN++; 722 sess->MaxCmdSN++; 723 724 if ((text_out = iscsi_malloc_atomic(2048)) == NULL) { 725 iscsi_err(__FILE__, __LINE__, 726 "iscsi_malloc_atomic() failed\n"); 727 return -1; 728 } 729 730 /* Read text parameters */ 731 if ((len_in = text_cmd.length) != 0) { 732 iscsi_parameter_t *ptr; 733 734 if ((text_in = iscsi_malloc_atomic(len_in + 1)) == NULL) { 735 iscsi_err(__FILE__, __LINE__, 736 "iscsi_malloc_atomic() failed\n"); 737 TC_CLEANUP; 738 return -1; 739 } 740 iscsi_trace(TRACE_ISCSI_DEBUG, 741 "reading %u bytes text parameters\n", len_in); 742 if ((unsigned)iscsi_sock_msg(sess->sock, 0, len_in, text_in, 743 0) != len_in) { 744 iscsi_err(__FILE__, __LINE__, 745 "iscsi_sock_msg() failed\n"); 746 TC_CLEANUP; 747 return -1; 748 } 749 text_in[len_in] = 0x0; 750 PARAM_TEXT_PARSE(sess->params, &sess->sess_params.cred, 751 text_in, (int) len_in, text_out, 752 (int *)(void *)&len_out, 2048, 0, TC_ERROR); 753 754 /* 755 * Handle exceptional cases not covered by parameters.c 756 * (e.g., SendTargets) 757 */ 758 if ((ptr = param_get(sess->params, "SendTargets")) == NULL) { 759 iscsi_err(__FILE__, __LINE__, 760 "param_get() failed\n"); 761 TC_CLEANUP; 762 return -1; 763 } 764 if (ptr->rx_offer) { 765 if (ptr->offer_rx && 766 strcmp(ptr->offer_rx, "All") == 0 && 767 !param_equiv(sess->params, "SessionType", 768 "Discovery")) { 769 iscsi_trace(TRACE_ISCSI_DEBUG, 770 "Rejecting SendTargets=All in a " 771 "non Discovery session\n"); 772 PARAM_TEXT_ADD(sess->params, "SendTargets", 773 "Reject", text_out, &len_out, 2048, 774 0, TC_ERROR); 775 } else { 776 targv = sess->target->lunv; 777 for (i = 0 ; i < targv->c ; i++) { 778 if (sess->address_family == 6 || 779 (sess->address_family == 4 && 780 allow_netmask(targv->v[i].mask, 781 sess->initiator))) { 782 (void) get_iqn(sess, i, buf, 783 sizeof(buf)); 784 PARAM_TEXT_ADD(sess->params, 785 "TargetName", buf, 786 text_out, &len_out, 787 2048, 0, TC_ERROR); 788 PARAM_TEXT_ADD(sess->params, 789 "TargetAddress", 790 iscsi_target_getvar(sess->target, "target address"), 791 text_out, &len_out, 792 2048, 0, TC_ERROR); 793 } else { 794 #ifdef HAVE_SYSLOG_H 795 syslog(LOG_INFO, 796 "WARNING: attempt to " 797 "discover targets from " 798 "%s (not allowed by %s)" 799 " has been rejected", 800 sess->initiator, 801 targv->v[0].mask); 802 #endif 803 } 804 } 805 } 806 ptr->rx_offer = 0; 807 } 808 /* Parse outgoing offer */ 809 810 if (len_out) { 811 PARAM_TEXT_PARSE(sess->params, 812 &sess->sess_params.cred, text_out, len_out, 813 NULL, NULL, 2048, 1, TC_ERROR); 814 } 815 } 816 if (sess->IsFullFeature) { 817 set_session_parameters(sess->params, &sess->sess_params); 818 } 819 /* Send response */ 820 821 text_rsp.final = text_cmd.final; 822 text_rsp.cont = 0; 823 text_rsp.length = len_out; 824 text_rsp.lun = text_cmd.lun; 825 text_rsp.tag = text_cmd.tag; 826 text_rsp.transfer_tag = (text_rsp.final) ? 0xffffffff : 0x1234; 827 text_rsp.StatSN = ++(sess->StatSN); 828 text_rsp.ExpCmdSN = sess->ExpCmdSN; 829 text_rsp.MaxCmdSN = sess->MaxCmdSN; 830 if (iscsi_text_rsp_encap(rsp_header, &text_rsp) != 0) { 831 iscsi_err(__FILE__, __LINE__, 832 "iscsi_text_rsp_encap() failed\n"); 833 TC_CLEANUP; 834 return -1; 835 } 836 if (iscsi_sock_msg(sess->sock, 1, ISCSI_HEADER_LEN, rsp_header, 0) != 837 ISCSI_HEADER_LEN) { 838 iscsi_err(__FILE__, __LINE__, 839 "iscsi_sock_msg() failed\n"); 840 TC_CLEANUP; 841 return -1; 842 } 843 if (len_out && iscsi_sock_msg(sess->sock, 1, (unsigned) len_out, 844 text_out, 0) != len_out) { 845 iscsi_err(__FILE__, __LINE__, 846 "iscsi_sock_msg() failed\n"); 847 TC_CLEANUP; 848 return -1; 849 } 850 TC_CLEANUP; 851 return 0; 852 } 853 854 /* given a target's iqn, find the relevant target that we're exporting */ 855 int 856 find_target_iqn(target_session_t *sess) 857 { 858 uint32_t i; 859 targv_t *targv; 860 char buf[BUFSIZ]; 861 862 targv = sess->target->lunv; 863 for (i = 0 ; i < targv->c ; i++) { 864 if (param_equiv(sess->params, "TargetName", 865 get_iqn(sess, i, buf, sizeof(buf)))) { 866 return sess->d = i; 867 } 868 } 869 return -1; 870 } 871 872 /* given a tsih, find the relevant target that we're exporting */ 873 int 874 find_target_tsih(iscsi_target_t *target, int tsih) 875 { 876 uint32_t i; 877 targv_t *targv; 878 879 targv = target->lunv; 880 for (i = 0 ; i < targv->c ; i++) { 881 if (targv->v[i].tsih == tsih) { 882 return i; 883 } 884 } 885 return -1; 886 } 887 888 /* 889 * login_command_t() handles login requests and replies. 890 */ 891 892 static int 893 login_command_t(target_session_t * sess, uint8_t *header) 894 { 895 iscsi_login_cmd_args_t cmd; 896 iscsi_login_rsp_args_t rsp; 897 uint8_t rsp_header[ISCSI_HEADER_LEN]; 898 targv_t *targv; 899 char *text_in = NULL; 900 char *text_out = NULL; 901 char logbuf[BUFSIZ]; 902 int len_in = 0; 903 int len_out = 0; 904 int status = 0; 905 int i; 906 907 /* Initialize response */ 908 909 #define LC_CLEANUP do { \ 910 if (text_in != NULL) { \ 911 iscsi_free_atomic(text_in); \ 912 } \ 913 if (text_out != NULL) { \ 914 iscsi_free_atomic(text_out); \ 915 } \ 916 } while (/* CONSTCOND */ 0) 917 #define LC_ERROR { \ 918 TC_CLEANUP; \ 919 return -1; \ 920 } 921 922 (void) memset(&rsp, 0x0, sizeof(rsp)); 923 rsp.status_class = ISCSI_LOGIN_STATUS_INITIATOR_ERROR; 924 925 /* Get login args & check preconditions */ 926 927 if (iscsi_login_cmd_decap(header, &cmd) != 0) { 928 iscsi_err(__FILE__, __LINE__, 929 "iscsi_login_cmd_decap() failed\n"); 930 goto response; 931 } 932 if (sess->IsLoggedIn) { 933 iscsi_err(__FILE__, __LINE__, 934 "duplicate login attempt on sess %d\n", sess->id); 935 goto response; 936 } 937 if ((cmd.cont != 0) && (cmd.transit != 0)) { 938 iscsi_err(__FILE__, __LINE__, 939 "Bad cmd.continue. Expected 0.\n"); 940 goto response; 941 } else if ((cmd.version_max < ISCSI_VERSION) || 942 (cmd.version_min > ISCSI_VERSION)) { 943 iscsi_err(__FILE__, __LINE__, 944 "Target iscsi version (%u) not supported by initiator " 945 "[Max Ver (%u) and Min Ver (%u)]\n", 946 ISCSI_VERSION, cmd.version_max, cmd.version_min); 947 rsp.status_class = ISCSI_LOGIN_STATUS_INITIATOR_ERROR; 948 rsp.status_detail = ISCSI_LOGIN_DETAIL_VERSION_NOT_SUPPORTED; 949 rsp.version_max = ISCSI_VERSION; 950 rsp.version_active = ISCSI_VERSION; 951 goto response; 952 } else if (cmd.tsih != 0) { 953 iscsi_err(__FILE__, __LINE__, 954 "Bad cmd.tsih (%u). Expected 0.\n", cmd.tsih); 955 goto response; 956 } 957 958 /* Parse text parameters and build response */ 959 if ((text_out = iscsi_malloc_atomic(2048)) == NULL) { 960 iscsi_err(__FILE__, __LINE__, 961 "iscsi_malloc_atomic() failed\n"); 962 return -1; 963 } 964 if ((len_in = cmd.length) != 0) { 965 iscsi_trace(TRACE_ISCSI_DEBUG, 966 "reading %d bytes text data\n", len_in); 967 text_in = iscsi_malloc_atomic((unsigned)(len_in + 1)); 968 if (text_in == NULL) { 969 iscsi_err(__FILE__, __LINE__, 970 "iscsi_malloc() failed\n"); 971 LC_CLEANUP; 972 return -1; 973 } 974 if (iscsi_sock_msg(sess->sock, 0, (unsigned) len_in, text_in, 975 0) != len_in) { 976 iscsi_err(__FILE__, __LINE__, 977 "iscsi_sock_msg() failed\n"); 978 LC_CLEANUP; 979 return -1; 980 } 981 text_in[len_in] = 0x0; 982 iscsi_trace(TRACE_ISCSI_DEBUG, 983 "successfully read %d bytes text data\n", len_in); 984 985 /* 986 * Parse incoming parameters (text_out will contain the 987 * response we need 988 */ 989 990 /* to send back to the initiator */ 991 992 993 status = param_text_parse(sess->params, 994 &sess->sess_params.cred, text_in, len_in, 995 text_out, &len_out, 2048, 0); 996 if (status != 0) { 997 switch (status) { 998 case ISCSI_PARAM_STATUS_FAILED: 999 rsp.status_detail = ISCSI_LOGIN_DETAIL_SUCCESS; 1000 break; 1001 case ISCSI_PARAM_STATUS_AUTH_FAILED: 1002 rsp.status_detail = 1003 ISCSI_LOGIN_DETAIL_INIT_AUTH_FAILURE; 1004 break; 1005 default: 1006 /* 1007 * We will need to set the detail 1008 * field based on more detailed error 1009 * cases. Will need to fix this if 1010 * compliciance test break 1011 * (status_detail field). 1012 */ 1013 break; 1014 } 1015 goto response; 1016 } 1017 /* Parse the outgoing offer */ 1018 if (!sess->LoginStarted) { 1019 PARAM_TEXT_ADD(sess->params, "TargetPortalGroupTag", 1020 "1", text_out, &len_out, 2048, 0, LC_ERROR); 1021 } 1022 if (len_out) { 1023 PARAM_TEXT_PARSE(sess->params, 1024 &sess->sess_params.cred, text_out, len_out, 1025 NULL, NULL, 2048, 1, LC_ERROR; 1026 ); 1027 } 1028 } 1029 if (!sess->LoginStarted) { 1030 sess->LoginStarted = 1; 1031 } 1032 /* 1033 * For now, we accept what ever the initiators' current and next 1034 * states are. And le are always 1035 */ 1036 /* ready to transitition to that state. */ 1037 1038 rsp.csg = cmd.csg; 1039 rsp.nsg = cmd.nsg; 1040 rsp.transit = cmd.transit; 1041 1042 if (cmd.csg == ISCSI_LOGIN_STAGE_SECURITY) { 1043 if (param_equiv(sess->params, "AuthResult", "No")) { 1044 rsp.transit = 0; 1045 } else if (param_equiv(sess->params, "AuthResult", "Fail")) { 1046 rsp.status_class = rsp.status_detail = 1047 ISCSI_LOGIN_DETAIL_INIT_AUTH_FAILURE; 1048 goto response; 1049 } 1050 } 1051 if (cmd.transit && cmd.nsg == ISCSI_LOGIN_STAGE_FULL_FEATURE) { 1052 iscsi_trace(TRACE_ISCSI_DEBUG, 1053 "transitioning to ISCSI_LOGIN_STAGE_FULL_FEATURE\n"); 1054 1055 /* Check post conditions */ 1056 if (param_equiv(sess->params, "InitiatorName", "")) { 1057 iscsi_err(__FILE__, __LINE__, 1058 "InitiatorName not specified\n"); 1059 goto response; 1060 } 1061 if (param_equiv(sess->params, "SessionType", "Normal")) { 1062 if (param_equiv(sess->params, "TargetName", "")) { 1063 iscsi_err(__FILE__, __LINE__, 1064 "TargetName not specified\n"); 1065 goto response; 1066 } 1067 if ((i = find_target_iqn(sess)) < 0) { 1068 iscsi_err(__FILE__, __LINE__, 1069 "Bad TargetName \"%s\"\n", 1070 param_val(sess->params, "TargetName")); 1071 goto response; 1072 } 1073 if (cmd.tsih != 0 && 1074 find_target_tsih(sess->target, cmd.tsih) != i) { 1075 targv = sess->target->lunv; 1076 iscsi_err(__FILE__, __LINE__, 1077 "target tsih expected %d, cmd.tsih %d, " 1078 "i %d\n", targv->v[i].tsih, cmd.tsih, 1079 i); 1080 } 1081 sess->d = i; 1082 } else if ((i = find_target_tsih(sess->target, cmd.tsih)) < 0) { 1083 iscsi_err(__FILE__, __LINE__, 1084 "Abnormal SessionType cmd.tsih %d not found\n", 1085 cmd.tsih); 1086 i = sess->d; 1087 } 1088 if (param_equiv(sess->params, "SessionType", "")) { 1089 iscsi_err(__FILE__, __LINE__, 1090 "SessionType not specified\n"); 1091 goto response; 1092 } 1093 sess->ExpCmdSN = sess->MaxCmdSN = cmd.CmdSN; 1094 sess->cid = cmd.cid; 1095 sess->isid = cmd.isid; 1096 1097 targv = sess->target->lunv; 1098 targv->v[i].tsih = sess->tsih = ++sess->target->last_tsih; 1099 sess->IsFullFeature = 1; 1100 1101 sess->IsLoggedIn = 1; 1102 if (!param_equiv(sess->params, "SessionType", "Discovery")) { 1103 (void) strlcpy(param_val(sess->params, 1104 "MaxConnections"), "1", 2); 1105 } 1106 set_session_parameters(sess->params, &sess->sess_params); 1107 } else { 1108 if ((i = find_target_tsih(sess->target, cmd.tsih)) < 0) { 1109 iscsi_err(__FILE__, __LINE__, 1110 "cmd.tsih %d not found\n", cmd.tsih); 1111 } 1112 } 1113 1114 /* No errors */ 1115 rsp.status_class = rsp.status_detail = ISCSI_LOGIN_DETAIL_SUCCESS; 1116 rsp.length = len_out; 1117 1118 /* Send login response */ 1119 response: 1120 sess->ExpCmdSN = sess->MaxCmdSN = cmd.CmdSN; 1121 rsp.isid = cmd.isid; 1122 rsp.StatSN = cmd.ExpStatSN; /* debug */ 1123 rsp.tag = cmd.tag; 1124 rsp.cont = cmd.cont; 1125 rsp.ExpCmdSN = sess->ExpCmdSN; 1126 rsp.MaxCmdSN = sess->MaxCmdSN; 1127 if (!rsp.status_class) { 1128 if (rsp.transit && 1129 (rsp.nsg == ISCSI_LOGIN_STAGE_FULL_FEATURE)) { 1130 rsp.version_max = ISCSI_VERSION; 1131 rsp.version_active = ISCSI_VERSION; 1132 rsp.StatSN = ++(sess->StatSN); 1133 rsp.tsih = sess->tsih; 1134 } 1135 } 1136 if (iscsi_login_rsp_encap(rsp_header, &rsp) != 0) { 1137 iscsi_err(__FILE__, __LINE__, 1138 "iscsi_login_rsp_encap() failed\n"); 1139 LC_CLEANUP; 1140 return -1; 1141 } 1142 iscsi_trace(TRACE_ISCSI_DEBUG, "sending login response\n"); 1143 if ((uint32_t)iscsi_sock_send_header_and_data(sess->sock, rsp_header, 1144 ISCSI_HEADER_LEN, text_out, rsp.length, 0) != 1145 ISCSI_HEADER_LEN + rsp.length) { 1146 iscsi_err(__FILE__, __LINE__, 1147 "iscsi_sock_send_header_and_data() failed\n"); 1148 LC_CLEANUP; 1149 return -1; 1150 } 1151 iscsi_trace(TRACE_ISCSI_DEBUG, 1152 "sent login response ok\n"); 1153 if (rsp.status_class != 0) { 1154 LC_CLEANUP; 1155 return -1; 1156 } 1157 if (cmd.transit && cmd.nsg == ISCSI_LOGIN_STAGE_FULL_FEATURE) { 1158 1159 /* log information to stdout */ 1160 (void) snprintf(logbuf, sizeof(logbuf), 1161 "> iSCSI %s login successful from %s on %s disk %d, " 1162 "ISID %" PRIu64 ", TSIH %u", 1163 param_val(sess->params, "SessionType"), 1164 param_val(sess->params, "InitiatorName"), 1165 sess->initiator, 1166 sess->d, 1167 sess->isid, 1168 sess->tsih); 1169 printf("%s\n", logbuf); 1170 #ifdef HAVE_SYSLOG_H 1171 /* log information to syslog */ 1172 syslog(LOG_INFO, "%s", logbuf); 1173 #endif 1174 1175 /* Buffer for data xfers to/from the scsi device */ 1176 if (!param_equiv(sess->params, "MaxRecvDataSegmentLength", 1177 "0")) { 1178 sess->buff = iscsi_malloc((unsigned)( 1179 param_atoi(sess->params, 1180 "MaxRecvDataSegmentLength"))); 1181 if (sess->buff == NULL) { 1182 iscsi_err(__FILE__, __LINE__, 1183 "iscsi_malloc() failed\n"); 1184 LC_CLEANUP; 1185 return -1; 1186 } 1187 } else { 1188 iscsi_err(__FILE__, __LINE__, 1189 "0 MaxRecvDataSegmentLength not supported\n"); 1190 LC_CLEANUP; 1191 return -1; 1192 } 1193 } 1194 LC_CLEANUP; 1195 return 0; 1196 } 1197 1198 static int 1199 logout_command_t(target_session_t * sess, uint8_t *header) 1200 { 1201 iscsi_logout_cmd_args_t cmd; 1202 iscsi_logout_rsp_args_t rsp; 1203 targv_t *targv; 1204 uint8_t rsp_header[ISCSI_HEADER_LEN]; 1205 char logbuf[BUFSIZ]; 1206 int i; 1207 1208 (void) memset(&rsp, 0x0, sizeof(rsp)); 1209 if (iscsi_logout_cmd_decap(header, &cmd) != 0) { 1210 iscsi_err(__FILE__, __LINE__, 1211 "iscsi_logout_cmd_decap() failed\n"); 1212 return -1; 1213 } 1214 sess->StatSN = cmd.ExpStatSN; 1215 if ((cmd.reason == ISCSI_LOGOUT_CLOSE_RECOVERY) && 1216 (param_equiv(sess->params, "ErrorRecoveryLevel", "0"))) { 1217 rsp.response = ISCSI_LOGOUT_STATUS_NO_RECOVERY; 1218 } 1219 #if 0 1220 RETURN_NOT_EQUAL("CmdSN", cmd.CmdSN, sess->ExpCmdSN, NO_CLEANUP, -1); 1221 RETURN_NOT_EQUAL("ExpStatSN", cmd.ExpStatSN, sess->StatSN, NO_CLEANUP, -1); 1222 #else 1223 if (cmd.CmdSN != sess->ExpCmdSN) { 1224 iscsi_err(__FILE__, __LINE__, "CmdSN"); 1225 NO_CLEANUP; 1226 return -1; 1227 } 1228 if (cmd.ExpStatSN != sess->StatSN) { 1229 iscsi_err(__FILE__, __LINE__, "ExpStatSN"); 1230 NO_CLEANUP; 1231 return -1; 1232 } 1233 #endif 1234 1235 rsp.tag = cmd.tag; 1236 rsp.StatSN = sess->StatSN; 1237 rsp.ExpCmdSN = ++sess->ExpCmdSN; 1238 rsp.MaxCmdSN = sess->MaxCmdSN; 1239 if (iscsi_logout_rsp_encap(rsp_header, &rsp) != 0) { 1240 iscsi_err(__FILE__, __LINE__, 1241 "iscsi_logout_rsp_encap() failed\n"); 1242 return -1; 1243 } 1244 if (iscsi_sock_msg(sess->sock, 1, ISCSI_HEADER_LEN, rsp_header, 0) != 1245 ISCSI_HEADER_LEN) { 1246 iscsi_err(__FILE__, __LINE__, 1247 "iscsi_sock_msg() failed\n"); 1248 return -1; 1249 } 1250 iscsi_trace(TRACE_ISCSI_DEBUG, "sent logout response OK\n"); 1251 1252 /* log information to stdout */ 1253 (void) snprintf(logbuf, sizeof(logbuf), 1254 "< iSCSI %s logout successful from %s on %s " 1255 "disk %d, ISID %" PRIu64 ", TSIH %u", 1256 param_val(sess->params, "SessionType"), 1257 param_val(sess->params, "InitiatorName"), 1258 sess->initiator, 1259 sess->d, 1260 sess->isid, 1261 sess->tsih); 1262 printf("%s\n", logbuf); 1263 #ifdef HAVE_SYSLOG 1264 /* log information to syslog */ 1265 syslog(LOG_INFO, "%s", logbuf); 1266 #endif 1267 1268 sess->IsLoggedIn = 0; 1269 1270 if (sess->sess_params.cred.user) { 1271 free(sess->sess_params.cred.user); 1272 sess->sess_params.cred.user = NULL; 1273 } 1274 1275 if ((i = find_target_tsih(sess->target, sess->tsih)) < 0) { 1276 iscsi_err(__FILE__, __LINE__, 1277 "logout sess->tsih %d not found\n", sess->tsih); 1278 } else { 1279 targv = sess->target->lunv; 1280 targv->v[i].tsih = 0; 1281 } 1282 sess->tsih = 0; 1283 1284 return 0; 1285 } 1286 1287 static int 1288 verify_cmd_t(target_session_t * sess, uint8_t *header) 1289 { 1290 int op = ISCSI_OPCODE(header); 1291 1292 if ((!sess->LoginStarted) && (op != ISCSI_LOGIN_CMD)) { 1293 /* Terminate the connection */ 1294 iscsi_err(__FILE__, __LINE__, 1295 "session %d: iSCSI op %#x attempted " 1296 "before LOGIN PHASE\n", 1297 sess->id, op); 1298 return -1; 1299 } 1300 if (!sess->IsFullFeature && 1301 ((op != ISCSI_LOGIN_CMD) && (op != ISCSI_LOGOUT_CMD))) { 1302 iscsi_login_rsp_args_t rsp; 1303 uint8_t rsp_header[ISCSI_HEADER_LEN]; 1304 iscsi_err(__FILE__, __LINE__, 1305 "session %d: iSCSI op %#x before FULL FEATURE\n", 1306 sess->id, op); 1307 /* Create Login Reject response */ 1308 (void) memset(&rsp, 0x0, sizeof(rsp)); 1309 rsp.status_class = ISCSI_LOGIN_STATUS_INITIATOR_ERROR; 1310 rsp.status_detail = ISCSI_LOGIN_DETAIL_NOT_LOGGED_IN; 1311 rsp.version_max = ISCSI_VERSION; 1312 rsp.version_active = ISCSI_VERSION; 1313 1314 if (iscsi_login_rsp_encap(rsp_header, &rsp) != 0) { 1315 iscsi_err(__FILE__, __LINE__, 1316 "iscsi_login_rsp_encap() failed\n"); 1317 return -1; 1318 } 1319 iscsi_trace(TRACE_ISCSI_DEBUG, "sending login response\n"); 1320 if ((uint32_t)iscsi_sock_send_header_and_data(sess->sock, 1321 rsp_header, ISCSI_HEADER_LEN, NULL, 0, 0) != 1322 ISCSI_HEADER_LEN + rsp.length) { 1323 iscsi_err(__FILE__, __LINE__, 1324 "iscsi_sock_send_header_and_data() failed\n"); 1325 return -1; 1326 } 1327 iscsi_trace(TRACE_ISCSI_DEBUG, "sent login response ok\n"); 1328 return -1; 1329 } 1330 return 0; 1331 } 1332 1333 /* 1334 * this function looks at the opcode in the received header for the session, 1335 * and does a switch on the opcode to call the required function. 1336 */ 1337 static int 1338 execute_t(target_session_t *sess, uint8_t *header) 1339 { 1340 int op = ISCSI_OPCODE(header); 1341 1342 if (verify_cmd_t(sess, header) != 0) { 1343 return -1; 1344 } 1345 switch (op) { 1346 case ISCSI_TASK_CMD: 1347 iscsi_trace(TRACE_ISCSI_CMD, 1348 "session %d: Task Command\n", sess->id); 1349 if (task_command_t(sess, header) != 0) { 1350 iscsi_err(__FILE__, __LINE__, 1351 "task_command_t() failed\n"); 1352 return -1; 1353 } 1354 break; 1355 1356 case ISCSI_NOP_OUT: 1357 iscsi_trace(TRACE_ISCSI_CMD, "session %d: NOP-Out\n", sess->id); 1358 if (nop_out_t(sess, header) != 0) { 1359 iscsi_err(__FILE__, __LINE__, 1360 "nop_out_t() failed\n"); 1361 return -1; 1362 } 1363 break; 1364 1365 case ISCSI_LOGIN_CMD: 1366 iscsi_trace(TRACE_ISCSI_CMD, 1367 "session %d: Login Command\n", sess->id); 1368 if (login_command_t(sess, header) != 0) { 1369 iscsi_err(__FILE__, __LINE__, 1370 "login_command_t() failed\n"); 1371 return -1; 1372 } 1373 break; 1374 1375 case ISCSI_TEXT_CMD: 1376 iscsi_trace(TRACE_ISCSI_CMD, 1377 "session %d: Text Command\n", sess->id); 1378 if (text_command_t(sess, header) != 0) { 1379 iscsi_err(__FILE__, __LINE__, 1380 "text_command_t() failed\n"); 1381 return -1; 1382 } 1383 break; 1384 1385 case ISCSI_LOGOUT_CMD: 1386 iscsi_trace(TRACE_ISCSI_CMD, 1387 "session %d: Logout Command\n", sess->id); 1388 if (logout_command_t(sess, header) != 0) { 1389 iscsi_err(__FILE__, __LINE__, 1390 "logout_command_t() failed\n"); 1391 return -1; 1392 } 1393 break; 1394 1395 case ISCSI_SCSI_CMD: 1396 iscsi_trace(TRACE_ISCSI_CMD, 1397 "session %d: SCSI Command\n", sess->id); 1398 if (scsi_command_t(sess, header) != 0) { 1399 iscsi_err(__FILE__, __LINE__, 1400 "scsi_command_t() failed\n"); 1401 return -1; 1402 } 1403 break; 1404 1405 default: 1406 iscsi_err(__FILE__, __LINE__, "Unknown Opcode %#x\n", 1407 ISCSI_OPCODE(header)); 1408 if (reject_t(sess, header, 0x04) != 0) { 1409 iscsi_err(__FILE__, __LINE__, 1410 "reject_t() failed\n"); 1411 return -1; 1412 } 1413 break; 1414 } 1415 return 0; 1416 } 1417 1418 /* 1419 * Currently one thread per session, used for both Rx and Tx. 1420 */ 1421 static int 1422 worker_proc_t(void *arg) 1423 { 1424 target_session_t *sess = (target_session_t *) arg; 1425 uint8_t header[ISCSI_HEADER_LEN]; 1426 iscsi_parameter_t **l = &sess->params; 1427 1428 ISCSI_THREAD_START("worker_thread"); 1429 sess->worker.pid = getpid(); 1430 sess->worker.state |= ISCSI_WORKER_STATE_STARTED; 1431 iscsi_trace(TRACE_ISCSI_DEBUG, "session %d: started\n", sess->id); 1432 1433 /* 1434 * ISCSI_PARAM_TYPE_LIST format: <type> <key> <dflt> <valid list values> 1435 * ISCSI_PARAM_TYPE_BINARY format: <type> <key> <dflt> <valid binary values> 1436 * ISCSI_PARAM_TYPE_NUMERICAL format: <type> <key> <dflt> <max> 1437 * ISCSI_PARAM_TYPE_DECLARATIVE format: <type> <key> <dflt> "" 1438 */ 1439 1440 sess->params = NULL; 1441 l = &sess->params; 1442 1443 /* CHAP Parameters */ 1444 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "AuthMethod", "CHAP", "CHAP,None", return -1); 1445 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "CHAP_A", "None", "5", return -1); 1446 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "CHAP_N", "", "", return -1); 1447 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "CHAP_R", "", "", return -1); 1448 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "CHAP_I", "", "", return -1); 1449 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "CHAP_C", "", "", return -1); 1450 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "TargetPortalGroupTag", "1", "1", return -1); 1451 /* CHAP Parameters */ 1452 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "HeaderDigest", "None", "None", return -1); 1453 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "DataDigest", "None", "None", return -1); 1454 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "MaxConnections", "1", "1", return -1); 1455 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "SendTargets", "", "", return -1); 1456 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "TargetName", "", "", return -1); 1457 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "InitiatorName", "", "", return -1); 1458 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "TargetAlias", "", "", return -1); 1459 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "InitiatorAlias", "", "", return -1); 1460 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "TargetAddress", "", "", return -1); 1461 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_OR, "InitialR2T", "Yes", "Yes,No", return -1); 1462 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_AND, "OFMarker", "No", "Yes,No", return -1); 1463 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_AND, "IFMarker", "No", "Yes,No", return -1); 1464 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "OFMarkInt", "1", "65536", return -1); 1465 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "IFMarkInt", "1", "65536", return -1); 1466 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_AND, "ImmediateData", "Yes", "Yes,No", return -1); 1467 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "MaxRecvDataSegmentLength", "8192", "16777215", return -1); 1468 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "MaxBurstLength", "262144", "16777215", return -1); 1469 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "FirstBurstLength", "65536", "16777215", return -1); 1470 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "DefaultTime2Wait", "2", "2", return -1); 1471 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "DefaultTime2Retain", "20", "20", return -1); 1472 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "MaxOutstandingR2T", "1", "1", return -1); 1473 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_OR, "DataPDUInOrder", "Yes", "Yes,No", return -1); 1474 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_OR, "DataSequenceInOrder", "Yes", "Yes,No", return -1); 1475 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "ErrorRecoveryLevel", "0", "0", return -1); 1476 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "SessionType", "Normal", "Normal,Discovery", return -1); 1477 /* 1478 * Auth Result is not in specs, we use this key to pass 1479 * authentication result 1480 */ 1481 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "AuthResult", "No", "Yes,No,Fail", return -1); 1482 1483 /* Set remaining session parameters */ 1484 1485 sess->UsePhaseCollapsedRead = ISCSI_USE_PHASE_COLLAPSED_READ_DFLT; 1486 1487 /* Loop for commands */ 1488 1489 while (sess->target->state != TARGET_SHUT_DOWN) { 1490 iscsi_trace(TRACE_ISCSI_DEBUG, 1491 "session %d: reading header\n", sess->id); 1492 if (iscsi_sock_msg(sess->sock, 0, ISCSI_HEADER_LEN, header, 0) 1493 != ISCSI_HEADER_LEN) { 1494 iscsi_trace(TRACE_ISCSI_DEBUG, 1495 "session %d: iscsi_sock_msg() failed\n", 1496 sess->id); 1497 break; 1498 } 1499 iscsi_trace(TRACE_ISCSI_DEBUG, 1500 "session %d: iscsi op %#x\n", sess->id, 1501 ISCSI_OPCODE(header)); 1502 if (execute_t(sess, header) != 0) { 1503 iscsi_err(__FILE__, __LINE__, 1504 "execute_t() failed\n"); 1505 break; 1506 } 1507 iscsi_trace(TRACE_ISCSI_DEBUG, 1508 "session %d: iscsi op %#x complete\n", sess->id, 1509 ISCSI_OPCODE(header)); 1510 if (ISCSI_OPCODE(header) == ISCSI_LOGOUT_CMD) { 1511 iscsi_trace(TRACE_ISCSI_DEBUG, 1512 "session %d: logout received, ending session\n", 1513 sess->id); 1514 break; 1515 } 1516 } 1517 1518 /* Clean up */ 1519 1520 iscsi_free(sess->buff); 1521 if (param_list_destroy(sess->params) != 0) { 1522 iscsi_err(__FILE__, __LINE__, 1523 "param_list_destroy() failed\n"); 1524 return -1; 1525 } 1526 /* Terminate connection */ 1527 1528 if (iscsi_sock_close(sess->sock) != 0) { 1529 iscsi_err(__FILE__, __LINE__, 1530 "iscsi_sock_close() failed\n"); 1531 } 1532 /* Make session available */ 1533 1534 ISCSI_LOCK(&g_session_q_mutex, return -1); 1535 (void) memset(sess, 0x0, sizeof(*sess)); 1536 sess->d = -1; 1537 if (iscsi_queue_insert(&g_session_q, sess) != 0) { 1538 iscsi_err(__FILE__, __LINE__, 1539 "iscsi_queue_insert() failed\n"); 1540 return -1; 1541 } 1542 ISCSI_UNLOCK(&g_session_q_mutex, return -1); 1543 iscsi_trace(TRACE_ISCSI_DEBUG, "session %d: ended\n", sess->id); 1544 1545 return 0; 1546 } 1547 1548 static int 1549 read_data_pdu(target_session_t * sess, 1550 iscsi_write_data_t * data, 1551 iscsi_scsi_cmd_args_t * args) 1552 { 1553 uint8_t header[ISCSI_HEADER_LEN]; 1554 int ret_val = -1; 1555 1556 if (iscsi_sock_msg(sess->sock, 0, ISCSI_HEADER_LEN, header, 0) != 1557 ISCSI_HEADER_LEN) { 1558 iscsi_err(__FILE__, __LINE__, 1559 "iscsi_sock_msg() failed\n"); 1560 return -1; 1561 } 1562 if ((ret_val = iscsi_write_data_decap(header, data)) != 0) { 1563 iscsi_err(__FILE__, __LINE__, 1564 "iscsi_write_data_decap() failed\n"); 1565 return ret_val; 1566 } 1567 /* Check args */ 1568 if (sess->sess_params.max_dataseg_len) { 1569 if (data->length > sess->sess_params.max_dataseg_len) { 1570 args->status = 0x02; 1571 return -1; 1572 } 1573 } 1574 if ((args->bytes_recv + data->length) > args->trans_len) { 1575 args->status = 0x02; 1576 return -1; 1577 } 1578 if (data->tag != args->tag) { 1579 iscsi_trace(TRACE_ISCSI_DEBUG, 1580 "Data ITT (%d) does not match with command ITT (%d)\n", 1581 data->tag, args->tag); 1582 if (data->final) { 1583 args->status = 0x02; 1584 return -1; 1585 } else { 1586 /* Send a reject PDU */ 1587 iscsi_trace(TRACE_ISCSI_DEBUG, "Sending Reject PDU\n"); 1588 if (reject_t(sess, header, 0x09) != 0) { 1589 /* Invalid PDU Field */ 1590 iscsi_trace(TRACE_ISCSI_DEBUG, 1591 "Sending Reject PDU failed\n"); 1592 return 1; 1593 } 1594 } 1595 } 1596 return 0; 1597 } 1598 1599 int 1600 target_transfer_data(target_session_t * sess, iscsi_scsi_cmd_args_t * args, 1601 struct iovec * sg, int sg_len) 1602 { 1603 iscsi_write_data_t data; 1604 struct iovec *iov, *iov_ptr = NULL; 1605 int iov_len; 1606 1607 #define TTD_CLEANUP do { \ 1608 if (iov_ptr != NULL) { \ 1609 iscsi_free_atomic(iov_ptr); \ 1610 } \ 1611 } while (/* CONSTCOND */ 0) 1612 1613 args->bytes_recv = 0; 1614 if ((!sess->sess_params.immediate_data) && args->length) { 1615 iscsi_trace(TRACE_ISCSI_DEBUG, 1616 "Cannot accept any Immediate data\n"); 1617 args->status = 0x02; 1618 return -1; 1619 } 1620 1621 /* Make a copy of the iovec */ 1622 iov_ptr = iscsi_malloc_atomic(sizeof(struct iovec) * sg_len); 1623 if (iov_ptr == NULL) { 1624 iscsi_err(__FILE__, __LINE__, 1625 "iscsi_malloc_atomic() failed\n"); 1626 return -1; 1627 } 1628 iov = iov_ptr; 1629 (void) memcpy(iov, sg, sizeof(struct iovec) * sg_len); 1630 iov_len = sg_len; 1631 1632 /* 1633 * Read any immediate data. 1634 */ 1635 1636 if (sess->sess_params.immediate_data && args->length) { 1637 if (sess->sess_params.max_dataseg_len && 1638 args->length > sess->sess_params.max_dataseg_len) { 1639 iscsi_err(__FILE__, __LINE__, 1640 "args->length (%u) too long\n", 1641 args->length); 1642 TTD_CLEANUP; 1643 return -1; 1644 } 1645 1646 /* Modify iov to include just immediate data */ 1647 if (modify_iov(&iov, &iov_len, 0, args->length) != 0) { 1648 iscsi_err(__FILE__, __LINE__, 1649 "modify_iov() failed\n"); 1650 TTD_CLEANUP; 1651 return -1; 1652 } 1653 iscsi_trace(TRACE_SCSI_DATA, 1654 "reading %u bytes immediate write data\n", 1655 args->length); 1656 if ((uint32_t)iscsi_sock_msg(sess->sock, 0, args->length, iov, 1657 iov_len) != args->length) { 1658 iscsi_err(__FILE__, __LINE__, 1659 "iscsi_sock_msg() failed\n"); 1660 TTD_CLEANUP; 1661 return -1; 1662 } 1663 iscsi_trace(TRACE_SCSI_DATA, 1664 "successfully read %u bytes immediate write data\n", 1665 args->length); 1666 args->bytes_recv += args->length; 1667 } 1668 1669 /* 1670 * Read iSCSI data PDUs 1671 */ 1672 if (args->bytes_recv < args->trans_len) { 1673 int r2t_flag = 0; 1674 int read_status = 0; 1675 iscsi_r2t_t r2t; 1676 int desired_xfer_len; 1677 1678 desired_xfer_len = MIN(sess->sess_params.first_burst_length, 1679 args->trans_len) - args->bytes_recv; 1680 (void) memset(&r2t, 0x0, sizeof(r2t)); 1681 do { 1682 1683 /* 1684 * Send R2T if we're either operating in solicted 1685 * mode or we're operating in unsolicted 1686 */ 1687 /* mode and have reached the first burst */ 1688 if (!r2t_flag && 1689 (sess->sess_params.initial_r2t || 1690 (sess->sess_params.first_burst_length && 1691 (args->bytes_recv >= 1692 sess->sess_params.first_burst_length)))) { 1693 uint8_t header[ISCSI_HEADER_LEN]; 1694 1695 desired_xfer_len = MIN(args->trans_len - 1696 args->bytes_recv, 1697 sess->sess_params.max_burst_length); 1698 iscsi_trace(TRACE_ISCSI_DEBUG, 1699 "sending R2T for %u bytes data\n", 1700 desired_xfer_len); 1701 r2t.tag = args->tag; 1702 1703 r2t.transfer_tag = 0x1234; 1704 1705 r2t.ExpCmdSN = sess->ExpCmdSN; 1706 r2t.MaxCmdSN = sess->MaxCmdSN; 1707 r2t.StatSN = ++(sess->StatSN); 1708 r2t.length = desired_xfer_len; 1709 r2t.offset = args->bytes_recv; 1710 if (iscsi_r2t_encap(header, &r2t) != 0) { 1711 iscsi_err(__FILE__, __LINE__, 1712 "r2t_encap() failed\n"); 1713 TTD_CLEANUP; 1714 return -1; 1715 } 1716 iscsi_trace(TRACE_ISCSI_DEBUG, 1717 "sending R2T tag %u transfer tag " 1718 "%u len %u offset %u\n", 1719 r2t.tag, r2t.transfer_tag, r2t.length, 1720 r2t.offset); 1721 if (iscsi_sock_msg(sess->sock, 1, 1722 ISCSI_HEADER_LEN, header, 0) != 1723 ISCSI_HEADER_LEN) { 1724 iscsi_err(__FILE__, __LINE__, 1725 "iscsi_sock_msg() failed\n"); 1726 TTD_CLEANUP; 1727 return -1; 1728 } 1729 r2t_flag = 1; 1730 r2t.R2TSN += 1; 1731 } 1732 1733 /* Read iSCSI data PDU */ 1734 iscsi_trace(TRACE_ISCSI_DEBUG, "reading data pdu\n"); 1735 read_status = read_data_pdu(sess, &data, args); 1736 if (read_status != 0) { 1737 if (read_status == 1) { 1738 iscsi_trace(TRACE_ISCSI_DEBUG, 1739 "Unknown PDU received and " 1740 "ignored. Expecting " 1741 "Data PDU\n"); 1742 continue; 1743 } else { 1744 iscsi_err(__FILE__, __LINE__, 1745 "read_data_pdu() failed\n"); 1746 args->status = 0x02; 1747 TTD_CLEANUP; 1748 return -1; 1749 } 1750 } 1751 if (data.ExpStatSN != sess->StatSN) { 1752 iscsi_warn(__FILE__, __LINE__, 1753 "Bad \"ExpStatSN\": Got %u " 1754 "expected %u.\n", 1755 data.ExpStatSN, sess->StatSN); 1756 } 1757 iscsi_trace(TRACE_ISCSI_DEBUG, 1758 "read data pdu OK (offset %u, length %u)\n", 1759 data.offset, data.length); 1760 1761 /* Modify iov with offset and length. */ 1762 iov = iov_ptr; 1763 (void) memcpy(iov, sg, sizeof(struct iovec) * sg_len); 1764 iov_len = sg_len; 1765 if (modify_iov(&iov, &iov_len, data.offset, 1766 data.length) != 0) { 1767 iscsi_err(__FILE__, __LINE__, 1768 "modify_iov() failed\n"); 1769 TTD_CLEANUP; 1770 return -1; 1771 } 1772 1773 /* Scatter into destination buffers */ 1774 if ((uint32_t)iscsi_sock_msg(sess->sock, 0, 1775 data.length, iov, iov_len) != data.length) { 1776 iscsi_err(__FILE__, __LINE__, 1777 "iscsi_sock_msg() failed\n"); 1778 TTD_CLEANUP; 1779 return -1; 1780 } 1781 iscsi_trace(TRACE_ISCSI_DEBUG, 1782 "successfully scattered %u bytes\n", 1783 data.length); 1784 args->bytes_recv += data.length; 1785 desired_xfer_len -= data.length; 1786 if ((!r2t_flag) && 1787 (args->bytes_recv > 1788 sess->sess_params.first_burst_length)) { 1789 iscsi_err(__FILE__, __LINE__, 1790 "Received unsolicited data (%u) " 1791 "more than first_burst_length (%u)\n", 1792 args->bytes_recv, 1793 sess->sess_params.first_burst_length); 1794 args->status = 0x02; 1795 TTD_CLEANUP; 1796 return -1; 1797 } 1798 if ((desired_xfer_len != 0) && data.final) { 1799 iscsi_err(__FILE__, __LINE__, 1800 "Expecting more data (%d) " 1801 "from initiator for this sequence\n", 1802 desired_xfer_len); 1803 args->status = 0x02; 1804 TTD_CLEANUP; 1805 return -1; 1806 } 1807 if ((desired_xfer_len == 0) && !data.final) { 1808 iscsi_err(__FILE__, __LINE__, 1809 "Final bit not set on the last " 1810 "data PDU of this sequence\n"); 1811 args->status = 0x02; 1812 TTD_CLEANUP; 1813 return -1; 1814 } 1815 if ((desired_xfer_len == 0) && 1816 (args->bytes_recv < args->trans_len)) { 1817 r2t_flag = 0; 1818 } 1819 } while (args->bytes_recv < args->trans_len); 1820 #if 0 1821 RETURN_NOT_EQUAL("Final bit", data.final, 1, TTD_CLEANUP, -1); 1822 #else 1823 if (data.final != 1) { 1824 iscsi_err(__FILE__, __LINE__, "Final bit\n"); 1825 TTD_CLEANUP; 1826 return -1; 1827 } 1828 #endif 1829 } else { 1830 #if 0 1831 RETURN_NOT_EQUAL("Final bit", args->final, 1, TTD_CLEANUP, -1); 1832 #else 1833 if (args->final != 1) { 1834 iscsi_err(__FILE__, __LINE__, "Final bit\n"); 1835 TTD_CLEANUP; 1836 return -1; 1837 } 1838 #endif 1839 } 1840 iscsi_trace(TRACE_ISCSI_DEBUG, 1841 "successfully transferred %u bytes write data\n", 1842 args->trans_len); 1843 TTD_CLEANUP; 1844 return 0; 1845 } 1846 1847 /* check there's enough space in the arrays */ 1848 static void 1849 size_arrays(iscsi_target_t *tgt, unsigned needed) 1850 { 1851 if (tgt->size == 0) { 1852 /* only get here first time around */ 1853 tgt->size = needed; 1854 tgt->name = calloc(sizeof(char *), needed); 1855 tgt->value = calloc(sizeof(char *), needed); 1856 } else if (tgt->c == tgt->size) { 1857 /* only uses 'needed' when filled array */ 1858 tgt->size += needed; 1859 tgt->name = realloc(tgt->name, sizeof(char *) * needed); 1860 tgt->value = realloc(tgt->value, sizeof(char *) * needed); 1861 } 1862 } 1863 1864 /* find the name in the array */ 1865 static int 1866 findvar(iscsi_target_t *tgt, const char *name) 1867 { 1868 unsigned i; 1869 1870 for (i = 0 ; i < tgt->c && strcmp(tgt->name[i], name) != 0; i++) { 1871 } 1872 return (i == tgt->c) ? -1 : (int)i; 1873 } 1874 1875 /******************** 1876 * Public Functions * 1877 ********************/ 1878 1879 int 1880 iscsi_target_set_defaults(iscsi_target_t *tgt) 1881 { 1882 char buf[32]; 1883 1884 /* set defaults */ 1885 (void) memset(tgt, 0x0, sizeof(*tgt)); 1886 iscsi_target_setvar(tgt, "iqn", DEFAULT_TARGET_NAME); 1887 (void) snprintf(buf, sizeof(buf), "%d", ISCSI_PORT); 1888 iscsi_target_setvar(tgt, "target port", buf); 1889 iscsi_target_setvar(tgt, "address family", "unspec"); 1890 (void) snprintf(buf, sizeof(buf), "%d", DEFAULT_TARGET_MAX_SESSIONS); 1891 iscsi_target_setvar(tgt, "max sessions", buf); 1892 iscsi_target_setvar(tgt, "configfile", _PATH_ISCSI_TARGETS); 1893 iscsi_target_setvar(tgt, "blocklen", "512"); 1894 return 1; 1895 } 1896 1897 /* re-read the configuration file */ 1898 int 1899 iscsi_target_reconfigure(iscsi_target_t *tgt) 1900 { 1901 targv_t *oldluns; 1902 devv_t *olddevices; 1903 extv_t *oldextents; 1904 targv_t *luns; 1905 devv_t *devices; 1906 extv_t *extents; 1907 char *config; 1908 1909 NEW(targv_t, luns, "iscsi_target_reconf 1", return -1); 1910 NEW(devv_t, devices, "iscsi_target_reconf 2", return -1); 1911 NEW(extv_t, extents, "iscsi_target_reconf 3", return -1); 1912 config = iscsi_target_getvar(tgt, "configfile"); 1913 if (!read_conf_file(config, tgt->lunv, tgt->devv, tgt->extentv)) { 1914 (void) fprintf(stderr, "Error: can't open `%s'\n", config); 1915 return 0; 1916 } 1917 /* it worked - let's reassign things */ 1918 /* XXX - agc - lock */ 1919 oldluns = tgt->lunv; 1920 olddevices = tgt->devv; 1921 oldextents = tgt->extentv; 1922 tgt->lunv = luns; 1923 tgt->devv = devices; 1924 tgt->extentv = extents; 1925 /* XXX - agc - unlock */ 1926 /* free up storage */ 1927 (void) free(oldluns); 1928 (void) free(olddevices); 1929 (void) free(oldextents); 1930 return 1; 1931 } 1932 1933 int 1934 iscsi_target_start(iscsi_target_t *tgt) 1935 { 1936 uint32_t j; 1937 targv_t *lunv; 1938 char *config; 1939 char *dbg; 1940 int maxsessions; 1941 int i; 1942 1943 if ((dbg = iscsi_target_getvar(tgt, "debug")) != NULL) { 1944 set_debug(dbg); 1945 } 1946 /* allocate space for disks, extents and targets */ 1947 NEW(targv_t, tgt->lunv, "iscsi_target_start 1", return -1); 1948 NEW(devv_t, tgt->devv, "iscsi_target_start 2", return -1); 1949 NEW(extv_t, tgt->extentv, "iscsi_target_start 3", return -1); 1950 /* read the configuration file */ 1951 config = iscsi_target_getvar(tgt, "configfile"); 1952 if (!read_conf_file(config, tgt->lunv, tgt->devv, tgt->extentv)) { 1953 (void) fprintf(stderr, "Error: can't open `%s'\n", config); 1954 return 0; 1955 } 1956 lunv = tgt->lunv; 1957 if (lunv->c == 0) { 1958 (void) fprintf(stderr, "No targets to initialise\n"); 1959 return -1; 1960 } 1961 maxsessions = atoi(iscsi_target_getvar(tgt, "max sessions")); 1962 NEWARRAY(target_session_t, g_session, maxsessions, "iscsi_target_start", 1963 return -1); 1964 device_set_var("blocklen", iscsi_target_getvar(tgt, "blocklen")); 1965 if (tgt->state == TARGET_INITIALIZING || 1966 tgt->state == TARGET_INITIALIZED) { 1967 iscsi_err(__FILE__, __LINE__, 1968 "duplicate target initialization attempted\n"); 1969 return -1; 1970 } 1971 tgt->state = TARGET_INITIALIZING; 1972 if (iscsi_queue_init(&g_session_q, maxsessions) != 0) { 1973 iscsi_err(__FILE__, __LINE__, 1974 "iscsi_queue_init() failed\n"); 1975 return -1; 1976 } 1977 tgt->main_pid = getpid(); 1978 for (i = 0; i < maxsessions; i++) { 1979 g_session[i].id = i; 1980 g_session[i].d = -1; 1981 if (iscsi_queue_insert(&g_session_q, &g_session[i]) != 0) { 1982 iscsi_err(__FILE__, __LINE__, 1983 "iscsi_queue_insert() failed\n"); 1984 return -1; 1985 } 1986 } 1987 for (j = 0 ; j < lunv->c ; j++) { 1988 int d = device_init(tgt, lunv, &lunv->v[j]); 1989 1990 if (d < 0) { 1991 iscsi_err(__FILE__, __LINE__, 1992 "device_init() failed\n"); 1993 return -1; 1994 } 1995 } 1996 ISCSI_MUTEX_INIT(&g_session_q_mutex, return -1); 1997 tgt->listener_listening = 0; 1998 tgt->listener_pid = -1; 1999 tgt->state = TARGET_INITIALIZED; 2000 printf("TARGET: iSCSI Qualified Name (IQN) is %s\n", 2001 iscsi_target_getvar(tgt, "iqn")); 2002 for (i = 0 ; i < tgt->sockc ; i++) { 2003 printf("\tsocket %d listening on port %s\n", tgt->sockv[i], 2004 iscsi_target_getvar(tgt, "target port")); 2005 } 2006 return 0; 2007 } 2008 2009 int 2010 iscsi_target_shutdown(iscsi_target_t *tgt) 2011 { 2012 target_session_t *sess; 2013 int maxsessions; 2014 int i; 2015 2016 if ((tgt->state == TARGET_SHUTTING_DOWN) || 2017 (tgt->state == TARGET_SHUT_DOWN)) { 2018 iscsi_err(__FILE__, __LINE__, 2019 "duplicate target shutdown attempted\n"); 2020 return -1; 2021 } 2022 tgt->state = TARGET_SHUTTING_DOWN; 2023 iscsi_trace(TRACE_ISCSI_DEBUG, "shutting down target\n"); 2024 maxsessions = atoi(iscsi_target_getvar(tgt, "max sessions")); 2025 for (i = 0; i < maxsessions; i++) { 2026 sess = &g_session[i]; 2027 2028 /* Need to replace with a call to session_destroy() */ 2029 2030 if (sess->IsLoggedIn) { 2031 printf("shutting down socket on sess %d\n", i); 2032 iscsi_trace(TRACE_ISCSI_DEBUG, 2033 "shutting down socket on sess %d\n", i); 2034 if (iscsi_sock_shutdown(sess->sock, 2) != 0) { 2035 iscsi_err(__FILE__, __LINE__, 2036 "iscsi_sock_shutdown() failed\n"); 2037 return -1; 2038 } 2039 printf("waiting for worker %d (pid %d, state %d)\n", 2040 i, sess->worker.pid, sess->worker.state); 2041 iscsi_trace(TRACE_ISCSI_DEBUG, 2042 "waiting for worker %d (pid %d, state %d)\n", 2043 i, sess->worker.pid, sess->worker.state); 2044 while (sess->worker.state & 2045 ISCSI_WORKER_STATE_STARTED) { 2046 ISCSI_SPIN; 2047 } 2048 iscsi_trace(TRACE_ISCSI_DEBUG, 2049 "worker %d has exited\n", i); 2050 } 2051 if (device_shutdown(sess) != 0) { 2052 iscsi_err(__FILE__, __LINE__, 2053 "device_shutdown() failed\n"); 2054 return -1; 2055 } 2056 } 2057 iscsi_trace(TRACE_ISCSI_DEBUG, "shutting down accept socket\n"); 2058 if (iscsi_sock_shutdown(tgt->sockv[0], 2) != 0) { 2059 iscsi_err(__FILE__, __LINE__, 2060 "iscsi_sock_shutdown() failed\n"); 2061 return -1; 2062 } 2063 if (tgt->listener_pid != getpid()) { 2064 iscsi_trace(TRACE_ISCSI_DEBUG, "waiting for listener thread\n"); 2065 while (tgt->listener_listening) { 2066 ISCSI_SPIN; 2067 } 2068 iscsi_trace(TRACE_ISCSI_DEBUG, "listener thread has exited\n"); 2069 } 2070 iscsi_trace(TRACE_ISCSI_DEBUG, "closing accept socket\n"); 2071 if (iscsi_sock_close(tgt->sockv[0]) != 0) { 2072 iscsi_err(__FILE__, __LINE__, 2073 "iscsi_sock_close() failed\n"); 2074 return -1; 2075 } 2076 ISCSI_MUTEX_DESTROY(&g_session_q_mutex, return -1); 2077 iscsi_trace(TRACE_ISCSI_DEBUG, "target shutdown complete\n"); 2078 tgt->state = TARGET_SHUT_DOWN; 2079 2080 return 0; 2081 } 2082 2083 int 2084 iscsi_target_listen(iscsi_target_t *tgt) 2085 { 2086 struct sockaddr_in6 remoteAddrStorage6; 2087 struct sockaddr_in6 localAddrStorage6; 2088 struct sockaddr_in remoteAddrStorage; 2089 struct sockaddr_in localAddrStorage; 2090 target_session_t *sess; 2091 socklen_t remoteAddrLen; 2092 socklen_t localAddrLen; 2093 char targetaddress[1024]; 2094 char remote[1024]; 2095 char local[1024]; 2096 char *config; 2097 int newconn; 2098 int i; 2099 2100 ISCSI_THREAD_START("listen_thread"); 2101 tgt->listener_pid = getpid(); 2102 tgt->listener_listening++; 2103 iscsi_trace(TRACE_ISCSI_DEBUG, "listener thread started\n"); 2104 2105 if (!iscsi_socks_establish(tgt->sockv, tgt->famv, &tgt->sockc, 2106 iscsi_target_getvar(tgt, "address family"), 2107 atoi(iscsi_target_getvar(tgt, "target port")))) { 2108 iscsi_err(__FILE__, __LINE__, 2109 "iscsi_sock_establish() failed\n"); 2110 goto done; 2111 } 2112 2113 iscsi_trace(TRACE_NET_DEBUG, "create, bind, listen OK\n"); 2114 2115 /* Loop for connections: FIX ME with queue */ 2116 2117 while (tgt->state != TARGET_SHUT_DOWN) { 2118 ISCSI_LOCK(&g_session_q_mutex, return -1); 2119 if ((sess = iscsi_queue_remove(&g_session_q)) == NULL) { 2120 iscsi_err(__FILE__, __LINE__, 2121 "no free sessions: iscsi_queue_remove() failed\n"); 2122 goto done; 2123 } 2124 ISCSI_UNLOCK(&g_session_q_mutex, return -1); 2125 assert(sess->d == -1); 2126 #if 0 2127 (void) memset(sess, 0x0, sizeof(*sess)); 2128 #endif 2129 2130 sess->target = tgt; 2131 2132 /* Accept connection, spawn session thread, and */ 2133 /* clean up old threads */ 2134 2135 config = iscsi_target_getvar(tgt, "configfile"); 2136 i = iscsi_waitfor_connection(tgt->sockv, tgt->sockc, config, 2137 &newconn); 2138 2139 iscsi_trace(TRACE_NET_DEBUG, 2140 "waiting for %s connection on port %s\n", 2141 iscsi_address_family(tgt->famv[i]), 2142 iscsi_target_getvar(tgt, "target port")); 2143 2144 if (!iscsi_sock_accept(newconn, &sess->sock)) { 2145 iscsi_trace(TRACE_ISCSI_DEBUG, 2146 "iscsi_sock_accept() failed\n"); 2147 goto done; 2148 } 2149 2150 switch (tgt->famv[i]) { 2151 case AF_INET: 2152 sess->address_family = 4; 2153 (void) memset(&localAddrStorage, 0x0, 2154 localAddrLen = sizeof(localAddrStorage)); 2155 if (getsockname(sess->sock, 2156 (struct sockaddr *)(void *)&localAddrStorage, 2157 &localAddrLen) < 0) { 2158 iscsi_err(__FILE__, __LINE__, 2159 "iscsi_sock_getsockname() failed\n"); 2160 goto done; 2161 } 2162 (void) memset(&remoteAddrStorage, 0x0, 2163 remoteAddrLen = sizeof(remoteAddrStorage)); 2164 if (getpeername(sess->sock, 2165 (struct sockaddr *)(void *) &remoteAddrStorage, 2166 &remoteAddrLen) < 0) { 2167 iscsi_err(__FILE__, __LINE__, 2168 "iscsi_sock_getpeername() failed\n"); 2169 goto done; 2170 } 2171 2172 #ifdef HAVE_GETNAMEINFO 2173 if (getnameinfo((struct sockaddr *)(void *) 2174 &localAddrStorage, 2175 sizeof(localAddrStorage), local, 2176 sizeof(local), NULL, 0, NI_NUMERICHOST) < 0) { 2177 iscsi_err(__FILE__, __LINE__, 2178 "getnameinfo local failed\n"); 2179 } 2180 if (getnameinfo((struct sockaddr *)(void *) 2181 &remoteAddrStorage, 2182 sizeof(remoteAddrStorage), remote, 2183 sizeof(remote), NULL, 0, NI_NUMERICHOST) < 0) { 2184 iscsi_err(__FILE__, __LINE__, 2185 "getnameinfo remote failed\n"); 2186 } 2187 (void) strlcpy(sess->initiator, remote, 2188 sizeof(sess->initiator)); 2189 #else 2190 (void) strlcpy(local, 2191 inet_ntoa(localAddrStorage.sin_addr), 2192 sizeof(local)); 2193 (void) strlcpy(sess->initiator, 2194 inet_ntoa(remoteAddrStorage.sin_addr), 2195 sizeof(sess->initiator)); 2196 #endif 2197 2198 (void) snprintf(targetaddress, sizeof(targetaddress), 2199 "%s:%s,1", local, 2200 iscsi_target_getvar(tgt, "target port")); 2201 iscsi_target_setvar(tgt, "target address", 2202 targetaddress); 2203 iscsi_trace(TRACE_ISCSI_DEBUG, 2204 "IPv4 connection accepted on port %s " 2205 "(local IP %s, remote IP %s)\n", 2206 iscsi_target_getvar(tgt, "target port"), 2207 local, sess->initiator); 2208 iscsi_trace(TRACE_ISCSI_DEBUG, 2209 "TargetAddress = \"%s\"\n", targetaddress); 2210 break; 2211 2212 case AF_INET6: 2213 sess->address_family = 6; 2214 (void) memset(&localAddrStorage6, 0x0, 2215 localAddrLen = sizeof(localAddrStorage6)); 2216 if (getsockname(sess->sock, (struct sockaddr *)(void *) 2217 &localAddrStorage6, &localAddrLen) < 0) { 2218 iscsi_err(__FILE__, __LINE__, 2219 "getsockname() failed\n"); 2220 goto done; 2221 } 2222 2223 (void) memset(&remoteAddrStorage6, 0x0, 2224 remoteAddrLen = sizeof(remoteAddrStorage6)); 2225 if (getpeername(sess->sock, (struct sockaddr *)(void *) 2226 &remoteAddrStorage6, &remoteAddrLen) < 0) { 2227 iscsi_err(__FILE__, __LINE__, 2228 "iscsi_sock_getpeername() failed\n"); 2229 goto done; 2230 } 2231 2232 if (getnameinfo((struct sockaddr *)(void *) 2233 &localAddrStorage6, sizeof(localAddrStorage6), 2234 local, sizeof(local), NULL, 0, 2235 NI_NUMERICHOST) < 0) { 2236 iscsi_err(__FILE__, __LINE__, 2237 "getnameinfo local failed\n"); 2238 } 2239 if (getnameinfo((struct sockaddr *)(void *) 2240 &remoteAddrStorage6, 2241 sizeof(remoteAddrStorage6), remote, 2242 sizeof(remote), NULL, 0, NI_NUMERICHOST) < 0) { 2243 iscsi_err(__FILE__, __LINE__, 2244 "getnameinfo remote failed\n"); 2245 } 2246 (void) strlcpy(sess->initiator, remote, 2247 sizeof(sess->initiator)); 2248 (void) snprintf(targetaddress, sizeof(targetaddress), 2249 "%s:%s,1", local, 2250 iscsi_target_getvar(tgt, "target port")); 2251 iscsi_target_setvar(tgt, "target address", 2252 targetaddress); 2253 iscsi_trace(TRACE_ISCSI_DEBUG, 2254 "IPv6 connection accepted on port %s " 2255 "(local IP %s, remote IP %s)\n", 2256 iscsi_target_getvar(tgt, "target port"), 2257 local, sess->initiator); 2258 iscsi_trace(TRACE_ISCSI_DEBUG, 2259 "TargetAddress = \"%s\"\n", targetaddress); 2260 break; 2261 } 2262 if (iscsi_thread_create(&sess->worker.thread, 2263 (void *) worker_proc_t, sess) != 0) { 2264 iscsi_err(__FILE__, __LINE__, 2265 "iscsi_thread_create() failed\n"); 2266 goto done; 2267 } 2268 } 2269 done: 2270 tgt->listener_listening--; 2271 return 0; 2272 } 2273 2274 /* write the pid to the pid file */ 2275 void 2276 iscsi_target_write_pidfile(const char *f) 2277 { 2278 FILE *fp; 2279 2280 if (f == NULL) { 2281 f = _PATH_ISCSI_PID_FILE; 2282 } 2283 if ((fp = fopen(f, "w")) == NULL) { 2284 (void) fprintf(stderr, "Couldn't create pid file \"%s\": %s", 2285 f, strerror(errno)); 2286 } else { 2287 (void) fprintf(fp, "%ld\n", (long) getpid()); 2288 (void) fclose(fp); 2289 } 2290 } 2291 2292 /* set a variable */ 2293 int 2294 iscsi_target_setvar(iscsi_target_t *tgt, const char *name, const char *value) 2295 { 2296 int i; 2297 2298 if ((i = findvar(tgt, name)) < 0) { 2299 /* add the element to the array */ 2300 size_arrays(tgt, tgt->size + 15); 2301 tgt->name[i = tgt->c++] = strdup(name); 2302 } else { 2303 /* replace the element in the array */ 2304 if (tgt->value[i]) { 2305 (void) free(tgt->value[i]); 2306 tgt->value[i] = NULL; 2307 } 2308 } 2309 /* sanity checks for range of values would go here */ 2310 tgt->value[i] = strdup(value); 2311 return 1; 2312 } 2313 2314 /* get a variable's value (NULL if not set) */ 2315 char * 2316 iscsi_target_getvar(iscsi_target_t *tgt, const char *name) 2317 { 2318 int i; 2319 2320 return ((i = findvar(tgt, name)) < 0) ? NULL : tgt->value[i]; 2321 } 2322