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