1 /* $NetBSD: iscsi_rcv.c,v 1.27 2024/11/03 10:50:21 mlelstv Exp $ */ 2 3 /*- 4 * Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Wasabi Systems, Inc. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 #include "iscsi_globals.h" 32 33 #include <sys/file.h> 34 #include <sys/socket.h> 35 #include <sys/socketvar.h> 36 37 /*****************************************************************************/ 38 39 /* 40 * my_soo_read: 41 * Replacement for soo_read with flag handling. 42 * 43 * Parameter: 44 * conn The connection 45 * u The uio descriptor 46 * flags Read flags 47 * 48 * Returns: 0 on success, else 1 49 */ 50 51 STATIC int 52 my_soo_read(connection_t *conn, struct uio *u, int flags) 53 { 54 struct socket *so; 55 int ret; 56 #ifdef ISCSI_DEBUG 57 size_t resid = u->uio_resid; 58 #endif 59 60 DEBC(conn, 99, ("soo_read req: %zu\n", resid)); 61 62 rw_enter(&conn->c_sock_rw, RW_READER); 63 if (conn->c_sock == NULL) { 64 ret = EIO; 65 } else { 66 so = conn->c_sock->f_socket; 67 if (flags & MSG_WAITALL) { 68 flags &= ~MSG_WAITALL; 69 do { 70 int oresid = u->uio_resid; 71 ret = (*so->so_receive)(so, NULL, u, 72 NULL, NULL, &flags); 73 if (!ret && u->uio_resid == oresid) 74 break; 75 } while (!ret && u->uio_resid > 0); 76 } else { 77 ret = (*so->so_receive)(so, NULL, u, 78 NULL, NULL, &flags); 79 } 80 } 81 82 rw_exit(&conn->c_sock_rw); 83 84 if (ret || (flags != MSG_DONTWAIT && u->uio_resid)) { 85 DEBC(conn, 1, ("Read failed (ret: %d, req: %zu, out: %zu)\n", 86 ret, resid, u->uio_resid)); 87 if (ret) 88 handle_connection_error(conn, ISCSI_STATUS_SOCKET_ERROR, 89 RECOVER_CONNECTION); 90 return 1; 91 } 92 return 0; 93 } 94 95 96 /* 97 * try_resynch_receive: 98 * Skip over everything in the socket's receive buffer, in the hope of 99 * ending up at the start of a new PDU. 100 * 101 * Parameter: 102 * conn The connection 103 */ 104 105 STATIC void 106 try_resynch_receive(connection_t *conn) 107 { 108 uint8_t buffer[64]; 109 struct uio uio; 110 struct iovec io_vec; 111 int rc; 112 113 uio.uio_rw = UIO_READ; 114 UIO_SETUP_SYSSPACE(&uio); 115 116 do { 117 io_vec.iov_base = buffer; 118 uio.uio_iov = &io_vec; 119 uio.uio_iovcnt = 1; 120 uio.uio_resid = io_vec.iov_len = sizeof(buffer); 121 122 rc = my_soo_read(conn, &uio, MSG_DONTWAIT); 123 DEBC(conn, 9, ("try_resynch_receive: rc = %d, resid = %zu\n", 124 rc, uio.uio_resid)); 125 } while (!rc && !uio.uio_resid); 126 } 127 128 129 /* 130 * ccb_from_itt 131 * Translate ITT into CCB pointer. 132 * 133 * Parameter: 134 * conn The connection 135 * itt The Initiator Task Tag 136 * 137 * Returns: 138 * Pointer to CCB, or NULL if ITT is not a valid CCB index. 139 */ 140 141 STATIC ccb_t * 142 ccb_from_itt(connection_t *conn, uint32_t itt) 143 { 144 ccb_t *ccb; 145 int cidx; 146 147 if (itt == 0xffffffff) 148 return NULL; 149 150 cidx = itt & 0xff; 151 if (cidx >= CCBS_PER_SESSION) 152 return NULL; 153 154 ccb = &conn->c_session->s_ccb[cidx]; 155 156 if (ccb->ccb_ITT != itt) { 157 DEBC(conn, 0, 158 ("ccb_from_itt: received invalid CCB itt %08x != %08x\n", 159 itt, ccb->ccb_ITT)); 160 return NULL; 161 } 162 163 if (ccb->ccb_disp <= CCBDISP_BUSY) { 164 DEBC(conn, 0, 165 ("ccb_from_itt: received CCB with invalid disp %d\n", 166 ccb->ccb_disp)); 167 return NULL; 168 } 169 170 return ccb; 171 } 172 173 174 /* 175 * read_pdu_data: 176 * Initialize the uio structure for receiving everything after the 177 * header, including data (if present), and padding. Read the data. 178 * 179 * Parameter: 180 * pdu The PDU 181 * data Pointer to data (may be NULL for auto-allocation) 182 * offset The offset into the data pointer 183 * 184 * Returns: 0 on success 185 * 1 if an error occurs during read 186 * -1 if the data digest was incorrect (PDU must be ignored) 187 */ 188 189 STATIC int 190 read_pdu_data(pdu_t *pdu, uint8_t *data, uint32_t offset) 191 { 192 static uint8_t pad_bytes[4]; 193 uint32_t len, digest; 194 struct uio *uio; 195 int i, pad; 196 connection_t *conn = pdu->pdu_connection; 197 198 DEB(15, ("read_pdu_data: data segment length = %d\n", 199 ntoh3(pdu->pdu_hdr.pduh_DataSegmentLength))); 200 if (!(len = ntoh3(pdu->pdu_hdr.pduh_DataSegmentLength))) { 201 return 0; 202 } 203 pad = len & 0x03; 204 if (pad) { 205 pad = 4 - pad; 206 } 207 208 KASSERT(data != NULL || offset == 0); 209 210 if (data == NULL) { 211 /* 212 * NOTE: Always allocate 2 extra bytes when reading temp data, 213 * since temp data is mostly used for received text, and we can 214 * make sure there's a double zero at the end of the data to mark EOF. 215 */ 216 if ((data = (uint8_t *) malloc(len + 2, M_TEMP, M_WAITOK)) == NULL) { 217 DEBOUT(("ran out of mem on receive\n")); 218 handle_connection_error(pdu->pdu_connection, 219 ISCSI_STATUS_NO_RESOURCES, LOGOUT_SESSION); 220 return 1; 221 } 222 pdu->pdu_temp_data = data; 223 pdu->pdu_temp_data_len = len; 224 } 225 226 pdu->pdu_io_vec[0].iov_base = data + offset; 227 pdu->pdu_io_vec[0].iov_len = len; 228 229 uio = &pdu->pdu_uio; 230 231 uio->uio_iov = pdu->pdu_io_vec; 232 uio->uio_iovcnt = 1; 233 uio->uio_rw = UIO_READ; 234 uio->uio_resid = len; 235 UIO_SETUP_SYSSPACE(uio); 236 237 if (pad) { 238 uio->uio_iovcnt++; 239 uio->uio_iov[1].iov_base = pad_bytes; 240 uio->uio_iov[1].iov_len = pad; 241 uio->uio_resid += pad; 242 } 243 244 if (conn->c_DataDigest) { 245 i = uio->uio_iovcnt++; 246 pdu->pdu_io_vec[i].iov_base = &pdu->pdu_data_digest; 247 pdu->pdu_io_vec[i].iov_len = 4; 248 uio->uio_resid += 4; 249 } 250 251 /* get the data */ 252 if (my_soo_read(conn, &pdu->pdu_uio, MSG_WAITALL) != 0) { 253 return 1; 254 } 255 if (conn->c_DataDigest) { 256 digest = gen_digest_2(data, len, pad_bytes, pad); 257 258 if (digest != pdu->pdu_data_digest) { 259 DEBOUT(("Data Digest Error: comp = %08x, rx = %08x\n", 260 digest, pdu->pdu_data_digest)); 261 switch (pdu->pdu_hdr.pduh_Opcode & OPCODE_MASK) { 262 case TOP_SCSI_Response: 263 case TOP_Text_Response: 264 send_snack(pdu->pdu_connection, pdu, NULL, SNACK_STATUS_NAK); 265 break; 266 267 case TOP_SCSI_Data_in: 268 send_snack(pdu->pdu_connection, pdu, NULL, SNACK_DATA_NAK); 269 break; 270 271 default: 272 /* ignore all others */ 273 break; 274 } 275 return -1; 276 } 277 } 278 return 0; 279 } 280 281 282 /* 283 * collect_text_data 284 * Handle text continuation in login and text response PDUs 285 * 286 * Parameter: 287 * pdu The received PDU 288 * req_CCB The CCB associated with the original request 289 * 290 * Returns: -1 if continue flag is set 291 * 0 if text is complete 292 * +1 if an error occurred (out of resources) 293 */ 294 STATIC int 295 collect_text_data(pdu_t *pdu, ccb_t *req_ccb) 296 { 297 298 if (req_ccb->ccb_text_data) { 299 int nlen; 300 uint8_t *newp; 301 302 nlen = req_ccb->ccb_text_len + pdu->pdu_temp_data_len; 303 /* Note: allocate extra 2 bytes for text terminator */ 304 if ((newp = malloc(nlen + 2, M_TEMP, M_WAITOK)) == NULL) { 305 DEBOUT(("Collect Text Data: Out of Memory, ccb = %p\n", req_ccb)); 306 req_ccb->ccb_status = ISCSI_STATUS_NO_RESOURCES; 307 /* XXX where is CCB freed? */ 308 return 1; 309 } 310 memcpy(newp, req_ccb->ccb_text_data, req_ccb->ccb_text_len); 311 memcpy(&newp[req_ccb->ccb_text_len], pdu->pdu_temp_data, pdu->pdu_temp_data_len); 312 313 free(req_ccb->ccb_text_data, M_TEMP); 314 free(pdu->pdu_temp_data, M_TEMP); 315 316 req_ccb->ccb_text_data = NULL; 317 pdu->pdu_temp_data = newp; 318 pdu->pdu_temp_data_len = nlen; 319 } 320 321 if (pdu->pdu_hdr.pduh_Flags & FLAG_CONTINUE) { 322 req_ccb->ccb_text_data = pdu->pdu_temp_data; 323 req_ccb->ccb_text_len = pdu->pdu_temp_data_len; 324 pdu->pdu_temp_data = NULL; 325 326 acknowledge_text(req_ccb->ccb_connection, pdu, req_ccb); 327 return -1; 328 } 329 return 0; 330 } 331 332 333 /* 334 * check_StatSN 335 * Check received vs. expected StatSN 336 * 337 * Parameter: 338 * conn The connection 339 * nw_sn The received StatSN in network byte order 340 * ack Acknowledge this SN if TRUE 341 */ 342 343 STATIC int 344 check_StatSN(connection_t *conn, uint32_t nw_sn, bool ack) 345 { 346 int rc; 347 uint32_t sn = ntohl(nw_sn); 348 349 rc = add_sernum(&conn->c_StatSN_buf, sn); 350 351 if (ack) 352 ack_sernum(&conn->c_StatSN_buf, sn); 353 354 if (rc != 1) { 355 if (rc == 0) { 356 DEBOUT(("Duplicate PDU, ExpSN %d, Recvd: %d\n", 357 conn->c_StatSN_buf.ExpSN, sn)); 358 return -1; 359 } 360 361 if (rc < 0) { 362 DEBOUT(("Excessive outstanding Status PDUs, ExpSN %d, Recvd: %d\n", 363 conn->c_StatSN_buf.ExpSN, sn)); 364 handle_connection_error(conn, ISCSI_STATUS_PDUS_LOST, 365 RECOVER_CONNECTION); 366 return rc; 367 } 368 369 DEBOUT(("Missing Status PDUs: First %d, num: %d\n", 370 conn->c_StatSN_buf.ExpSN, rc - 1)); 371 if (conn->c_state == ST_FULL_FEATURE && 372 conn->c_session->s_ErrorRecoveryLevel) { 373 snack_missing(conn, NULL, SNACK_STATUS_NAK, 374 conn->c_StatSN_buf.ExpSN, rc - 1); 375 } else { 376 DEBOUT(("StatSN killing connection (State = %d, " 377 "ErrorRecoveryLevel = %d)\n", 378 conn->c_state, conn->c_session->s_ErrorRecoveryLevel)); 379 handle_connection_error(conn, ISCSI_STATUS_PDUS_LOST, 380 RECOVER_CONNECTION); 381 return -1; 382 } 383 } 384 return 0; 385 } 386 387 388 /* 389 * check_CmdSN 390 * Check received vs. expected CmdSN 391 * 392 * Parameter: 393 * conn The connection 394 * nw_sn The received ExpCmdSN in network byte order 395 */ 396 397 STATIC void 398 check_CmdSN(connection_t *conn, uint32_t nw_sn) 399 { 400 uint32_t sn = ntohl(nw_sn); 401 ccb_t *ccb, *nxt; 402 403 TAILQ_FOREACH_SAFE(ccb, &conn->c_ccbs_waiting, ccb_chain, nxt) { 404 DEBC(conn, 10, 405 ("CheckCmdSN - CmdSN=%d, ExpCmdSn=%d, waiting=%p, flags=%x\n", 406 ccb->ccb_CmdSN, sn, ccb->ccb_pdu_waiting, ccb->ccb_flags)); 407 if (ccb->ccb_pdu_waiting != NULL && 408 sn_a_lt_b(sn, ccb->ccb_CmdSN) && 409 !(ccb->ccb_flags & CCBF_GOT_RSP)) { 410 DEBC(conn, 1, ("CheckCmdSN resending - CmdSN=%d, ExpCmdSn=%d\n", 411 ccb->ccb_CmdSN, sn)); 412 413 ccb->ccb_total_tries++; 414 415 if (++ccb->ccb_num_timeouts > MAX_CCB_TIMEOUTS || 416 ccb->ccb_total_tries > MAX_CCB_TRIES) { 417 handle_connection_error(conn, 418 ISCSI_STATUS_TIMEOUT, 419 (ccb->ccb_total_tries <= MAX_CCB_TRIES) 420 ? RECOVER_CONNECTION 421 : LOGOUT_CONNECTION); 422 break; 423 } else { 424 resend_pdu(ccb); 425 } 426 } 427 428 /* 429 * The target can respond to a NOP-In before subsequent 430 * commands are processed. So our CmdSN can exceed the 431 * returned ExpCmdSN by the number of commands that are 432 * in flight. Adjust the expected value accordingly. 433 */ 434 sn++; 435 } 436 } 437 438 439 /* 440 * receive_login_pdu 441 * Handle receipt of a login response PDU. 442 * 443 * Parameter: 444 * conn The connection 445 * pdu The PDU 446 * req_CCB The CCB associated with the original request (if any) 447 */ 448 449 STATIC int 450 receive_login_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb) 451 { 452 int rc; 453 454 DEBC(conn, 9, ("Received Login Response PDU, op=%x, flags=%x, sn=%u\n", 455 pdu->pdu_hdr.pduh_Opcode, pdu->pdu_hdr.pduh_Flags, 456 ntohl(pdu->pdu_hdr.pduh_p.login_rsp.StatSN))); 457 458 if (req_ccb == NULL) { 459 /* Duplicate?? */ 460 DEBOUT(("Received duplicate login response (no associated CCB)\n")); 461 return -1; 462 } 463 464 if (pdu->pdu_hdr.pduh_p.login_rsp.StatusClass) { 465 DEBC(conn, 1, ("Login problem - Class = %x, Detail = %x\n", 466 pdu->pdu_hdr.pduh_p.login_rsp.StatusClass, 467 pdu->pdu_hdr.pduh_p.login_rsp.StatusDetail)); 468 wake_ccb(req_ccb, ISCSI_STATUS_LOGIN_FAILED); 469 return 0; 470 } 471 472 if (!conn->c_StatSN_buf.next_sn) { 473 conn->c_StatSN_buf.next_sn = conn->c_StatSN_buf.ExpSN = 474 ntohl(pdu->pdu_hdr.pduh_p.login_rsp.StatSN) + 1; 475 } else if (check_StatSN(conn, pdu->pdu_hdr.pduh_p.login_rsp.StatSN, TRUE)) 476 return -1; 477 478 if (pdu->pdu_temp_data_len) { 479 if ((rc = collect_text_data(pdu, req_ccb)) != 0) 480 return max(rc, 0); 481 } 482 483 negotiate_login(conn, pdu, req_ccb); 484 485 /* negotiate_login will decide whether login is complete or not */ 486 return 0; 487 } 488 489 490 /* 491 * receive_text_response_pdu 492 * Handle receipt of a text response PDU. 493 * 494 * Parameter: 495 * conn The connection 496 * pdu The PDU 497 * req_CCB The CCB associated with the original request (if any) 498 */ 499 500 STATIC int 501 receive_text_response_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb) 502 { 503 int rc; 504 505 DEBC(conn, 9, ("Received Text Response PDU, op=%x, flags=%x\n", 506 pdu->pdu_hdr.pduh_Opcode, pdu->pdu_hdr.pduh_Flags)); 507 508 if (check_StatSN(conn, pdu->pdu_hdr.pduh_p.text_rsp.StatSN, TRUE)) { 509 return -1; 510 } 511 if (req_ccb == NULL) { 512 DEBOUT(("Received unsolicited text response\n")); 513 handle_connection_error(conn, ISCSI_STATUS_TARGET_ERROR, 514 LOGOUT_CONNECTION); 515 return -1; 516 } 517 518 if (req_ccb->ccb_pdu_waiting != NULL) { 519 ccb_timeout_start(req_ccb, COMMAND_TIMEOUT); 520 req_ccb->ccb_num_timeouts = 0; 521 } 522 523 if ((rc = collect_text_data(pdu, req_ccb)) != 0) { 524 return max(0, rc); 525 } 526 negotiate_text(conn, pdu, req_ccb); 527 528 return 0; 529 } 530 531 532 /* 533 * receive_logout_pdu 534 * Handle receipt of a logout response PDU. 535 * 536 * Parameter: 537 * conn The connection 538 * pdu The PDU 539 * req_CCB The CCB associated with the original request (if any) 540 */ 541 542 STATIC int 543 receive_logout_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb) 544 { 545 bool otherconn; 546 uint8_t response; 547 uint32_t status; 548 549 otherconn = (req_ccb != NULL) ? (req_ccb->ccb_flags & CCBF_OTHERCONN) != 0 : 1; 550 response = pdu->pdu_hdr.pduh_OpcodeSpecific [0]; 551 DEBC(conn, 1, 552 ("Received Logout PDU - CCB = %p, otherconn=%d, response=%d\n", 553 req_ccb, otherconn, response)); 554 555 if (req_ccb == NULL) 556 return 0; 557 558 if (otherconn && check_StatSN(conn, pdu->pdu_hdr.pduh_p.logout_rsp.StatSN, TRUE)) 559 return -1; 560 561 switch (response) { 562 case 0: 563 status = ISCSI_STATUS_SUCCESS; 564 break; 565 case 1: 566 status = ISCSI_STATUS_LOGOUT_CID_NOT_FOUND; 567 break; 568 case 2: 569 status = ISCSI_STATUS_LOGOUT_RECOVERY_NS; 570 break; 571 default: 572 status = ISCSI_STATUS_LOGOUT_ERROR; 573 break; 574 } 575 576 if (conn->c_session->s_ErrorRecoveryLevel >= 2 && response != 1) { 577 connection_t *refconn = (otherconn) ? req_ccb->ccb_par : conn; 578 579 refconn->c_Time2Wait = ntohs(pdu->pdu_hdr.pduh_p.logout_rsp.Time2Wait); 580 refconn->c_Time2Retain = ntohs(pdu->pdu_hdr.pduh_p.logout_rsp.Time2Retain); 581 } 582 583 wake_ccb(req_ccb, status); 584 585 mutex_enter(&conn->c_lock); 586 if (!otherconn && conn->c_state == ST_LOGOUT_SENT) { 587 conn->c_terminating = ISCSI_STATUS_LOGOUT; 588 conn->c_state = ST_SETTLING; 589 conn->c_loggedout = (response) ? LOGOUT_FAILED : LOGOUT_SUCCESS; 590 mutex_exit(&conn->c_lock); 591 592 connection_timeout_stop(conn); 593 594 /* let send thread take over next step of cleanup */ 595 mutex_enter(&conn->c_lock); 596 cv_broadcast(&conn->c_conn_cv); 597 } 598 mutex_exit(&conn->c_lock); 599 600 return !otherconn; 601 } 602 603 604 /* 605 * receive_data_in_pdu 606 * Handle receipt of a data in PDU. 607 * 608 * Parameter: 609 * conn The connection 610 * pdu The PDU 611 * req_CCB The CCB associated with the original request (if any) 612 */ 613 614 STATIC int 615 receive_data_in_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb) 616 { 617 uint32_t dsl, sn; 618 bool done; 619 int rc; 620 621 dsl = ntoh3(pdu->pdu_hdr.pduh_DataSegmentLength); 622 623 if (req_ccb == NULL || !req_ccb->ccb_data_in || !req_ccb->ccb_data_len) { 624 DEBOUT(("Received Data In, but req_ccb not waiting for it, ignored\n")); 625 return 0; 626 } 627 req_ccb->ccb_flags |= CCBF_GOT_RSP; 628 629 if (req_ccb->ccb_pdu_waiting != NULL) { 630 ccb_timeout_start(req_ccb, COMMAND_TIMEOUT); 631 req_ccb->ccb_num_timeouts = 0; 632 } 633 634 sn = ntohl(pdu->pdu_hdr.pduh_p.data_in.DataSN); 635 636 if ((rc = add_sernum(&req_ccb->ccb_DataSN_buf, sn)) != 1) { 637 if (!rc) { 638 return -1; 639 } 640 if (rc < 0) { 641 DEBOUT(("Excessive outstanding Data PDUs\n")); 642 handle_connection_error(req_ccb->ccb_connection, 643 ISCSI_STATUS_PDUS_LOST, LOGOUT_CONNECTION); 644 return -1; 645 } 646 DEBOUT(("Missing Data PDUs: First %d, num: %d\n", 647 req_ccb->ccb_DataSN_buf.ExpSN, rc - 1)); 648 649 if (conn->c_state == ST_FULL_FEATURE && 650 conn->c_session->s_ErrorRecoveryLevel) { 651 snack_missing(req_ccb->ccb_connection, req_ccb, 652 SNACK_DATA_NAK, req_ccb->ccb_DataSN_buf.ExpSN, 653 rc - 1); 654 } else { 655 DEBOUT(("Killing connection (State=%d, ErrorRecoveryLevel=%d)\n", 656 conn->c_state, conn->c_session->s_ErrorRecoveryLevel)); 657 handle_connection_error(conn, ISCSI_STATUS_PDUS_LOST, 658 LOGOUT_CONNECTION); 659 return -1; 660 } 661 } 662 663 ack_sernum(&req_ccb->ccb_DataSN_buf, sn); 664 665 req_ccb->ccb_xfer_len += dsl; 666 667 if ((pdu->pdu_hdr.pduh_Flags & FLAG_ACK) && conn->c_session->s_ErrorRecoveryLevel) 668 send_snack(conn, pdu, req_ccb, SNACK_DATA_ACK); 669 670 done = sn_empty(&req_ccb->ccb_DataSN_buf); 671 672 if (pdu->pdu_hdr.pduh_Flags & FLAG_STATUS) { 673 DEBC(conn, 10, ("Rx Data In %d, done = %d\n", 674 req_ccb->ccb_CmdSN, done)); 675 676 req_ccb->ccb_flags |= CCBF_COMPLETE; 677 /* successful transfer, reset recover count */ 678 conn->c_recover = 0; 679 680 if (done) 681 wake_ccb(req_ccb, ISCSI_STATUS_SUCCESS); 682 if (check_StatSN(conn, pdu->pdu_hdr.pduh_p.data_in.StatSN, done)) 683 return -1; 684 685 } else if (done && (req_ccb->ccb_flags & CCBF_COMPLETE)) { 686 wake_ccb(req_ccb, ISCSI_STATUS_SUCCESS); 687 } 688 /* else wait for command response */ 689 690 return 0; 691 } 692 693 694 /* 695 * receive_r2t_pdu 696 * Handle receipt of a R2T PDU. 697 * 698 * Parameter: 699 * conn The connection 700 * pdu The PDU 701 * req_CCB The CCB associated with the original request (if any) 702 */ 703 704 STATIC int 705 receive_r2t_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb) 706 { 707 708 DEBC(conn, 10, ("Received R2T PDU - CCB = %p\n", req_ccb)); 709 710 if (req_ccb != NULL) { 711 if (req_ccb->ccb_pdu_waiting != NULL) { 712 ccb_timeout_start(req_ccb, COMMAND_TIMEOUT); 713 req_ccb->ccb_num_timeouts = 0; 714 } 715 send_data_out(conn, pdu, req_ccb, CCBDISP_NOWAIT, TRUE); 716 } 717 718 return 0; 719 } 720 721 722 /* 723 * receive_command_response_pdu 724 * Handle receipt of a command response PDU. 725 * 726 * Parameter: 727 * conn The connection 728 * pdu The PDU 729 * req_CCB The CCB associated with the original request (if any) 730 */ 731 732 STATIC int 733 receive_command_response_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb) 734 { 735 int len, rc; 736 bool done; 737 uint32_t status; 738 739 /* Read any provided data */ 740 if (pdu->pdu_temp_data_len && req_ccb != NULL && req_ccb->ccb_sense_len_req) { 741 len = min(req_ccb->ccb_sense_len_req, 742 ntohs(*((uint16_t *) pdu->pdu_temp_data))); 743 memcpy(req_ccb->ccb_sense_ptr, ((uint16_t *) pdu->pdu_temp_data) + 1, 744 len); 745 req_ccb->ccb_sense_len_got = len; 746 } 747 748 if (req_ccb == NULL) { 749 /* Assume duplicate... */ 750 DEBOUT(("Possibly duplicate command response (no associated CCB)\n")); 751 return -1; 752 } 753 754 if (req_ccb->ccb_flags & CCBF_COMPLETE) { 755 DEBOUT(("Possibly duplicate command response (tagged as COMPLETE)\n")); 756 return -1; 757 } 758 759 if (req_ccb->ccb_pdu_waiting != NULL) { 760 ccb_timeout_start(req_ccb, COMMAND_TIMEOUT); 761 req_ccb->ccb_num_timeouts = 0; 762 } 763 764 req_ccb->ccb_flags |= CCBF_COMPLETE; 765 conn->c_recover = 0; /* successful transfer, reset recover count */ 766 767 if (pdu->pdu_hdr.pduh_OpcodeSpecific[0]) { /* Response */ 768 status = ISCSI_STATUS_TARGET_FAILURE; 769 } else { 770 switch (pdu->pdu_hdr.pduh_OpcodeSpecific[1]) { /* Status */ 771 case 0x00: 772 status = ISCSI_STATUS_SUCCESS; 773 break; 774 775 case 0x02: 776 status = ISCSI_STATUS_CHECK_CONDITION; 777 break; 778 779 case 0x08: 780 status = ISCSI_STATUS_TARGET_BUSY; 781 break; 782 783 default: 784 status = ISCSI_STATUS_TARGET_ERROR; 785 break; 786 } 787 } 788 789 if (pdu->pdu_hdr.pduh_Flags & (FLAG_OVERFLOW | FLAG_UNDERFLOW)) 790 req_ccb->ccb_residual = ntohl(pdu->pdu_hdr.pduh_p.response.ResidualCount); 791 792 done = status || sn_empty(&req_ccb->ccb_DataSN_buf); 793 794 DEBC(conn, 10, ("Rx Response: CmdSN %d, rsp = %x, status = %x\n", 795 req_ccb->ccb_CmdSN, 796 pdu->pdu_hdr.pduh_OpcodeSpecific[0], 797 pdu->pdu_hdr.pduh_OpcodeSpecific[1])); 798 799 rc = check_StatSN(conn, pdu->pdu_hdr.pduh_p.response.StatSN, done); 800 801 if (done) 802 wake_ccb(req_ccb, status); 803 804 return rc; 805 } 806 807 808 /* 809 * receive_asynch_pdu 810 * Handle receipt of an asynchronous message PDU. 811 * 812 * Parameter: 813 * conn The connection 814 * pdu The PDU 815 */ 816 817 STATIC int 818 receive_asynch_pdu(connection_t *conn, pdu_t *pdu) 819 { 820 821 DEBOUT(("Received Asynch PDU, Event %d\n", pdu->pdu_hdr.pduh_p.asynch.AsyncEvent)); 822 823 switch (pdu->pdu_hdr.pduh_p.asynch.AsyncEvent) { 824 case 0: /* SCSI Asynch event. Don't know what to do with it... */ 825 break; 826 827 case 1: /* Target requests logout. */ 828 if (conn->c_session->s_active_connections > 1) { 829 kill_connection(conn, ISCSI_STATUS_TARGET_LOGOUT, 830 LOGOUT_CONNECTION, FALSE); 831 } else { 832 kill_session(conn->c_session->s_id, 833 ISCSI_STATUS_TARGET_LOGOUT, LOGOUT_SESSION, 834 FALSE); 835 } 836 break; 837 838 case 2: /* Target is dropping connection */ 839 conn = find_connection(conn->c_session, 840 ntohs(pdu->pdu_hdr.pduh_p.asynch.Parameter1)); 841 if (conn != NULL) { 842 conn->c_Time2Wait = 843 ntohs(pdu->pdu_hdr.pduh_p.asynch.Parameter2); 844 conn->c_Time2Retain = 845 ntohs(pdu->pdu_hdr.pduh_p.asynch.Parameter3); 846 kill_connection(conn, ISCSI_STATUS_TARGET_DROP, 847 NO_LOGOUT, TRUE); 848 } 849 break; 850 851 case 3: /* Target is dropping all connections of session */ 852 conn->c_session->s_DefaultTime2Wait = 853 ntohs(pdu->pdu_hdr.pduh_p.asynch.Parameter2); 854 conn->c_session->s_DefaultTime2Retain = 855 ntohs(pdu->pdu_hdr.pduh_p.asynch.Parameter3); 856 kill_session(conn->c_session->s_id, 857 ISCSI_STATUS_TARGET_DROP, NO_LOGOUT, 858 TRUE); 859 break; 860 861 case 4: /* Target requests parameter negotiation */ 862 start_text_negotiation(conn); 863 break; 864 865 default: 866 /* ignore */ 867 break; 868 } 869 return 0; 870 } 871 872 873 /* 874 * receive_reject_pdu 875 * Handle receipt of a reject PDU. 876 * 877 * Parameter: 878 * conn The connection 879 * pdu The PDU 880 */ 881 882 STATIC int 883 receive_reject_pdu(connection_t *conn, pdu_t *pdu) 884 { 885 pdu_header_t *hpdu; 886 ccb_t *req_ccb; 887 uint32_t status; 888 889 DEBOUT(("Received Reject PDU, reason = %x, data_len = %d\n", 890 pdu->pdu_hdr.pduh_OpcodeSpecific[0], pdu->pdu_temp_data_len)); 891 892 if (pdu->pdu_temp_data_len >= BHS_SIZE) { 893 hpdu = (pdu_header_t *) pdu->pdu_temp_data; 894 req_ccb = ccb_from_itt(conn, hpdu->pduh_InitiatorTaskTag); 895 896 DEBC(conn, 9, ("Reject PDU ITT (ccb)= %x (%p)\n", 897 hpdu->pduh_InitiatorTaskTag, req_ccb)); 898 if (!req_ccb) { 899 return 0; 900 } 901 switch (pdu->pdu_hdr.pduh_OpcodeSpecific[0]) { 902 case REJECT_DIGEST_ERROR: 903 /* don't retransmit data out */ 904 if ((hpdu->pduh_Opcode & OPCODE_MASK) == IOP_SCSI_Data_out) 905 return 0; 906 resend_pdu(req_ccb); 907 return 0; 908 909 case REJECT_IMMED_COMMAND: 910 case REJECT_LONG_OPERATION: 911 resend_pdu(req_ccb); 912 return 0; 913 914 case REJECT_SNACK: 915 case REJECT_PROTOCOL_ERROR: 916 status = ISCSI_STATUS_PROTOCOL_ERROR; 917 break; 918 919 case REJECT_CMD_NOT_SUPPORTED: 920 status = ISCSI_STATUS_CMD_NOT_SUPPORTED; 921 break; 922 923 case REJECT_INVALID_PDU_FIELD: 924 status = ISCSI_STATUS_PDU_ERROR; 925 break; 926 927 default: 928 status = ISCSI_STATUS_GENERAL_ERROR; 929 break; 930 } 931 932 wake_ccb(req_ccb, status); 933 handle_connection_error(conn, ISCSI_STATUS_PROTOCOL_ERROR, 934 LOGOUT_CONNECTION); 935 } 936 return 0; 937 } 938 939 940 /* 941 * receive_task_management_pdu 942 * Handle receipt of a task management PDU. 943 * 944 * Parameter: 945 * conn The connection 946 * pdu The PDU 947 * req_CCB The CCB associated with the original request (if any) 948 */ 949 950 STATIC int 951 receive_task_management_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb) 952 { 953 uint32_t status; 954 955 DEBC(conn, 2, ("Received Task Management PDU, response %d, req_ccb %p\n", 956 pdu->pdu_hdr.pduh_OpcodeSpecific[0], req_ccb)); 957 958 if (req_ccb != NULL) { 959 switch (pdu->pdu_hdr.pduh_OpcodeSpecific[0]) { /* Response */ 960 case 0: 961 status = ISCSI_STATUS_SUCCESS; 962 break; 963 case 1: 964 status = ISCSI_STATUS_TASK_NOT_FOUND; 965 break; 966 case 2: 967 status = ISCSI_STATUS_LUN_NOT_FOUND; 968 break; 969 case 3: 970 status = ISCSI_STATUS_TASK_ALLEGIANT; 971 break; 972 case 4: 973 status = ISCSI_STATUS_CANT_REASSIGN; 974 break; 975 case 5: 976 status = ISCSI_STATUS_FUNCTION_UNSUPPORTED; 977 break; 978 case 6: 979 status = ISCSI_STATUS_FUNCTION_NOT_AUTHORIZED; 980 break; 981 case 255: 982 status = ISCSI_STATUS_FUNCTION_REJECTED; 983 break; 984 default: 985 status = ISCSI_STATUS_UNKNOWN_REASON; 986 break; 987 } 988 wake_ccb(req_ccb, status); 989 } 990 991 check_StatSN(conn, pdu->pdu_hdr.pduh_p.task_rsp.StatSN, TRUE); 992 993 return 0; 994 } 995 996 997 /* 998 * receive_nop_in_pdu 999 * Handle receipt of a Nop-In PDU. 1000 * 1001 * Parameter: 1002 * conn The connection 1003 * pdu The PDU 1004 * req_CCB The CCB associated with the original request (if any) 1005 */ 1006 1007 STATIC int 1008 receive_nop_in_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb) 1009 { 1010 DEBC(conn, 10, 1011 ("Received NOP_In PDU, req_ccb=%p, ITT=%x, TTT=%x, StatSN=%u\n", 1012 req_ccb, pdu->pdu_hdr.pduh_InitiatorTaskTag, 1013 pdu->pdu_hdr.pduh_p.nop_in.TargetTransferTag, 1014 ntohl(pdu->pdu_hdr.pduh_p.nop_in.StatSN))); 1015 1016 if (pdu->pdu_hdr.pduh_InitiatorTaskTag == 0xffffffff) { 1017 /* this is a target ping - respond with a pong */ 1018 if (pdu->pdu_hdr.pduh_p.nop_in.TargetTransferTag != 0xffffffff) 1019 send_nop_out(conn, pdu); 1020 1021 /* 1022 Any receive resets the connection timeout, but we got a ping, which 1023 means that it's likely the other side was waiting for something to 1024 happen on the connection. If we aren't idle, send a ping right 1025 away to synch counters (don't synch on this ping because other 1026 PDUs may be on the way). 1027 */ 1028 if (TAILQ_FIRST(&conn->c_ccbs_waiting) != NULL) 1029 send_nop_out(conn, NULL); 1030 } else if (req_ccb != NULL) { 1031 /* this is a solicited ping, check CmdSN for lost commands */ 1032 /* and advance StatSN */ 1033 check_CmdSN(conn, pdu->pdu_hdr.pduh_p.nop_in.ExpCmdSN); 1034 1035 wake_ccb(req_ccb, ISCSI_STATUS_SUCCESS); 1036 1037 check_StatSN(conn, pdu->pdu_hdr.pduh_p.nop_in.StatSN, TRUE); 1038 } else { 1039 DEBC(conn, 0, ("Received unsolicted NOP_In, itt=%08x\n", 1040 pdu->pdu_hdr.pduh_InitiatorTaskTag)); 1041 } 1042 1043 return 0; 1044 } 1045 1046 1047 /* 1048 * receive_pdu 1049 * Get parameters, call the appropriate handler for a received PDU. 1050 * 1051 * Parameter: 1052 * conn The connection 1053 * pdu The PDU 1054 * 1055 * Returns: 0 on success, nonzero if the connection is broken. 1056 */ 1057 1058 STATIC int 1059 receive_pdu(connection_t *conn, pdu_t *pdu) 1060 { 1061 ccb_t *req_ccb; 1062 int rc; 1063 uint32_t MaxCmdSN, ExpCmdSN, digest; 1064 session_t *sess = conn->c_session; 1065 1066 if (conn->c_HeaderDigest) { 1067 digest = gen_digest(&pdu->pdu_hdr, BHS_SIZE); 1068 if (digest != pdu->pdu_hdr.pduh_HeaderDigest) { 1069 DEBOUT(("Header Digest Error: comp = %08x, rx = %08x\n", 1070 digest, pdu->pdu_hdr.pduh_HeaderDigest)); 1071 /* try to skip to next PDU */ 1072 try_resynch_receive(conn); 1073 free_pdu(pdu); 1074 return 0; 1075 } 1076 } 1077 1078 DEBC(conn, 10, ("Received PDU StatSN=%u, ExpCmdSN=%u MaxCmdSN=%u ExpDataSN=%u\n", 1079 ntohl(pdu->pdu_hdr.pduh_p.response.StatSN), 1080 ntohl(pdu->pdu_hdr.pduh_p.response.ExpCmdSN), 1081 ntohl(pdu->pdu_hdr.pduh_p.response.MaxCmdSN), 1082 ntohl(pdu->pdu_hdr.pduh_p.response.ExpDataSN))); 1083 1084 req_ccb = ccb_from_itt(conn, pdu->pdu_hdr.pduh_InitiatorTaskTag); 1085 1086 if (req_ccb != NULL && req_ccb->ccb_data_in && req_ccb->ccb_data_len && 1087 (pdu->pdu_hdr.pduh_Opcode & OPCODE_MASK) == TOP_SCSI_Data_in) { 1088 uint32_t dsl, offset; 1089 1090 dsl = ntoh3(pdu->pdu_hdr.pduh_DataSegmentLength); 1091 offset = ntohl(pdu->pdu_hdr.pduh_p.data_in.BufferOffset); 1092 1093 if ((offset + dsl) > req_ccb->ccb_data_len) { 1094 DEBOUT(("Received more data than requested (len %d, offset %d)\n", 1095 dsl, offset)); 1096 handle_connection_error(conn, ISCSI_STATUS_TARGET_ERROR, NO_LOGOUT); 1097 return 1; 1098 } 1099 DEBC(conn, 10, 1100 ("Received Data in PDU - CCB = %p, Datalen = %d, Offset = %d\n", 1101 req_ccb, dsl, offset)); 1102 1103 rc = read_pdu_data(pdu, req_ccb->ccb_data_ptr, offset); 1104 } else { 1105 rc = read_pdu_data(pdu, NULL, 0); 1106 } 1107 if (!rc && (conn->c_state <= ST_WINDING_DOWN || 1108 (pdu->pdu_hdr.pduh_Opcode & OPCODE_MASK) == TOP_Logout_Response)) { 1109 1110 switch (pdu->pdu_hdr.pduh_Opcode & OPCODE_MASK) { 1111 case TOP_NOP_In: 1112 rc = receive_nop_in_pdu(conn, pdu, req_ccb); 1113 break; 1114 1115 case TOP_SCSI_Response: 1116 rc = receive_command_response_pdu(conn, pdu, req_ccb); 1117 break; 1118 1119 case TOP_SCSI_Task_Management: 1120 rc = receive_task_management_pdu(conn, pdu, req_ccb); 1121 break; 1122 1123 case TOP_Login_Response: 1124 rc = receive_login_pdu(conn, pdu, req_ccb); 1125 break; 1126 1127 case TOP_Text_Response: 1128 rc = receive_text_response_pdu(conn, pdu, req_ccb); 1129 break; 1130 1131 case TOP_SCSI_Data_in: 1132 rc = receive_data_in_pdu(conn, pdu, req_ccb); 1133 break; 1134 1135 case TOP_Logout_Response: 1136 rc = receive_logout_pdu(conn, pdu, req_ccb); 1137 break; 1138 1139 case TOP_R2T: 1140 rc = receive_r2t_pdu(conn, pdu, req_ccb); 1141 break; 1142 1143 case TOP_Asynchronous_Message: 1144 rc = receive_asynch_pdu(conn, pdu); 1145 break; 1146 1147 case TOP_Reject: 1148 rc = receive_reject_pdu(conn, pdu); 1149 break; 1150 1151 default: 1152 DEBOUT(("Received Invalid Opcode %x\n", pdu->pdu_hdr.pduh_Opcode)); 1153 try_resynch_receive(conn); 1154 rc = -1; 1155 break; 1156 } 1157 } 1158 1159 free_pdu(pdu); 1160 if (rc) 1161 return rc; 1162 1163 /* MaxCmdSN and ExpCmdSN are in the same place in all received PDUs */ 1164 ExpCmdSN = ntohl(pdu->pdu_hdr.pduh_p.nop_in.ExpCmdSN); 1165 MaxCmdSN = ntohl(pdu->pdu_hdr.pduh_p.nop_in.MaxCmdSN); 1166 1167 /* received a valid frame, reset timeout */ 1168 conn->c_num_timeouts = 0; 1169 if ((pdu->pdu_hdr.pduh_Opcode & OPCODE_MASK) == TOP_NOP_In && 1170 TAILQ_EMPTY(&conn->c_ccbs_waiting)) 1171 connection_timeout_start(conn, conn->c_idle_timeout_val); 1172 else 1173 connection_timeout_start(conn, CONNECTION_TIMEOUT); 1174 1175 /* Update session window */ 1176 mutex_enter(&sess->s_lock); 1177 if (sn_a_le_b(ExpCmdSN - 1, MaxCmdSN)) { 1178 if (sn_a_lt_b(sess->s_ExpCmdSN, ExpCmdSN)) 1179 sess->s_ExpCmdSN = ExpCmdSN; 1180 if (sn_a_lt_b(sess->s_MaxCmdSN, MaxCmdSN)) 1181 sess->s_MaxCmdSN = MaxCmdSN; 1182 } 1183 mutex_exit(&sess->s_lock); 1184 1185 return 0; 1186 } 1187 1188 /*****************************************************************************/ 1189 1190 /* 1191 * iscsi_receive_thread 1192 * Per connection thread handling receive data. 1193 * 1194 * Parameter: 1195 * conn The connection 1196 */ 1197 1198 void 1199 iscsi_rcv_thread(void *par) 1200 { 1201 connection_t *conn = (connection_t *) par; 1202 pdu_t *pdu; 1203 size_t hlen; 1204 1205 do { 1206 while (!conn->c_terminating) { 1207 pdu = get_pdu(conn, TRUE); 1208 if (pdu == NULL) { 1209 KASSERT(conn->c_terminating); 1210 break; 1211 } 1212 1213 pdu->pdu_uio.uio_iov = pdu->pdu_io_vec; 1214 UIO_SETUP_SYSSPACE(&pdu->pdu_uio); 1215 pdu->pdu_uio.uio_iovcnt = 1; 1216 pdu->pdu_uio.uio_rw = UIO_READ; 1217 1218 pdu->pdu_io_vec[0].iov_base = &pdu->pdu_hdr; 1219 hlen = (conn->c_HeaderDigest) ? BHS_SIZE + 4 : BHS_SIZE; 1220 pdu->pdu_io_vec[0].iov_len = hlen; 1221 pdu->pdu_uio.uio_resid = hlen; 1222 1223 DEBC(conn, 99, ("Receive thread waiting for data\n")); 1224 if (my_soo_read(conn, &pdu->pdu_uio, MSG_WAITALL)) { 1225 free_pdu(pdu); 1226 break; 1227 } 1228 /* Check again for header digest */ 1229 /* (it may have changed during the wait) */ 1230 if (hlen == BHS_SIZE && conn->c_HeaderDigest) { 1231 pdu->pdu_uio.uio_iov = pdu->pdu_io_vec; 1232 pdu->pdu_uio.uio_iovcnt = 1; 1233 pdu->pdu_io_vec[0].iov_base = &pdu->pdu_hdr.pduh_HeaderDigest; 1234 pdu->pdu_io_vec[0].iov_len = 4; 1235 pdu->pdu_uio.uio_resid = 4; 1236 if (my_soo_read(conn, &pdu->pdu_uio, MSG_WAITALL)) { 1237 free_pdu(pdu); 1238 break; 1239 } 1240 } 1241 1242 if (receive_pdu(conn, pdu) > 0) { 1243 break; 1244 } 1245 } 1246 mutex_enter(&conn->c_lock); 1247 if (!conn->c_destroy) { 1248 cv_timedwait(&conn->c_idle_cv, &conn->c_lock, CONNECTION_IDLE_TIMEOUT); 1249 } 1250 mutex_exit(&conn->c_lock); 1251 } while (!conn->c_destroy); 1252 1253 conn->c_rcvproc = NULL; 1254 DEBC(conn, 5, ("Receive thread exits\n")); 1255 kthread_exit(0); 1256 } 1257