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