1/* $NetBSD: iscsi_rcv.c,v 1.26 2022/09/13 13:09:16 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 51STATIC int 52my_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 105STATIC void 106try_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 141STATIC ccb_t * 142ccb_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 189STATIC int 190read_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 */ 294STATIC int 295collect_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 343STATIC int 344check_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 397STATIC void 398check_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 449STATIC int 450receive_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 500STATIC int 501receive_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 542STATIC int 543receive_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 if (!otherconn && conn->c_state == ST_LOGOUT_SENT) { 586 conn->c_terminating = ISCSI_STATUS_LOGOUT; 587 conn->c_state = ST_SETTLING; 588 conn->c_loggedout = (response) ? LOGOUT_FAILED : LOGOUT_SUCCESS; 589 590 connection_timeout_stop(conn); 591 592 /* let send thread take over next step of cleanup */ 593 mutex_enter(&conn->c_lock); 594 cv_broadcast(&conn->c_conn_cv); 595 mutex_exit(&conn->c_lock); 596 } 597 598 return !otherconn; 599} 600 601 602/* 603 * receive_data_in_pdu 604 * Handle receipt of a data in PDU. 605 * 606 * Parameter: 607 * conn The connection 608 * pdu The PDU 609 * req_CCB The CCB associated with the original request (if any) 610 */ 611 612STATIC int 613receive_data_in_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb) 614{ 615 uint32_t dsl, sn; 616 bool done; 617 int rc; 618 619 dsl = ntoh3(pdu->pdu_hdr.pduh_DataSegmentLength); 620 621 if (req_ccb == NULL || !req_ccb->ccb_data_in || !req_ccb->ccb_data_len) { 622 DEBOUT(("Received Data In, but req_ccb not waiting for it, ignored\n")); 623 return 0; 624 } 625 req_ccb->ccb_flags |= CCBF_GOT_RSP; 626 627 if (req_ccb->ccb_pdu_waiting != NULL) { 628 ccb_timeout_start(req_ccb, COMMAND_TIMEOUT); 629 req_ccb->ccb_num_timeouts = 0; 630 } 631 632 sn = ntohl(pdu->pdu_hdr.pduh_p.data_in.DataSN); 633 634 if ((rc = add_sernum(&req_ccb->ccb_DataSN_buf, sn)) != 1) { 635 if (!rc) { 636 return -1; 637 } 638 if (rc < 0) { 639 DEBOUT(("Excessive outstanding Data PDUs\n")); 640 handle_connection_error(req_ccb->ccb_connection, 641 ISCSI_STATUS_PDUS_LOST, LOGOUT_CONNECTION); 642 return -1; 643 } 644 DEBOUT(("Missing Data PDUs: First %d, num: %d\n", 645 req_ccb->ccb_DataSN_buf.ExpSN, rc - 1)); 646 647 if (conn->c_state == ST_FULL_FEATURE && 648 conn->c_session->s_ErrorRecoveryLevel) { 649 snack_missing(req_ccb->ccb_connection, req_ccb, 650 SNACK_DATA_NAK, req_ccb->ccb_DataSN_buf.ExpSN, 651 rc - 1); 652 } else { 653 DEBOUT(("Killing connection (State=%d, ErrorRecoveryLevel=%d)\n", 654 conn->c_state, conn->c_session->s_ErrorRecoveryLevel)); 655 handle_connection_error(conn, ISCSI_STATUS_PDUS_LOST, 656 LOGOUT_CONNECTION); 657 return -1; 658 } 659 } 660 661 ack_sernum(&req_ccb->ccb_DataSN_buf, sn); 662 663 req_ccb->ccb_xfer_len += dsl; 664 665 if ((pdu->pdu_hdr.pduh_Flags & FLAG_ACK) && conn->c_session->s_ErrorRecoveryLevel) 666 send_snack(conn, pdu, req_ccb, SNACK_DATA_ACK); 667 668 done = sn_empty(&req_ccb->ccb_DataSN_buf); 669 670 if (pdu->pdu_hdr.pduh_Flags & FLAG_STATUS) { 671 DEBC(conn, 10, ("Rx Data In %d, done = %d\n", 672 req_ccb->ccb_CmdSN, done)); 673 674 req_ccb->ccb_flags |= CCBF_COMPLETE; 675 /* successful transfer, reset recover count */ 676 conn->c_recover = 0; 677 678 if (done) 679 wake_ccb(req_ccb, ISCSI_STATUS_SUCCESS); 680 if (check_StatSN(conn, pdu->pdu_hdr.pduh_p.data_in.StatSN, done)) 681 return -1; 682 683 } else if (done && (req_ccb->ccb_flags & CCBF_COMPLETE)) { 684 wake_ccb(req_ccb, ISCSI_STATUS_SUCCESS); 685 } 686 /* else wait for command response */ 687 688 return 0; 689} 690 691 692/* 693 * receive_r2t_pdu 694 * Handle receipt of a R2T PDU. 695 * 696 * Parameter: 697 * conn The connection 698 * pdu The PDU 699 * req_CCB The CCB associated with the original request (if any) 700 */ 701 702STATIC int 703receive_r2t_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb) 704{ 705 706 DEBC(conn, 10, ("Received R2T PDU - CCB = %p\n", req_ccb)); 707 708 if (req_ccb != NULL) { 709 if (req_ccb->ccb_pdu_waiting != NULL) { 710 ccb_timeout_start(req_ccb, COMMAND_TIMEOUT); 711 req_ccb->ccb_num_timeouts = 0; 712 } 713 send_data_out(conn, pdu, req_ccb, CCBDISP_NOWAIT, TRUE); 714 } 715 716 return 0; 717} 718 719 720/* 721 * receive_command_response_pdu 722 * Handle receipt of a command response PDU. 723 * 724 * Parameter: 725 * conn The connection 726 * pdu The PDU 727 * req_CCB The CCB associated with the original request (if any) 728 */ 729 730STATIC int 731receive_command_response_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb) 732{ 733 int len, rc; 734 bool done; 735 uint32_t status; 736 737 /* Read any provided data */ 738 if (pdu->pdu_temp_data_len && req_ccb != NULL && req_ccb->ccb_sense_len_req) { 739 len = min(req_ccb->ccb_sense_len_req, 740 ntohs(*((uint16_t *) pdu->pdu_temp_data))); 741 memcpy(req_ccb->ccb_sense_ptr, ((uint16_t *) pdu->pdu_temp_data) + 1, 742 len); 743 req_ccb->ccb_sense_len_got = len; 744 } 745 746 if (req_ccb == NULL) { 747 /* Assume duplicate... */ 748 DEBOUT(("Possibly duplicate command response (no associated CCB)\n")); 749 return -1; 750 } 751 752 if (req_ccb->ccb_flags & CCBF_COMPLETE) { 753 DEBOUT(("Possibly duplicate command response (tagged as COMPLETE)\n")); 754 return -1; 755 } 756 757 if (req_ccb->ccb_pdu_waiting != NULL) { 758 ccb_timeout_start(req_ccb, COMMAND_TIMEOUT); 759 req_ccb->ccb_num_timeouts = 0; 760 } 761 762 req_ccb->ccb_flags |= CCBF_COMPLETE; 763 conn->c_recover = 0; /* successful transfer, reset recover count */ 764 765 if (pdu->pdu_hdr.pduh_OpcodeSpecific[0]) { /* Response */ 766 status = ISCSI_STATUS_TARGET_FAILURE; 767 } else { 768 switch (pdu->pdu_hdr.pduh_OpcodeSpecific[1]) { /* Status */ 769 case 0x00: 770 status = ISCSI_STATUS_SUCCESS; 771 break; 772 773 case 0x02: 774 status = ISCSI_STATUS_CHECK_CONDITION; 775 break; 776 777 case 0x08: 778 status = ISCSI_STATUS_TARGET_BUSY; 779 break; 780 781 default: 782 status = ISCSI_STATUS_TARGET_ERROR; 783 break; 784 } 785 } 786 787 if (pdu->pdu_hdr.pduh_Flags & (FLAG_OVERFLOW | FLAG_UNDERFLOW)) 788 req_ccb->ccb_residual = ntohl(pdu->pdu_hdr.pduh_p.response.ResidualCount); 789 790 done = status || sn_empty(&req_ccb->ccb_DataSN_buf); 791 792 DEBC(conn, 10, ("Rx Response: CmdSN %d, rsp = %x, status = %x\n", 793 req_ccb->ccb_CmdSN, 794 pdu->pdu_hdr.pduh_OpcodeSpecific[0], 795 pdu->pdu_hdr.pduh_OpcodeSpecific[1])); 796 797 rc = check_StatSN(conn, pdu->pdu_hdr.pduh_p.response.StatSN, done); 798 799 if (done) 800 wake_ccb(req_ccb, status); 801 802 return rc; 803} 804 805 806/* 807 * receive_asynch_pdu 808 * Handle receipt of an asynchronous message PDU. 809 * 810 * Parameter: 811 * conn The connection 812 * pdu The PDU 813 */ 814 815STATIC int 816receive_asynch_pdu(connection_t *conn, pdu_t *pdu) 817{ 818 819 DEBOUT(("Received Asynch PDU, Event %d\n", pdu->pdu_hdr.pduh_p.asynch.AsyncEvent)); 820 821 switch (pdu->pdu_hdr.pduh_p.asynch.AsyncEvent) { 822 case 0: /* SCSI Asynch event. Don't know what to do with it... */ 823 break; 824 825 case 1: /* Target requests logout. */ 826 if (conn->c_session->s_active_connections > 1) { 827 kill_connection(conn, ISCSI_STATUS_TARGET_LOGOUT, 828 LOGOUT_CONNECTION, FALSE); 829 } else { 830 kill_session(conn->c_session->s_id, 831 ISCSI_STATUS_TARGET_LOGOUT, LOGOUT_SESSION, 832 FALSE); 833 } 834 break; 835 836 case 2: /* Target is dropping connection */ 837 conn = find_connection(conn->c_session, 838 ntohs(pdu->pdu_hdr.pduh_p.asynch.Parameter1)); 839 if (conn != NULL) { 840 conn->c_Time2Wait = 841 ntohs(pdu->pdu_hdr.pduh_p.asynch.Parameter2); 842 conn->c_Time2Retain = 843 ntohs(pdu->pdu_hdr.pduh_p.asynch.Parameter3); 844 kill_connection(conn, ISCSI_STATUS_TARGET_DROP, 845 NO_LOGOUT, TRUE); 846 } 847 break; 848 849 case 3: /* Target is dropping all connections of session */ 850 conn->c_session->s_DefaultTime2Wait = 851 ntohs(pdu->pdu_hdr.pduh_p.asynch.Parameter2); 852 conn->c_session->s_DefaultTime2Retain = 853 ntohs(pdu->pdu_hdr.pduh_p.asynch.Parameter3); 854 kill_session(conn->c_session->s_id, 855 ISCSI_STATUS_TARGET_DROP, NO_LOGOUT, 856 TRUE); 857 break; 858 859 case 4: /* Target requests parameter negotiation */ 860 start_text_negotiation(conn); 861 break; 862 863 default: 864 /* ignore */ 865 break; 866 } 867 return 0; 868} 869 870 871/* 872 * receive_reject_pdu 873 * Handle receipt of a reject PDU. 874 * 875 * Parameter: 876 * conn The connection 877 * pdu The PDU 878 */ 879 880STATIC int 881receive_reject_pdu(connection_t *conn, pdu_t *pdu) 882{ 883 pdu_header_t *hpdu; 884 ccb_t *req_ccb; 885 uint32_t status; 886 887 DEBOUT(("Received Reject PDU, reason = %x, data_len = %d\n", 888 pdu->pdu_hdr.pduh_OpcodeSpecific[0], pdu->pdu_temp_data_len)); 889 890 if (pdu->pdu_temp_data_len >= BHS_SIZE) { 891 hpdu = (pdu_header_t *) pdu->pdu_temp_data; 892 req_ccb = ccb_from_itt(conn, hpdu->pduh_InitiatorTaskTag); 893 894 DEBC(conn, 9, ("Reject PDU ITT (ccb)= %x (%p)\n", 895 hpdu->pduh_InitiatorTaskTag, req_ccb)); 896 if (!req_ccb) { 897 return 0; 898 } 899 switch (pdu->pdu_hdr.pduh_OpcodeSpecific[0]) { 900 case REJECT_DIGEST_ERROR: 901 /* don't retransmit data out */ 902 if ((hpdu->pduh_Opcode & OPCODE_MASK) == IOP_SCSI_Data_out) 903 return 0; 904 resend_pdu(req_ccb); 905 return 0; 906 907 case REJECT_IMMED_COMMAND: 908 case REJECT_LONG_OPERATION: 909 resend_pdu(req_ccb); 910 return 0; 911 912 case REJECT_SNACK: 913 case REJECT_PROTOCOL_ERROR: 914 status = ISCSI_STATUS_PROTOCOL_ERROR; 915 break; 916 917 case REJECT_CMD_NOT_SUPPORTED: 918 status = ISCSI_STATUS_CMD_NOT_SUPPORTED; 919 break; 920 921 case REJECT_INVALID_PDU_FIELD: 922 status = ISCSI_STATUS_PDU_ERROR; 923 break; 924 925 default: 926 status = ISCSI_STATUS_GENERAL_ERROR; 927 break; 928 } 929 930 wake_ccb(req_ccb, status); 931 handle_connection_error(conn, ISCSI_STATUS_PROTOCOL_ERROR, 932 LOGOUT_CONNECTION); 933 } 934 return 0; 935} 936 937 938/* 939 * receive_task_management_pdu 940 * Handle receipt of a task management PDU. 941 * 942 * Parameter: 943 * conn The connection 944 * pdu The PDU 945 * req_CCB The CCB associated with the original request (if any) 946 */ 947 948STATIC int 949receive_task_management_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb) 950{ 951 uint32_t status; 952 953 DEBC(conn, 2, ("Received Task Management PDU, response %d, req_ccb %p\n", 954 pdu->pdu_hdr.pduh_OpcodeSpecific[0], req_ccb)); 955 956 if (req_ccb != NULL) { 957 switch (pdu->pdu_hdr.pduh_OpcodeSpecific[0]) { /* Response */ 958 case 0: 959 status = ISCSI_STATUS_SUCCESS; 960 break; 961 case 1: 962 status = ISCSI_STATUS_TASK_NOT_FOUND; 963 break; 964 case 2: 965 status = ISCSI_STATUS_LUN_NOT_FOUND; 966 break; 967 case 3: 968 status = ISCSI_STATUS_TASK_ALLEGIANT; 969 break; 970 case 4: 971 status = ISCSI_STATUS_CANT_REASSIGN; 972 break; 973 case 5: 974 status = ISCSI_STATUS_FUNCTION_UNSUPPORTED; 975 break; 976 case 6: 977 status = ISCSI_STATUS_FUNCTION_NOT_AUTHORIZED; 978 break; 979 case 255: 980 status = ISCSI_STATUS_FUNCTION_REJECTED; 981 break; 982 default: 983 status = ISCSI_STATUS_UNKNOWN_REASON; 984 break; 985 } 986 wake_ccb(req_ccb, status); 987 } 988 989 check_StatSN(conn, pdu->pdu_hdr.pduh_p.task_rsp.StatSN, TRUE); 990 991 return 0; 992} 993 994 995/* 996 * receive_nop_in_pdu 997 * Handle receipt of a Nop-In PDU. 998 * 999 * Parameter: 1000 * conn The connection 1001 * pdu The PDU 1002 * req_CCB The CCB associated with the original request (if any) 1003 */ 1004 1005STATIC int 1006receive_nop_in_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb) 1007{ 1008 DEBC(conn, 10, 1009 ("Received NOP_In PDU, req_ccb=%p, ITT=%x, TTT=%x, StatSN=%u\n", 1010 req_ccb, pdu->pdu_hdr.pduh_InitiatorTaskTag, 1011 pdu->pdu_hdr.pduh_p.nop_in.TargetTransferTag, 1012 ntohl(pdu->pdu_hdr.pduh_p.nop_in.StatSN))); 1013 1014 if (pdu->pdu_hdr.pduh_InitiatorTaskTag == 0xffffffff) { 1015 /* this is a target ping - respond with a pong */ 1016 if (pdu->pdu_hdr.pduh_p.nop_in.TargetTransferTag != 0xffffffff) 1017 send_nop_out(conn, pdu); 1018 1019 /* 1020 Any receive resets the connection timeout, but we got a ping, which 1021 means that it's likely the other side was waiting for something to 1022 happen on the connection. If we aren't idle, send a ping right 1023 away to synch counters (don't synch on this ping because other 1024 PDUs may be on the way). 1025 */ 1026 if (TAILQ_FIRST(&conn->c_ccbs_waiting) != NULL) 1027 send_nop_out(conn, NULL); 1028 } else if (req_ccb != NULL) { 1029 /* this is a solicited ping, check CmdSN for lost commands */ 1030 /* and advance StatSN */ 1031 check_CmdSN(conn, pdu->pdu_hdr.pduh_p.nop_in.ExpCmdSN); 1032 1033 wake_ccb(req_ccb, ISCSI_STATUS_SUCCESS); 1034 1035 check_StatSN(conn, pdu->pdu_hdr.pduh_p.nop_in.StatSN, TRUE); 1036 } else { 1037 DEBC(conn, 0, ("Received unsolicted NOP_In, itt=%08x\n", 1038 pdu->pdu_hdr.pduh_InitiatorTaskTag)); 1039 } 1040 1041 return 0; 1042} 1043 1044 1045/* 1046 * receive_pdu 1047 * Get parameters, call the appropriate handler for a received PDU. 1048 * 1049 * Parameter: 1050 * conn The connection 1051 * pdu The PDU 1052 * 1053 * Returns: 0 on success, nonzero if the connection is broken. 1054 */ 1055 1056STATIC int 1057receive_pdu(connection_t *conn, pdu_t *pdu) 1058{ 1059 ccb_t *req_ccb; 1060 int rc; 1061 uint32_t MaxCmdSN, ExpCmdSN, digest; 1062 session_t *sess = conn->c_session; 1063 1064 if (conn->c_HeaderDigest) { 1065 digest = gen_digest(&pdu->pdu_hdr, BHS_SIZE); 1066 if (digest != pdu->pdu_hdr.pduh_HeaderDigest) { 1067 DEBOUT(("Header Digest Error: comp = %08x, rx = %08x\n", 1068 digest, pdu->pdu_hdr.pduh_HeaderDigest)); 1069 /* try to skip to next PDU */ 1070 try_resynch_receive(conn); 1071 free_pdu(pdu); 1072 return 0; 1073 } 1074 } 1075 1076 DEBC(conn, 10, ("Received PDU StatSN=%u, ExpCmdSN=%u MaxCmdSN=%u ExpDataSN=%u\n", 1077 ntohl(pdu->pdu_hdr.pduh_p.response.StatSN), 1078 ntohl(pdu->pdu_hdr.pduh_p.response.ExpCmdSN), 1079 ntohl(pdu->pdu_hdr.pduh_p.response.MaxCmdSN), 1080 ntohl(pdu->pdu_hdr.pduh_p.response.ExpDataSN))); 1081 1082 req_ccb = ccb_from_itt(conn, pdu->pdu_hdr.pduh_InitiatorTaskTag); 1083 1084 if (req_ccb != NULL && req_ccb->ccb_data_in && req_ccb->ccb_data_len && 1085 (pdu->pdu_hdr.pduh_Opcode & OPCODE_MASK) == TOP_SCSI_Data_in) { 1086 uint32_t dsl, offset; 1087 1088 dsl = ntoh3(pdu->pdu_hdr.pduh_DataSegmentLength); 1089 offset = ntohl(pdu->pdu_hdr.pduh_p.data_in.BufferOffset); 1090 1091 if ((offset + dsl) > req_ccb->ccb_data_len) { 1092 DEBOUT(("Received more data than requested (len %d, offset %d)\n", 1093 dsl, offset)); 1094 handle_connection_error(conn, ISCSI_STATUS_TARGET_ERROR, NO_LOGOUT); 1095 return 1; 1096 } 1097 DEBC(conn, 10, 1098 ("Received Data in PDU - CCB = %p, Datalen = %d, Offset = %d\n", 1099 req_ccb, dsl, offset)); 1100 1101 rc = read_pdu_data(pdu, req_ccb->ccb_data_ptr, offset); 1102 } else { 1103 rc = read_pdu_data(pdu, NULL, 0); 1104 } 1105 if (!rc && (conn->c_state <= ST_WINDING_DOWN || 1106 (pdu->pdu_hdr.pduh_Opcode & OPCODE_MASK) == TOP_Logout_Response)) { 1107 1108 switch (pdu->pdu_hdr.pduh_Opcode & OPCODE_MASK) { 1109 case TOP_NOP_In: 1110 rc = receive_nop_in_pdu(conn, pdu, req_ccb); 1111 break; 1112 1113 case TOP_SCSI_Response: 1114 rc = receive_command_response_pdu(conn, pdu, req_ccb); 1115 break; 1116 1117 case TOP_SCSI_Task_Management: 1118 rc = receive_task_management_pdu(conn, pdu, req_ccb); 1119 break; 1120 1121 case TOP_Login_Response: 1122 rc = receive_login_pdu(conn, pdu, req_ccb); 1123 break; 1124 1125 case TOP_Text_Response: 1126 rc = receive_text_response_pdu(conn, pdu, req_ccb); 1127 break; 1128 1129 case TOP_SCSI_Data_in: 1130 rc = receive_data_in_pdu(conn, pdu, req_ccb); 1131 break; 1132 1133 case TOP_Logout_Response: 1134 rc = receive_logout_pdu(conn, pdu, req_ccb); 1135 break; 1136 1137 case TOP_R2T: 1138 rc = receive_r2t_pdu(conn, pdu, req_ccb); 1139 break; 1140 1141 case TOP_Asynchronous_Message: 1142 rc = receive_asynch_pdu(conn, pdu); 1143 break; 1144 1145 case TOP_Reject: 1146 rc = receive_reject_pdu(conn, pdu); 1147 break; 1148 1149 default: 1150 DEBOUT(("Received Invalid Opcode %x\n", pdu->pdu_hdr.pduh_Opcode)); 1151 try_resynch_receive(conn); 1152 rc = -1; 1153 break; 1154 } 1155 } 1156 1157 free_pdu(pdu); 1158 if (rc) 1159 return rc; 1160 1161 /* MaxCmdSN and ExpCmdSN are in the same place in all received PDUs */ 1162 ExpCmdSN = ntohl(pdu->pdu_hdr.pduh_p.nop_in.ExpCmdSN); 1163 MaxCmdSN = ntohl(pdu->pdu_hdr.pduh_p.nop_in.MaxCmdSN); 1164 1165 /* received a valid frame, reset timeout */ 1166 conn->c_num_timeouts = 0; 1167 if ((pdu->pdu_hdr.pduh_Opcode & OPCODE_MASK) == TOP_NOP_In && 1168 TAILQ_EMPTY(&conn->c_ccbs_waiting)) 1169 connection_timeout_start(conn, conn->c_idle_timeout_val); 1170 else 1171 connection_timeout_start(conn, CONNECTION_TIMEOUT); 1172 1173 /* Update session window */ 1174 mutex_enter(&sess->s_lock); 1175 if (sn_a_le_b(ExpCmdSN - 1, MaxCmdSN)) { 1176 if (sn_a_lt_b(sess->s_ExpCmdSN, ExpCmdSN)) 1177 sess->s_ExpCmdSN = ExpCmdSN; 1178 if (sn_a_lt_b(sess->s_MaxCmdSN, MaxCmdSN)) 1179 sess->s_MaxCmdSN = MaxCmdSN; 1180 } 1181 mutex_exit(&sess->s_lock); 1182 1183 return 0; 1184} 1185 1186/*****************************************************************************/ 1187 1188/* 1189 * iscsi_receive_thread 1190 * Per connection thread handling receive data. 1191 * 1192 * Parameter: 1193 * conn The connection 1194 */ 1195 1196void 1197iscsi_rcv_thread(void *par) 1198{ 1199 connection_t *conn = (connection_t *) par; 1200 pdu_t *pdu; 1201 size_t hlen; 1202 1203 do { 1204 while (!conn->c_terminating) { 1205 pdu = get_pdu(conn, TRUE); 1206 if (pdu == NULL) { 1207 KASSERT(conn->c_terminating); 1208 break; 1209 } 1210 1211 pdu->pdu_uio.uio_iov = pdu->pdu_io_vec; 1212 UIO_SETUP_SYSSPACE(&pdu->pdu_uio); 1213 pdu->pdu_uio.uio_iovcnt = 1; 1214 pdu->pdu_uio.uio_rw = UIO_READ; 1215 1216 pdu->pdu_io_vec[0].iov_base = &pdu->pdu_hdr; 1217 hlen = (conn->c_HeaderDigest) ? BHS_SIZE + 4 : BHS_SIZE; 1218 pdu->pdu_io_vec[0].iov_len = hlen; 1219 pdu->pdu_uio.uio_resid = hlen; 1220 1221 DEBC(conn, 99, ("Receive thread waiting for data\n")); 1222 if (my_soo_read(conn, &pdu->pdu_uio, MSG_WAITALL)) { 1223 free_pdu(pdu); 1224 break; 1225 } 1226 /* Check again for header digest */ 1227 /* (it may have changed during the wait) */ 1228 if (hlen == BHS_SIZE && conn->c_HeaderDigest) { 1229 pdu->pdu_uio.uio_iov = pdu->pdu_io_vec; 1230 pdu->pdu_uio.uio_iovcnt = 1; 1231 pdu->pdu_io_vec[0].iov_base = &pdu->pdu_hdr.pduh_HeaderDigest; 1232 pdu->pdu_io_vec[0].iov_len = 4; 1233 pdu->pdu_uio.uio_resid = 4; 1234 if (my_soo_read(conn, &pdu->pdu_uio, MSG_WAITALL)) { 1235 free_pdu(pdu); 1236 break; 1237 } 1238 } 1239 1240 if (receive_pdu(conn, pdu) > 0) { 1241 break; 1242 } 1243 } 1244 mutex_enter(&conn->c_lock); 1245 if (!conn->c_destroy) { 1246 cv_timedwait(&conn->c_idle_cv, &conn->c_lock, CONNECTION_IDLE_TIMEOUT); 1247 } 1248 mutex_exit(&conn->c_lock); 1249 } while (!conn->c_destroy); 1250 1251 conn->c_rcvproc = NULL; 1252 DEBC(conn, 5, ("Receive thread exits\n")); 1253 kthread_exit(0); 1254} 1255