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