1/* 2 * Copyright 2010, Andreas Färber <andreas.faerber@web.de> 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 7#include <boot/net/iSCSITarget.h> 8 9#include <stdio.h> 10#include <string.h> 11#include <ctype.h> 12 13#include <KernelExport.h> 14 15#include <boot/net/ChainBuffer.h> 16#include <boot/net/TCP.h> 17#include <iscsi_cmds.h> 18#include <scsi_cmds.h> 19 20#include "real_time_clock.h" 21 22 23//#define TRACE_ISCSI 24 25#ifndef TRACE_ISCSI 26#define TRACE(fmt, ...) ; 27#else 28#define TRACE(fmt, ...) printf("iSCSI: %s(): " fmt, __func__, ## __VA_ARGS__) 29#endif 30#define PANIC(fmt, ...) panic("iSCSI: %s(): " fmt, __func__, ## __VA_ARGS__) 31 32 33#define ISCSI_ALIGNMENT 4 34#define ISCSI_ALIGN(x) (((x) + ISCSI_ALIGNMENT - 1) & ~(ISCSI_ALIGNMENT - 1)) 35#define ISCSI_PADDING(x) ((((x) % ISCSI_ALIGNMENT) == 0) ? 0 \ 36 : (ISCSI_ALIGNMENT - ((x) % ISCSI_ALIGNMENT))) 37 38// derived from the schedulers 39static int 40_rand(void) 41{ 42 static int next = 0; 43 if (next == 0) 44 next = real_time_clock_usecs() / 1000; 45 46 next = next * 1103515245 + 12345; 47 return next; 48} 49 50 51bool 52iSCSITarget::DiscoverTargets(ip_addr_t address, uint16 port, NodeList* devicesList) 53{ 54 iSCSISession* session = new(nothrow) iSCSISession(); 55 if (session == NULL) 56 return false; 57 status_t error = session->Init(address, port); 58 if (error != B_OK) { 59 delete session; 60 return false; 61 } 62 const char* request = "SendTargets=All"; 63 char* sendTargets = NULL; 64 size_t sendTargetsLength = 0; 65 error = session->Connection()->GetText(request, strlen(request) + 1, 66 &sendTargets, &sendTargetsLength); 67 session->Close(); 68 delete session; 69 if (error != B_OK) { 70 return false; 71 } 72 73 bool addedDisk = false; 74 char* targetName = NULL; 75 bool seenAddress = false; 76 for (char* pair = sendTargets; pair < sendTargets + sendTargetsLength; 77 pair += strlen(pair) + 1) { 78 printf("%s\n", pair); 79 if (strncmp(pair, "TargetName=", strlen("TargetName=")) == 0) { 80 if (targetName != NULL && !seenAddress) { 81 if (_AddDevice(address, port, targetName, devicesList)) 82 addedDisk = true; 83 } 84 seenAddress = false; 85 targetName = pair + strlen("TargetName="); 86 } else if (strncmp(pair, "TargetAddress=", strlen("TargetAddress=")) 87 == 0) { 88 seenAddress = true; 89 char* targetAddress = pair + strlen("TargetAddress="); 90 char* comma = strrchr(targetAddress, ','); 91 int addressLength = comma - targetAddress; 92 targetAddress = strndup(targetAddress, addressLength); 93 uint16 targetPort = ISCSI_PORT; 94 char* colon = strrchr(targetAddress, ':'); 95 if (colon != NULL) { 96 // colon could be part of an IPv6 address, e.g. [::1.2.3.4] 97 char* bracket = strrchr(targetAddress, ']'); 98 if (bracket == NULL || bracket < colon) { 99 targetPort = strtol(colon + 1, NULL, 10); 100 colon[0] = '\0'; 101 } 102 } 103 if (targetName != NULL && isdigit(targetAddress[0])) { 104 if (_AddDevice(ip_parse_address(targetAddress), targetPort, 105 targetName, devicesList)) 106 addedDisk = true; 107 } 108 free(targetAddress); 109 } 110 } 111 if (targetName != NULL && !seenAddress) { 112 if (_AddDevice(address, port, targetName, devicesList)) 113 addedDisk = true; 114 } 115 free(sendTargets); 116 return addedDisk; 117} 118 119 120bool 121iSCSITarget::_AddDevice(ip_addr_t address, uint16 port, 122 const char* targetName, NodeList* devicesList) 123{ 124 TRACE("=> %s @ %08lx:%u\n", targetName, address, port); 125 iSCSITarget* disk = new(nothrow) iSCSITarget(); 126 if (disk == NULL) 127 return false; 128 status_t error = disk->Init(address, port, targetName); 129 if (error != B_OK) { 130 delete disk; 131 return false; 132 } 133 TRACE("disk size = %llu\n", disk->Size()); 134 devicesList->Add(disk); 135 return true; 136} 137 138 139iSCSITarget::iSCSITarget() 140 : 141 fSession(NULL), 142 fTargetName(NULL), 143 fTargetAlias(NULL) 144{ 145} 146 147 148iSCSITarget::~iSCSITarget() 149{ 150 free(fTargetName); 151 free(fTargetAlias); 152 delete fSession; 153} 154 155 156status_t 157iSCSITarget::Init(ip_addr_t address, uint16 port, const char* targetName) 158{ 159 fTargetName = strdup(targetName); 160 if (fTargetName == NULL) 161 return B_NO_MEMORY; 162 163 fSession = new(nothrow) iSCSISession(fTargetName); 164 if (fSession == NULL) 165 return B_NO_MEMORY; 166 status_t error = fSession->Init(address, port, &fTargetAlias); 167 if (error != B_OK) 168 return error; 169 if (targetName == NULL) 170 return B_OK; 171 172 error = _GetSize(); 173 if (error != B_OK) 174 return error; 175 176 return B_OK; 177} 178 179 180status_t 181iSCSITarget::_GetSize() 182{ 183 scsi_cmd_read_capacity cmd; 184 memset(&cmd, 0, sizeof(cmd)); 185 cmd.opcode = SCSI_OP_READ_CAPACITY; 186 cmd.relative_address = 0; 187 cmd.lun = 0; 188 cmd.lba = 0; 189 cmd.pmi = 0; 190 cmd.control = 0; 191 scsi_res_read_capacity resp; 192 status_t error = fSession->Connection()->SendCommand(&cmd, sizeof(cmd), 193 true, false, sizeof(resp), 194 &resp, 0, sizeof(resp)); 195 if (error != B_OK) { 196 TRACE("error %lx sending command!\n", error); 197 return error; 198 } 199 TRACE("lba = %lu, block size = %lu\n", resp.lba, resp.block_size); 200 fLastLBA = resp.lba; 201 fBlockSize = resp.block_size; 202 203 return B_OK; 204} 205 206 207ssize_t 208iSCSITarget::ReadAt(void* /*cookie*/, off_t pos, void* buffer, size_t bufferSize) 209{ 210 TRACE("pos=%llu, size = %lu\n", pos, bufferSize); 211 212 if (fSession == NULL) 213 return B_NO_INIT; 214 215 if (buffer == NULL || pos < 0 || pos >= Size()) 216 return B_BAD_VALUE; 217 if (bufferSize == 0) 218 return 0; 219 220 uint32 blockOffset = pos % fBlockSize; 221 222 scsi_cmd_rw_16 cmd; 223 memset(&cmd, 0, sizeof(cmd)); 224 cmd.opcode = SCSI_OP_READ_16; 225 cmd.force_unit_access = 0; 226 cmd.disable_page_out = 0; 227 cmd.lba = pos / fBlockSize; 228 cmd.length = (blockOffset + bufferSize + fBlockSize - 1) / fBlockSize; 229 cmd.control = 0; 230 status_t error = fSession->Connection()->SendCommand(&cmd, sizeof(cmd), 231 true, false, cmd.length * fBlockSize, buffer, blockOffset, bufferSize); 232 if (error != B_OK) { 233 TRACE("error %lx sending command!\n", error); 234 return error; 235 } 236 237 return bufferSize; 238} 239 240 241ssize_t 242iSCSITarget::WriteAt(void* /*cookie*/, off_t pos, const void* buffer, size_t bufferSize) 243{ 244 return B_PERMISSION_DENIED; 245} 246 247 248status_t 249iSCSITarget::GetName(char* buffer, size_t bufferSize) const 250{ 251 if (buffer == NULL) 252 return B_BAD_VALUE; 253 254 snprintf(buffer, bufferSize, "iSCSI:%s", fTargetName); 255 256 return B_OK; 257} 258 259 260off_t 261iSCSITarget::Size() const 262{ 263 return (fLastLBA + 1) * fBlockSize; 264} 265 266 267iSCSISession::iSCSISession(const char* targetName) 268 : 269 fDiscovery(targetName == NULL), 270 fTargetName(targetName), 271 fConnection(NULL) 272{ 273 fCommandSequenceNumber = _rand(); 274} 275 276 277iSCSISession::~iSCSISession() 278{ 279 if (Active()) 280 Close(); 281 delete fConnection; 282} 283 284 285status_t 286iSCSISession::Init(ip_addr_t address, uint16 port, char** targetAlias) 287{ 288 fConnection = new(nothrow) iSCSIConnection(); 289 if (fConnection == NULL) 290 return B_NO_MEMORY; 291 status_t error = fConnection->Init(this); 292 if (error != B_OK) 293 return error; 294 error = fConnection->Open(address, port); 295 if (error != B_OK) 296 return error; 297 error = fConnection->Login(fTargetName, targetAlias); 298 if (error != B_OK) 299 return error; 300 return B_OK; 301} 302 303 304status_t 305iSCSISession::Close() 306{ 307 if (fConnection == NULL) 308 return B_NO_INIT; 309 310 status_t error = fConnection->Logout(true); 311 if (error != B_OK) 312 return error; 313 return B_OK; 314} 315 316 317iSCSIConnection::iSCSIConnection() 318 : 319 fSession(NULL), 320 fSocket(NULL), 321 fConnected(false), 322 fConnectionID(0) 323{ 324} 325 326 327iSCSIConnection::~iSCSIConnection() 328{ 329 if (fSocket != NULL && fConnected) { 330 if (Logout() != B_OK) 331 fSocket->Close(); 332 } 333 delete fSocket; 334} 335 336 337status_t 338iSCSIConnection::Init(iSCSISession* session) 339{ 340 fSession = session; 341 return B_OK; 342} 343 344status_t 345iSCSIConnection::Open(ip_addr_t address, uint16 port) 346{ 347 fSocket = new(nothrow) TCPSocket(); 348 if (fSocket == NULL) 349 return B_NO_MEMORY; 350 TRACE("connecting to target...\n"); 351 status_t error = fSocket->Connect(address, port); 352 if (error != B_OK) 353 return error; 354 fConnected = true; 355 TRACE("connected.\n"); 356 fConnectionID = _rand(); 357 return B_OK; 358} 359 360 361status_t 362iSCSIConnection::Login(const char* targetName, char** targetAlias) 363{ 364 char data[256]; 365 size_t dataLength = 0; 366 const char* keyValue; 367 keyValue = "InitiatorName=iqn.2002-10.org.haiku-os:haiku.bootloader.hostX"; 368 strcpy(data + dataLength, keyValue); 369 dataLength += strlen(keyValue) + 1; 370 if (targetName != NULL) { 371 dataLength += sprintf(data + dataLength, "TargetName=%s", targetName) + 1; 372 } else { 373 keyValue = "SessionType=Discovery"; 374 strcpy(data + dataLength, keyValue); 375 dataLength += strlen(keyValue) + 1; 376 } 377 378 iscsi_login_request req; 379 req.transit = true; 380 req.c = false; 381 req.currentStage = ISCSI_SESSION_STAGE_LOGIN_OPERATIONAL_NEGOTIATION; 382 req.nextStage = ISCSI_SESSION_STAGE_FULL_FEATURE_PHASE; 383 req.versionMax = ISCSI_VERSION; 384 req.versionMin = ISCSI_VERSION; 385 req.totalAHSLength = 0; 386 req.dataSegmentLength = dataLength; 387 TRACE("data segment length = %lu\n", req.dataSegmentLength); 388 req.isid.t = ISCSI_ISID_RANDOM; 389 req.isid.a = 0; 390 uint32 random = _rand(); 391 req.isid.b = random >> 8; 392 req.isid.c = random & 0xff; 393 req.isid.d = _rand(); 394 req.tsih = 0; 395 req.initiatorTaskTag = _rand(); 396 req.cid = fConnectionID; 397 req.cmdSN = fSession->CommandSequenceNumber(); 398 req.expStatSN = 0; 399 status_t error = fSocket->Write(&req, sizeof(req)); 400 if (error != B_OK) { 401 return error; 402 } 403 for (unsigned int i = dataLength; i < ISCSI_ALIGN(dataLength); i++) 404 data[i] = '\0'; 405 dataLength = ISCSI_ALIGN(dataLength); 406 TRACE("data length = %lu\n", dataLength); 407 error = fSocket->Write(data, dataLength); 408 if (error != B_OK) { 409 TRACE("write error\n"); 410 return error; 411 } 412 TRACE("request sent.\n"); 413 414 iscsi_login_response resp; 415 size_t bytesRead = 0; 416 error = fSocket->Read(&resp, sizeof(resp), &bytesRead, 10000000LL); 417 if (error != B_OK) { 418 TRACE("response read error\n"); 419 return error; 420 } 421 TRACE("bytesRead = %lu\n", bytesRead); 422 TRACE("response: opcode = %x, status class = %u, status detail = %u, transit = %u, data length = %lu\n", 423 resp.opcode, resp.statusClass, resp.statusDetail, resp.transit, resp.dataSegmentLength); 424 if (resp.dataSegmentLength > 0) { 425 error = fSocket->Read(data, resp.dataSegmentLength, &bytesRead, 1000000LL); 426 if (error != B_OK) { 427 TRACE("response data read error\n"); 428 return error; 429 } 430 TRACE("bytesRead = %lu\n", bytesRead); 431 if (resp.dataSegmentLength % ISCSI_ALIGNMENT) { 432 error = fSocket->Read(NULL, ISCSI_PADDING(resp.dataSegmentLength), &bytesRead, 100000LL); 433 if (error != B_OK) { 434 TRACE("response padding read error\n"); 435 return error; 436 } 437 TRACE("bytesRead = %lu\n", bytesRead); 438 } 439 for (char* pair = data; pair < data + resp.dataSegmentLength; pair += strlen(pair) + 1) { 440 TRACE("%s\n", pair); 441 if (targetAlias != NULL && strncmp(pair, "TargetAlias=", strlen("TargetAlias=")) == 0) { 442 *targetAlias = strdup(pair + strlen("TargetAlias=")); 443 } 444 } 445 } 446 fStatusSequenceNumber = resp.statSN; 447 if (resp.statusClass != 0) { 448 TRACE("response indicates failure.\n"); 449 return B_BAD_VALUE; 450 } 451 return B_OK; 452} 453 454 455status_t 456iSCSIConnection::Logout(bool closeSession) 457{ 458 iscsi_logout_request req; 459 TRACE("req size = %lu\n", sizeof(req)); 460 req.immediateDelivery = true; 461 req.reasonCode = closeSession ? ISCSI_LOGOUT_REASON_CLOSE_SESSION 462 : ISCSI_LOGOUT_REASON_CLOSE_CONNECTION; 463 req.totalAHSLength = 0; 464 req.dataSegmentLength = 0; 465 req.initiatorTaskTag = _rand(); 466 req.cid = closeSession ? 0 : fConnectionID; 467 req.cmdSN = fSession->CommandSequenceNumber(); 468 req.expStatSN = fStatusSequenceNumber; 469 status_t error = fSocket->Write(&req, sizeof(req)); 470 if (error != B_OK) { 471 TRACE("request write error\n"); 472 return error; 473 } 474 475 iscsi_logout_response resp; 476 TRACE("resp size = %lu\n", sizeof(resp)); 477 size_t bytesRead = 0; 478 error = fSocket->Read(&resp, sizeof(resp), &bytesRead, 10000000LL); 479 if (error != B_OK) { 480 TRACE("response read error\n"); 481 return error; 482 } 483 TRACE("bytesRead = %lu\n", bytesRead); 484 TRACE("response: opcode = %x, response = %u\n", 485 resp.opcode, resp.response); 486 fStatusSequenceNumber = resp.statSN; 487 if (resp.response != 0) { 488 TRACE("response indicates failure.\n"); 489 return B_ERROR; 490 } 491 492 fSocket->Close(); 493 fConnected = false; 494 return B_OK; 495} 496 497 498status_t 499iSCSIConnection::GetText(const char* request, size_t requestLength, char** response, size_t* responseLength) 500{ 501 iscsi_text_request req; 502 req.immediateDelivery = true; 503 req.final = true; 504 req.c = false; 505 req.totalAHSLength = 0; 506 req.dataSegmentLength = requestLength; 507 req.lun = 0; 508 req.initiatorTaskTag = _rand(); 509 req.targetTransferTag = 0xffffffffL; 510 req.cmdSN = fSession->NextCommandSequenceNumber(); 511 req.expStatSN = fStatusSequenceNumber; 512 status_t error = fSocket->Write(&req, sizeof(req)); 513 if (error != B_OK) { 514 TRACE("iSCSI text request write error\n"); 515 return error; 516 } 517 error = fSocket->Write(request, requestLength); 518 if (error != B_OK) { 519 TRACE("iSCSI text request data write error\n"); 520 return error; 521 } 522 if (requestLength % ISCSI_ALIGNMENT != 0) { 523 char buffer[3]; 524 memset(buffer, 0, 3); 525 error = fSocket->Write(buffer, ISCSI_ALIGNMENT - (requestLength % ISCSI_ALIGNMENT)); 526 if (error != B_OK) { 527 TRACE("iSCSI text request padding write error\n"); 528 return error; 529 } 530 } 531 532 iscsi_logout_response resp; 533 size_t bytesRead = 0; 534 error = fSocket->Read(&resp, sizeof(resp), &bytesRead, 10000000LL); 535 if (error != B_OK) { 536 TRACE("response read error\n"); 537 return error; 538 } 539 TRACE("bytesRead = %lu\n", bytesRead); 540 TRACE("response: opcode = %x\n", 541 resp.opcode); 542 *response = (char*)malloc(resp.dataSegmentLength); 543 if (*response == NULL) 544 return B_NO_MEMORY; 545 error = fSocket->Read(*response, resp.dataSegmentLength, &bytesRead, 10000000LL); 546 if (error != B_OK) { 547 TRACE("response read error\n"); 548 return error; 549 } 550 TRACE("bytesRead = %lu\n", bytesRead); 551 *responseLength = resp.dataSegmentLength; 552 if ((resp.dataSegmentLength % ISCSI_ALIGNMENT) != 0) { 553 error = fSocket->Read(NULL, ISCSI_PADDING(resp.dataSegmentLength), &bytesRead, 1000000LL); 554 if (error != B_OK) { 555 TRACE("response padding read error\n"); 556 return error; 557 } 558 } 559 fStatusSequenceNumber = resp.statSN; 560 if (resp.opcode != ISCSI_OPCODE_TEXT_RESPONSE) { 561 PANIC("response opcode unexpected!"); 562 return B_ERROR; 563 } 564 565 return B_OK; 566} 567 568 569status_t 570iSCSIConnection::SendCommand(const void* command, size_t commandSize, 571 bool r, bool w, uint32 expectedDataTransferLength, 572 void* response, uint32 responseOffset, size_t responseLength) 573{ 574 TRACE("command size = %lu, offset = %lu, length = %lu\n", commandSize, responseOffset, responseLength); 575 iscsi_scsi_command req; 576 req.immediateDelivery = true; 577 req.final = true; 578 req.r = r; 579 req.w = w; 580 req.attr = 0; 581 req.totalAHSLength = 0; 582 req.dataSegmentLength = (commandSize > 16) ? commandSize - 16 : 0; 583 req.lun = 0; 584 req.initiatorTaskTag = _rand(); 585 req.expectedDataTransferLength = expectedDataTransferLength; 586 req.cmdSN = fSession->NextCommandSequenceNumber(); 587 req.expStatSN = fStatusSequenceNumber; 588 if (commandSize < 16) 589 memset(req.cdb, 0, 16); 590 memcpy(req.cdb, command, commandSize <= 16 ? commandSize : 16); 591 status_t error = fSocket->Write(&req, sizeof(req)); 592 if (error != B_OK) { 593 TRACE("write error\n"); 594 return error; 595 } 596 if (commandSize > 16) { 597 error = fSocket->Write((uint8*)command + 16, commandSize - 16); 598 if (error != B_OK) { 599 TRACE("write error\n"); 600 return error; 601 } 602 if ((req.dataSegmentLength % ISCSI_ALIGNMENT) != 0) { 603 char buffer[3]; 604 memset(buffer, 0, 3); 605 error = fSocket->Write(buffer, ISCSI_ALIGNMENT - (req.dataSegmentLength % ISCSI_ALIGNMENT)); 606 if (error != B_OK) { 607 TRACE("request padding write error\n"); 608 return error; 609 } 610 } 611 } 612 613 struct iscsi_basic_header_segment resp; 614 error = _ReadResponse(&resp); 615 if (error != B_OK) { 616 TRACE("response read error\n"); 617 return error; 618 } 619 while (r && resp.opcode != ISCSI_OPCODE_SCSI_RESPONSE) { 620 if (resp.opcode != ISCSI_OPCODE_SCSI_DATA_IN) 621 PANIC("response opcode %x unexpected\n", resp.opcode); 622 iscsi_scsi_data_in* dataIn = (iscsi_scsi_data_in*)&resp; 623 TRACE("A=%u, S=%u, bufferOffset=%lu, length = %lu\n", dataIn->acknowledge, dataIn->S, dataIn->bufferOffset, resp.dataSegmentLength); 624 if (resp.dataSegmentLength > 0) { 625 size_t toRead = resp.dataSegmentLength; 626 size_t toSkip = 0; 627 if (dataIn->bufferOffset < responseOffset) { 628 toSkip = MIN(responseOffset - dataIn->bufferOffset, resp.dataSegmentLength); 629 error = _Read(NULL, toSkip); 630 if (error != B_OK) { 631 TRACE("response skip error\n"); 632 return error; 633 } 634 TRACE("skipped %lu response bytes\n", toSkip); 635 toRead -= toSkip; 636 } 637 if (toRead > 0) { 638 if (dataIn->bufferOffset + resp.dataSegmentLength > responseOffset + responseLength) { 639 size_t cutOff = expectedDataTransferLength - (responseOffset + responseLength); 640 toRead = MIN(expectedDataTransferLength - cutOff - dataIn->bufferOffset, resp.dataSegmentLength) - toSkip; 641 } 642 error = _Read((uint8*)response + dataIn->bufferOffset - responseOffset, toRead); 643 if (error != B_OK) { 644 TRACE("response read error: %lx\n", error); 645 return error; 646 } 647 } 648 size_t toCutOff = resp.dataSegmentLength - toSkip - toRead; 649 if (toCutOff > 0) { 650 error = _Read(NULL, toCutOff); 651 if (error != B_OK) { 652 TRACE("response cut-off error\n"); 653 return error; 654 } 655 } 656 if (toSkip + toRead + toCutOff != resp.dataSegmentLength) 657 PANIC("inconcistency while reading detected!\n"); 658 } 659 if ((resp.dataSegmentLength % ISCSI_ALIGNMENT) != 0) { 660 error = _Read(NULL, ISCSI_PADDING(resp.dataSegmentLength)); 661 if (error != B_OK) { 662 TRACE("response padding read error\n"); 663 return error; 664 } 665 } 666 fStatusSequenceNumber = dataIn->statSN; 667 if (dataIn->S) { 668 // TODO check status 669 return B_OK; 670 } 671 672 error = _ReadResponse(&resp); 673 if (error != B_OK) { 674 TRACE("response read error\n"); 675 return error; 676 } 677 } 678 679 TRACE("response: opcode = %x, AHS length = %x, data length = %lu\n", 680 resp.opcode, resp.totalAHSLength, resp.dataSegmentLength); 681 if (resp.opcode != ISCSI_OPCODE_SCSI_RESPONSE) { 682 PANIC("response opcode unexpected!\n"); 683 return B_ERROR; 684 } 685 iscsi_scsi_response* scsiResp = (iscsi_scsi_response*)&resp; 686 TRACE("response: response = %x, status = %x, SNACK=%x\n", 687 scsiResp->response, scsiResp->status, scsiResp->snackTag); 688 if (resp.dataSegmentLength > 0) { 689 void* buffer = NULL; 690 if (scsiResp->status != SCSI_STATUS_GOOD) 691 buffer = malloc(resp.dataSegmentLength); 692 error = _Read(buffer, resp.dataSegmentLength); 693 if (error != B_OK) { 694 TRACE("response read error\n"); 695 return error; 696 } 697#ifdef TRACE_ISCSI 698 if (scsiResp->status != SCSI_STATUS_GOOD) { 699 scsi_sense* sense = (scsi_sense*)((uint16*)buffer + 1); 700 TRACE("error code = %u, sense key = %u, ILI = %u, asc = %x\n", 701 sense->error_code, sense->sense_key, sense->ILI, (sense->asc << 8) | sense->ascq); 702 } 703#endif 704 free(buffer); 705 } 706 if ((resp.dataSegmentLength % ISCSI_ALIGNMENT) != 0) { 707 error = _Read(NULL, ISCSI_PADDING(resp.dataSegmentLength)); 708 if (error != B_OK) { 709 TRACE("response padding read error\n"); 710 return error; 711 } 712 } 713 fStatusSequenceNumber = scsiResp->statSN; 714 if (scsiResp->response != 0x00 || scsiResp->status != SCSI_STATUS_GOOD) 715 return B_BAD_VALUE; 716 717 return B_OK; 718} 719 720 721status_t 722iSCSIConnection::_ReadResponse(iscsi_basic_header_segment* resp, bigtime_t timeout) 723{ 724 if (resp == NULL) 725 return B_BAD_VALUE; 726 727 status_t error = _Read(resp, sizeof(iscsi_basic_header_segment), timeout); 728 if (error != B_OK) { 729 TRACE("initial read failed\n"); 730 return error; 731 } 732 733 if (resp->reserved == true) { 734 TRACE("reserved bit is 1!\n"); 735 return B_IO_ERROR; 736 } 737 // theoretically process header checksum here, if available 738 739 // At any time a target can send a NOP-In, requiring us to answer with a NOP-Out. 740 // TODO handle asynchronous event message 741 while (resp->opcode == ISCSI_OPCODE_NOP_IN) { 742 iscsi_nop_in* nopIn = (iscsi_nop_in*)resp; 743 // A NOP-In can also be the answer to our own NOP-Out though. 744 if (nopIn->targetTransferTag == 0xffffffff) 745 return B_OK; 746 TRACE("received NOP-In (ping data length = %lu)\n", nopIn->dataSegmentLength); 747 void* pingData = NULL; 748 if (nopIn->dataSegmentLength > 0) { 749 pingData = malloc(nopIn->dataSegmentLength); 750 if (pingData == NULL) 751 return B_NO_MEMORY; 752 error = _Read(pingData, nopIn->dataSegmentLength, timeout); 753 if (error != B_OK) { 754 TRACE("NOP-In ping data read error\n"); 755 free(pingData); 756 return error; 757 } 758 } 759 if ((nopIn->dataSegmentLength % ISCSI_ALIGNMENT) != 0) { 760 error = _Read(NULL, ISCSI_PADDING(nopIn->dataSegmentLength), timeout); 761 if (error != B_OK) { 762 TRACE("NOP-In padding read error\n"); 763 free(pingData); 764 return error; 765 } 766 } 767 iscsi_nop_out nopOut; 768 nopOut.totalAHSLength = 0; 769 nopOut.dataSegmentLength = nopIn->dataSegmentLength; 770 nopOut.lun = nopIn->lun; 771 nopOut.initiatorTaskTag = 0xffffffff; 772 nopOut.targetTransferTag = nopIn->targetTransferTag; 773 nopOut.cmdSN = fSession->NextCommandSequenceNumber(); 774 nopOut.expStatSN = fStatusSequenceNumber; 775 error = fSocket->Write(&nopOut, sizeof(nopOut)); 776 if (error != B_OK) { 777 TRACE("NOP-Out write error\n"); 778 free(pingData); 779 return error; 780 } 781 if (nopOut.dataSegmentLength > 0) { 782 error = fSocket->Write(pingData, nopOut.dataSegmentLength); 783 free(pingData); 784 if (error != B_OK) { 785 TRACE("NOP-Out ping data write error\n"); 786 return error; 787 } 788 if ((nopOut.dataSegmentLength % ISCSI_ALIGNMENT) != 0) { 789 char buffer[3]; 790 memset(buffer, 0, 3); 791 error = fSocket->Write(buffer, ISCSI_ALIGNMENT - (nopOut.dataSegmentLength % ISCSI_ALIGNMENT)); 792 if (error != B_OK) { 793 TRACE("NOP-Out padding write error\n"); 794 return error; 795 } 796 } 797 } 798 799 error = _Read(resp, sizeof(iscsi_basic_header_segment), timeout); 800 if (error != B_OK) { 801 TRACE("read failed\n"); 802 return error; 803 } 804 } 805 806 return B_OK; 807} 808 809 810status_t 811iSCSIConnection::_Read(void* buffer, size_t bufferSize, bigtime_t timeout) 812{ 813 size_t bytesRead; 814 status_t error = fSocket->Read(buffer, bufferSize, &bytesRead, timeout); 815 if (error != B_OK) 816 return error; 817 if (bytesRead < bufferSize) { 818 dprintf("not enough data available: %lu vs. %lu\n", bytesRead, bufferSize); 819 return B_ERROR; 820 } 821 return B_OK; 822} 823