VirtualBox

source: vbox/trunk/src/VBox/Storage/ISCSI.cpp@ 63785

Last change on this file since 63785 was 63785, checked in by vboxsync, 8 years ago

Storage/VD: Doxygen after VBOXHDDBACKEND -> VDIMAGEBACKEND

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 198.0 KB
Line 
1/* $Id: ISCSI.cpp 63785 2016-09-09 21:48:46Z vboxsync $ */
2/** @file
3 * iSCSI initiator driver, VD backend.
4 */
5
6/*
7 * Copyright (C) 2006-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_VD_ISCSI
23#include <VBox/vd-plugin.h>
24#include <VBox/err.h>
25
26#include <VBox/log.h>
27#include <iprt/alloc.h>
28#include <iprt/assert.h>
29#include <iprt/uuid.h>
30#include <iprt/string.h>
31#include <iprt/asm.h>
32#include <iprt/thread.h>
33#include <iprt/semaphore.h>
34#include <iprt/md5.h>
35#include <iprt/tcp.h>
36#include <iprt/time.h>
37#include <VBox/scsi.h>
38
39#include "VDBackends.h"
40
41
42/*********************************************************************************************************************************
43* Defined Constants And Macros *
44*********************************************************************************************************************************/
45
46/** The maximum number of release log entries per image. */
47#define MAX_LOG_REL_ERRORS 1024
48
49/** Default port number to use for iSCSI. */
50#define ISCSI_DEFAULT_PORT 3260
51
52
53/** Converts a number in the range of 0 - 15 into the corresponding hex char. */
54#define NUM_2_HEX(b) ('0' + (b) + (((b) > 9) ? 39 : 0))
55/** Converts a hex char into the corresponding number in the range 0-15. */
56#define HEX_2_NUM(c) (((c) <= '9') ? ((c) - '0') : (((c - 'A' + 10) & 0xf)))
57/* Converts a base64 char into the corresponding number in the range 0-63. */
58#define B64_2_NUM(c) ((c >= 'A' && c <= 'Z') ? (c - 'A') : (c >= 'a' && c <= 'z') ? (c - 'a' + 26) : (c >= '0' && c <= '9') ? (c - '0' + 52) : (c == '+') ? 62 : (c == '/') ? 63 : -1)
59
60
61/** Minimum CHAP_MD5 challenge length in bytes. */
62#define CHAP_MD5_CHALLENGE_MIN 16
63/** Maximum CHAP_MD5 challenge length in bytes. */
64#define CHAP_MD5_CHALLENGE_MAX 24
65
66
67/**
68 * SCSI peripheral device type. */
69typedef enum SCSIDEVTYPE
70{
71 /** direct-access device. */
72 SCSI_DEVTYPE_DISK = 0,
73 /** sequential-access device. */
74 SCSI_DEVTYPE_TAPE,
75 /** printer device. */
76 SCSI_DEVTYPE_PRINTER,
77 /** processor device. */
78 SCSI_DEVTYPE_PROCESSOR,
79 /** write-once device. */
80 SCSI_DEVTYPE_WORM,
81 /** CD/DVD device. */
82 SCSI_DEVTYPE_CDROM,
83 /** scanner device. */
84 SCSI_DEVTYPE_SCANNER,
85 /** optical memory device. */
86 SCSI_DEVTYPE_OPTICAL,
87 /** medium changer. */
88 SCSI_DEVTYPE_CHANGER,
89 /** communications device. */
90 SCSI_DEVTYPE_COMMUNICATION,
91 /** storage array controller device. */
92 SCSI_DEVTYPE_RAIDCTL = 0x0c,
93 /** enclosure services device. */
94 SCSI_DEVTYPE_ENCLOSURE,
95 /** simplified direct-access device. */
96 SCSI_DEVTYPE_SIMPLEDISK,
97 /** optical card reader/writer device. */
98 SCSI_DEVTYPE_OCRW,
99 /** bridge controller device. */
100 SCSI_DEVTYPE_BRIDGE,
101 /** object-based storage device. */
102 SCSI_DEVTYPE_OSD
103} SCSIDEVTYPE;
104
105/** Mask for extracting the SCSI device type out of the first byte of the INQUIRY response. */
106#define SCSI_DEVTYPE_MASK 0x1f
107
108/** Mask to extract the CmdQue bit out of the seventh byte of the INQUIRY response. */
109#define SCSI_INQUIRY_CMDQUE_MASK 0x02
110
111/** Maximum PDU payload size we can handle in one piece. Greater or equal than
112 * s_iscsiConfigDefaultWriteSplit. */
113#define ISCSI_DATA_LENGTH_MAX _256K
114
115/** Maximum PDU size we can handle in one piece. */
116#define ISCSI_RECV_PDU_BUFFER_SIZE (ISCSI_DATA_LENGTH_MAX + ISCSI_BHS_SIZE)
117
118
119/** Version of the iSCSI standard which this initiator driver can handle. */
120#define ISCSI_MY_VERSION 0
121
122
123/** Length of ISCSI basic header segment. */
124#define ISCSI_BHS_SIZE 48
125
126
127/** Reserved task tag value. */
128#define ISCSI_TASK_TAG_RSVD 0xffffffff
129
130
131/**
132 * iSCSI opcodes. */
133typedef enum ISCSIOPCODE
134{
135 /** NOP-Out. */
136 ISCSIOP_NOP_OUT = 0x00000000,
137 /** SCSI command. */
138 ISCSIOP_SCSI_CMD = 0x01000000,
139 /** SCSI task management request. */
140 ISCSIOP_SCSI_TASKMGMT_REQ = 0x02000000,
141 /** Login request. */
142 ISCSIOP_LOGIN_REQ = 0x03000000,
143 /** Text request. */
144 ISCSIOP_TEXT_REQ = 0x04000000,
145 /** SCSI Data-Out. */
146 ISCSIOP_SCSI_DATA_OUT = 0x05000000,
147 /** Logout request. */
148 ISCSIOP_LOGOUT_REQ = 0x06000000,
149 /** SNACK request. */
150 ISCSIOP_SNACK_REQ = 0x10000000,
151
152 /** NOP-In. */
153 ISCSIOP_NOP_IN = 0x20000000,
154 /** SCSI response. */
155 ISCSIOP_SCSI_RES = 0x21000000,
156 /** SCSI Task Management response. */
157 ISCSIOP_SCSI_TASKMGMT_RES = 0x22000000,
158 /** Login response. */
159 ISCSIOP_LOGIN_RES = 0x23000000,
160 /** Text response. */
161 ISCSIOP_TEXT_RES = 0x24000000,
162 /** SCSI Data-In. */
163 ISCSIOP_SCSI_DATA_IN = 0x25000000,
164 /** Logout response. */
165 ISCSIOP_LOGOUT_RES = 0x26000000,
166 /** Ready To Transfer (R2T). */
167 ISCSIOP_R2T = 0x31000000,
168 /** Asynchronous message. */
169 ISCSIOP_ASYN_MSG = 0x32000000,
170 /** Reject. */
171 ISCSIOP_REJECT = 0x3f000000
172} ISCSIOPCODE;
173
174/** Mask for extracting the iSCSI opcode out of the first header word. */
175#define ISCSIOP_MASK 0x3f000000
176
177
178/** ISCSI BHS word 0: Request should be processed immediately. */
179#define ISCSI_IMMEDIATE_DELIVERY_BIT 0x40000000
180
181/** ISCSI BHS word 0: This is the final PDU for this request/response. */
182#define ISCSI_FINAL_BIT 0x00800000
183/** ISCSI BHS word 0: Mask for extracting the CSG. */
184#define ISCSI_CSG_MASK 0x000c0000
185/** ISCSI BHS word 0: Shift offset for extracting the CSG. */
186#define ISCSI_CSG_SHIFT 18
187/** ISCSI BHS word 0: Mask for extracting the NSG. */
188#define ISCSI_NSG_MASK 0x00030000
189/** ISCSI BHS word 0: Shift offset for extracting the NSG. */
190#define ISCSI_NSG_SHIFT 16
191
192/** ISCSI BHS word 0: task attribute untagged */
193#define ISCSI_TASK_ATTR_UNTAGGED 0x00000000
194/** ISCSI BHS word 0: task attribute simple */
195#define ISCSI_TASK_ATTR_SIMPLE 0x00010000
196/** ISCSI BHS word 0: task attribute ordered */
197#define ISCSI_TASK_ATTR_ORDERED 0x00020000
198/** ISCSI BHS word 0: task attribute head of queue */
199#define ISCSI_TASK_ATTR_HOQ 0x00030000
200/** ISCSI BHS word 0: task attribute ACA */
201#define ISCSI_TASK_ATTR_ACA 0x00040000
202
203/** ISCSI BHS word 0: transit to next login phase. */
204#define ISCSI_TRANSIT_BIT 0x00800000
205/** ISCSI BHS word 0: continue with login negotiation. */
206#define ISCSI_CONTINUE_BIT 0x00400000
207
208/** ISCSI BHS word 0: residual underflow. */
209#define ISCSI_RESIDUAL_UNFL_BIT 0x00020000
210/** ISCSI BHS word 0: residual overflow. */
211#define ISCSI_RESIDUAL_OVFL_BIT 0x00040000
212/** ISCSI BHS word 0: Bidirectional read residual underflow. */
213#define ISCSI_BI_READ_RESIDUAL_UNFL_BIT 0x00080000
214/** ISCSI BHS word 0: Bidirectional read residual overflow. */
215#define ISCSI_BI_READ_RESIDUAL_OVFL_BIT 0x00100000
216
217/** ISCSI BHS word 0: SCSI response mask. */
218#define ISCSI_SCSI_RESPONSE_MASK 0x0000ff00
219/** ISCSI BHS word 0: SCSI status mask. */
220#define ISCSI_SCSI_STATUS_MASK 0x000000ff
221
222/** ISCSI BHS word 0: response includes status. */
223#define ISCSI_STATUS_BIT 0x00010000
224
225/** Maximum number of scatter/gather segments needed to send a PDU. */
226#define ISCSI_SG_SEGMENTS_MAX 4
227
228/** Number of entries in the command table. */
229#define ISCSI_CMD_WAITING_ENTRIES 32
230
231/**
232 * iSCSI login status class. */
233typedef enum ISCSILOGINSTATUSCLASS
234{
235 /** Success. */
236 ISCSI_LOGIN_STATUS_CLASS_SUCCESS = 0,
237 /** Redirection. */
238 ISCSI_LOGIN_STATUS_CLASS_REDIRECTION,
239 /** Initiator error. */
240 ISCSI_LOGIN_STATUS_CLASS_INITIATOR_ERROR,
241 /** Target error. */
242 ISCSI_LOGIN_STATUS_CLASS_TARGET_ERROR
243} ISCSILOGINSTATUSCLASS;
244
245
246/**
247 * iSCSI connection state. */
248typedef enum ISCSISTATE
249{
250 /** Not having a connection/session at all. */
251 ISCSISTATE_FREE,
252 /** Currently trying to login. */
253 ISCSISTATE_IN_LOGIN,
254 /** Normal operation, corresponds roughly to the Full Feature Phase. */
255 ISCSISTATE_NORMAL,
256 /** Currently trying to logout. */
257 ISCSISTATE_IN_LOGOUT
258} ISCSISTATE;
259
260/**
261 * iSCSI PDU send/receive flags (and maybe more in the future). */
262typedef enum ISCSIPDUFLAGS
263{
264 /** No special flags */
265 ISCSIPDU_DEFAULT = 0,
266 /** Do not attempt to re-attach to the target if the connection is lost */
267 ISCSIPDU_NO_REATTACH = RT_BIT(1)
268} ISCSIPDUFLAGS;
269
270
271/*********************************************************************************************************************************
272* Structures and Typedefs *
273*********************************************************************************************************************************/
274
275/**
276 * iSCSI login negotiation parameter
277 */
278typedef struct ISCSIPARAMETER
279{
280 /** Name of the parameter. */
281 const char *pszParamName;
282 /** Value of the parameter. */
283 const char *pszParamValue;
284 /** Length of the binary parameter. 0=zero-terminated string. */
285 size_t cbParamValue;
286} ISCSIPARAMETER;
287
288
289/**
290 * iSCSI Response PDU buffer (scatter).
291 */
292typedef struct ISCSIRES
293{
294 /** Length of PDU segment. */
295 size_t cbSeg;
296 /** Pointer to PDU segment. */
297 void *pvSeg;
298} ISCSIRES;
299/** Pointer to an iSCSI Response PDU buffer. */
300typedef ISCSIRES *PISCSIRES;
301/** Pointer to a const iSCSI Response PDU buffer. */
302typedef ISCSIRES const *PCISCSIRES;
303
304
305/**
306 * iSCSI Request PDU buffer (gather).
307 */
308typedef struct ISCSIREQ
309{
310 /** Length of PDU segment in bytes. */
311 size_t cbSeg;
312 /** Pointer to PDU segment. */
313 const void *pcvSeg;
314} ISCSIREQ;
315/** Pointer to an iSCSI Request PDU buffer. */
316typedef ISCSIREQ *PISCSIREQ;
317/** Pointer to a const iSCSI Request PDU buffer. */
318typedef ISCSIREQ const *PCISCSIREQ;
319
320
321/**
322 * SCSI transfer directions.
323 */
324typedef enum SCSIXFER
325{
326 SCSIXFER_NONE = 0,
327 SCSIXFER_TO_TARGET,
328 SCSIXFER_FROM_TARGET,
329 SCSIXFER_TO_FROM_TARGET
330} SCSIXFER, *PSCSIXFER;
331
332/** Forward declaration. */
333typedef struct ISCSIIMAGE *PISCSIIMAGE;
334
335/**
336 * SCSI request structure.
337 */
338typedef struct SCSIREQ
339{
340 /** I/O context associated with this request. */
341 PVDIOCTX pIoCtx;
342 /** Transfer direction. */
343 SCSIXFER enmXfer;
344 /** Length of command block. */
345 size_t cbCDB;
346 /** Length of Initiator2Target data buffer. */
347 size_t cbI2TData;
348 /** Length of Target2Initiator data buffer. */
349 size_t cbT2IData;
350 /** Length of sense buffer
351 * This contains the number of sense bytes received upon completion. */
352 size_t cbSense;
353 /** Completion status of the command. */
354 uint8_t status;
355 /** The CDB. */
356 uint8_t abCDB[16];
357 /** The sense buffer. */
358 uint8_t abSense[96];
359 /** Status code to return if we got sense data. */
360 int rcSense;
361 /** Pointer to the Initiator2Target S/G list. */
362 PRTSGSEG paI2TSegs;
363 /** Number of entries in the I2T S/G list. */
364 unsigned cI2TSegs;
365 /** Pointer to the Target2Initiator S/G list. */
366 PRTSGSEG paT2ISegs;
367 /** Number of entries in the T2I S/G list. */
368 unsigned cT2ISegs;
369 /** S/G buffer for the target to initiator bits. */
370 RTSGBUF SgBufT2I;
371 /** Number of retries if the command completes with sense
372 * data before we return with an error.
373 */
374 unsigned cSenseRetries;
375 /** The S/G list - variable in size.
376 * This array holds both the I2T and T2I segments.
377 * The I2T segments are first and the T2I are second.
378 */
379 RTSGSEG aSegs[1];
380} SCSIREQ, *PSCSIREQ;
381
382typedef enum ISCSICMDTYPE
383{
384 /** Process a SCSI request. */
385 ISCSICMDTYPE_REQ = 0,
386 /** Call a function in the I/O thread. */
387 ISCSICMDTYPE_EXEC,
388 /** Usual 32bit hack. */
389 ISCSICMDTYPE_32BIT_HACK = 0x7fffffff
390} ISCSICMDTYPE;
391
392
393/** The command completion function. */
394typedef DECLCALLBACK(void) FNISCSICMDCOMPLETED(PISCSIIMAGE pImage, int rcReq, void *pvUser);
395/** Pointer to a command completion function. */
396typedef FNISCSICMDCOMPLETED *PFNISCSICMDCOMPLETED;
397
398/** The command execution function. */
399typedef DECLCALLBACK(int) FNISCSIEXEC(void *pvUser);
400/** Pointer to a command execution function. */
401typedef FNISCSIEXEC *PFNISCSIEXEC;
402
403/**
404 * Structure used to complete a synchronous request.
405 */
406typedef struct ISCSICMDSYNC
407{
408 /** Event semaphore to wakeup the waiting thread. */
409 RTSEMEVENT EventSem;
410 /** Status code of the command. */
411 int rcCmd;
412} ISCSICMDSYNC, *PISCSICMDSYNC;
413
414/**
415 * iSCSI command.
416 * Used to forward requests to the I/O thread
417 * if existing.
418 */
419typedef struct ISCSICMD
420{
421 /** Next one in the list. */
422 struct ISCSICMD *pNext;
423 /** Assigned ITT. */
424 uint32_t Itt;
425 /** Completion callback. */
426 PFNISCSICMDCOMPLETED pfnComplete;
427 /** Opaque user data. */
428 void *pvUser;
429 /** Command to execute. */
430 ISCSICMDTYPE enmCmdType;
431 /** Command type dependent data. */
432 union
433 {
434 /** Process a SCSI request. */
435 struct
436 {
437 /** The SCSI request to process. */
438 PSCSIREQ pScsiReq;
439 } ScsiReq;
440 /** Call a function in the I/O thread. */
441 struct
442 {
443 /** The method to execute. */
444 PFNISCSIEXEC pfnExec;
445 /** User data. */
446 void *pvUser;
447 } Exec;
448 } CmdType;
449} ISCSICMD, *PISCSICMD;
450
451/**
452 * Send iSCSI PDU.
453 * Contains all necessary data to send a PDU.
454 */
455typedef struct ISCSIPDUTX
456{
457 /** Pointer to the next PDu to send. */
458 struct ISCSIPDUTX *pNext;
459 /** The BHS. */
460 uint32_t aBHS[12];
461 /** Assigned CmdSN for this PDU. */
462 uint32_t CmdSN;
463 /** The S/G buffer used for sending. */
464 RTSGBUF SgBuf;
465 /** Number of bytes to send until the PDU completed. */
466 size_t cbSgLeft;
467 /** The iSCSI command this PDU belongs to. */
468 PISCSICMD pIScsiCmd;
469 /** Number of segments in the request segments array. */
470 unsigned cISCSIReq;
471 /** The request segments - variable in size. */
472 RTSGSEG aISCSIReq[1];
473} ISCSIPDUTX, *PISCSIPDUTX;
474
475/**
476 * Block driver instance data.
477 */
478typedef struct ISCSIIMAGE
479{
480 /** Pointer to the filename (location). Not really used. */
481 const char *pszFilename;
482 /** Pointer to the initiator name. */
483 char *pszInitiatorName;
484 /** Pointer to the target name. */
485 char *pszTargetName;
486 /** Pointer to the target address. */
487 char *pszTargetAddress;
488 /** Pointer to the user name for authenticating the Initiator. */
489 char *pszInitiatorUsername;
490 /** Pointer to the secret for authenticating the Initiator. */
491 uint8_t *pbInitiatorSecret;
492 /** Length of the secret for authenticating the Initiator. */
493 size_t cbInitiatorSecret;
494 /** Pointer to the user name for authenticating the Target. */
495 char *pszTargetUsername;
496 /** Pointer to the secret for authenticating the Initiator. */
497 uint8_t *pbTargetSecret;
498 /** Length of the secret for authenticating the Initiator. */
499 size_t cbTargetSecret;
500 /** Limit for iSCSI writes, essentially limiting the amount of data
501 * written in a single write. This is negotiated with the target, so
502 * the actual size might be smaller. */
503 uint32_t cbWriteSplit;
504 /** Initiator session identifier. */
505 uint64_t ISID;
506 /** SCSI Logical Unit Number. */
507 uint64_t LUN;
508 /** Pointer to the per-disk VD interface list. */
509 PVDINTERFACE pVDIfsDisk;
510 /** Pointer to the per-image VD interface list. */
511 PVDINTERFACE pVDIfsImage;
512 /** Error interface. */
513 PVDINTERFACEERROR pIfError;
514 /** Config interface. */
515 PVDINTERFACECONFIG pIfConfig;
516 /** I/O interface. */
517 PVDINTERFACEIOINT pIfIo;
518 /** TCP network stack interface. */
519 PVDINTERFACETCPNET pIfNet;
520 /** Image open flags. */
521 unsigned uOpenFlags;
522 /** Number of re-login retries when a connection fails. */
523 uint32_t cISCSIRetries;
524 /** Sector size on volume. */
525 uint32_t cbSector;
526 /** Size of volume in sectors. */
527 uint64_t cVolume;
528 /** Total volume size in bytes. Easier than multiplying the above values all the time. */
529 uint64_t cbSize;
530
531 /** Negotiated maximum data length when sending to target. */
532 uint32_t cbSendDataLength;
533 /** Negotiated maximum data length when receiving from target. */
534 uint32_t cbRecvDataLength;
535
536 /** Current state of the connection/session. */
537 ISCSISTATE state;
538 /** Flag whether the first Login Response PDU has been seen. */
539 bool FirstRecvPDU;
540 /** Initiator Task Tag of the last iSCSI request PDU. */
541 uint32_t ITT;
542 /** Sequence number of the last command. */
543 uint32_t CmdSN;
544 /** Sequence number of the next command expected by the target. */
545 uint32_t ExpCmdSN;
546 /** Maximum sequence number accepted by the target (determines size of window). */
547 uint32_t MaxCmdSN;
548 /** Expected sequence number of next status. */
549 uint32_t ExpStatSN;
550 /** Currently active request. */
551 PISCSIREQ paCurrReq;
552 /** Segment number of currently active request. */
553 uint32_t cnCurrReq;
554 /** Pointer to receive PDU buffer. (Freed by RT) */
555 void *pvRecvPDUBuf;
556 /** Length of receive PDU buffer. */
557 size_t cbRecvPDUBuf;
558 /** Mutex protecting against concurrent use from several threads. */
559 RTSEMMUTEX Mutex;
560
561 /** Pointer to the target hostname. */
562 char *pszHostname;
563 /** Port to use on the target host. */
564 uint32_t uPort;
565 /** Socket handle of the TCP connection. */
566 VDSOCKET Socket;
567 /** Timeout for read operations on the TCP connection (in milliseconds). */
568 uint32_t uReadTimeout;
569 /** Flag whether to automatically generate the initiator name. */
570 bool fAutomaticInitiatorName;
571 /** Flag whether to use the host IP stack or DevINIP. */
572 bool fHostIP;
573 /** Flag whether to dump malformed packets in the release log. */
574 bool fDumpMalformedPackets;
575 /** Flag whtether the target is readonly. */
576 bool fTargetReadOnly;
577 /** Flag whether to retry the connection before processing new requests. */
578 bool fTryReconnect;
579
580 /** Head of request queue */
581 PISCSICMD pScsiReqQueue;
582 /** Mutex protecting the request queue from concurrent access. */
583 RTSEMMUTEX MutexReqQueue;
584 /** I/O thread. */
585 RTTHREAD hThreadIo;
586 /** Flag whether the thread should be still running. */
587 volatile bool fRunning;
588 /* Flag whether the target supports command queuing. */
589 bool fCmdQueuingSupported;
590 /** Flag whether extended select is supported. */
591 bool fExtendedSelectSupported;
592 /** Padding used for aligning the PDUs. */
593 uint8_t aPadding[4];
594 /** Socket events to poll for. */
595 uint32_t fPollEvents;
596 /** Number of bytes to read to complete the current PDU. */
597 size_t cbRecvPDUResidual;
598 /** Current position in the PDU buffer. */
599 uint8_t *pbRecvPDUBufCur;
600 /** Flag whether we are currently reading the BHS. */
601 bool fRecvPDUBHS;
602 /** List of PDUs waiting to get transmitted. */
603 PISCSIPDUTX pIScsiPDUTxHead;
604 /** Tail of PDUs waiting to get transmitted. */
605 PISCSIPDUTX pIScsiPDUTxTail;
606 /** PDU we are currently transmitting. */
607 PISCSIPDUTX pIScsiPDUTxCur;
608 /** Number of commands waiting for an answer from the target.
609 * Used for timeout handling for poll.
610 */
611 unsigned cCmdsWaiting;
612 /** Table of commands waiting for a response from the target. */
613 PISCSICMD aCmdsWaiting[ISCSI_CMD_WAITING_ENTRIES];
614 /** Number of logins since last successful I/O.
615 * Used to catch the case where logging succeeds but
616 * processing read/write/flushes cause a disconnect.
617 */
618 volatile uint32_t cLoginsSinceIo;
619
620 /** Release log counter. */
621 unsigned cLogRelErrors;
622} ISCSIIMAGE;
623
624
625/*********************************************************************************************************************************
626* Static Variables *
627*********************************************************************************************************************************/
628
629/** Default initiator basename. */
630static const char *s_iscsiDefaultInitiatorBasename = "iqn.2009-08.com.sun.virtualbox.initiator";
631
632/** Default LUN. */
633static const char *s_iscsiConfigDefaultLUN = "0";
634
635/** Default timeout, 10 seconds. */
636static const char *s_iscsiConfigDefaultTimeout = "10000";
637
638/** Default write split value, less or equal to ISCSI_DATA_LENGTH_MAX. */
639static const char *s_iscsiConfigDefaultWriteSplit = "262144";
640
641/** Default host IP stack. */
642static const char *s_iscsiConfigDefaultHostIPStack = "1";
643
644/** Default dump malformed packet configuration value. */
645static const char *s_iscsiConfigDefaultDumpMalformedPackets = "0";
646
647/** Description of all accepted config parameters. */
648static const VDCONFIGINFO s_iscsiConfigInfo[] =
649{
650 { "TargetName", NULL, VDCFGVALUETYPE_STRING, VD_CFGKEY_MANDATORY },
651 /* LUN is defined of string type to handle the "enc" prefix. */
652 { "LUN", s_iscsiConfigDefaultLUN, VDCFGVALUETYPE_STRING, VD_CFGKEY_MANDATORY },
653 { "TargetAddress", NULL, VDCFGVALUETYPE_STRING, VD_CFGKEY_MANDATORY },
654 { "InitiatorName", NULL, VDCFGVALUETYPE_STRING, 0 },
655 { "InitiatorUsername", NULL, VDCFGVALUETYPE_STRING, 0 },
656 { "InitiatorSecret", NULL, VDCFGVALUETYPE_BYTES, 0 },
657 { "TargetUsername", NULL, VDCFGVALUETYPE_STRING, VD_CFGKEY_EXPERT },
658 { "TargetSecret", NULL, VDCFGVALUETYPE_BYTES, VD_CFGKEY_EXPERT },
659 { "WriteSplit", s_iscsiConfigDefaultWriteSplit, VDCFGVALUETYPE_INTEGER, VD_CFGKEY_EXPERT },
660 { "Timeout", s_iscsiConfigDefaultTimeout, VDCFGVALUETYPE_INTEGER, VD_CFGKEY_EXPERT },
661 { "HostIPStack", s_iscsiConfigDefaultHostIPStack, VDCFGVALUETYPE_INTEGER, VD_CFGKEY_EXPERT },
662 { "DumpMalformedPackets", s_iscsiConfigDefaultDumpMalformedPackets, VDCFGVALUETYPE_INTEGER, VD_CFGKEY_EXPERT },
663 { NULL, NULL, VDCFGVALUETYPE_INTEGER, 0 }
664};
665
666
667/*********************************************************************************************************************************
668* Internal Functions *
669*********************************************************************************************************************************/
670
671/* iSCSI low-level functions (only to be used from the iSCSI high-level functions). */
672static uint32_t iscsiNewITT(PISCSIIMAGE pImage);
673static int iscsiSendPDU(PISCSIIMAGE pImage, PISCSIREQ paReq, uint32_t cnReq, uint32_t uFlags);
674static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes, uint32_t fFlags);
675static int iscsiRecvPDUAsync(PISCSIIMAGE pImage);
676static int iscsiSendPDUAsync(PISCSIIMAGE pImage);
677static int iscsiValidatePDU(PISCSIRES paRes, uint32_t cnRes);
678static int iscsiRecvPDUProcess(PISCSIIMAGE pImage, PISCSIRES paRes, uint32_t cnRes);
679static int iscsiPDUTxPrepare(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd);
680static int iscsiRecvPDUUpdateRequest(PISCSIIMAGE pImage, PISCSIRES paRes, uint32_t cnRes);
681static void iscsiCmdComplete(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd, int rcCmd);
682static int iscsiTextAddKeyValue(uint8_t *pbBuf, size_t cbBuf, size_t *pcbBufCurr, const char *pcszKey, const char *pcszValue, size_t cbValue);
683static int iscsiTextGetKeyValue(const uint8_t *pbBuf, size_t cbBuf, const char *pcszKey, const char **ppcszValue);
684static int iscsiStrToBinary(const char *pcszValue, uint8_t *pbValue, size_t *pcbValue);
685static int iscsiUpdateParameters(PISCSIIMAGE pImage, const uint8_t *pbBuf, size_t cbBuf);
686
687/* Serial number arithmetic comparison. */
688static bool serial_number_less(uint32_t sn1, uint32_t sn2);
689static bool serial_number_greater(uint32_t sn1, uint32_t sn2);
690
691/* CHAP-MD5 functions. */
692#ifdef IMPLEMENT_TARGET_AUTH
693static void chap_md5_generate_challenge(uint8_t *pbChallenge, size_t *pcbChallenge);
694#endif
695static void chap_md5_compute_response(uint8_t *pbResponse, uint8_t id, const uint8_t *pbChallenge, size_t cbChallenge,
696 const uint8_t *pbSecret, size_t cbSecret);
697
698/**
699 * Internal: release log wrapper limiting the number of entries.
700 */
701DECLINLINE(void) iscsiLogRel(PISCSIIMAGE pImage, const char *pcszFormat, ...)
702{
703 if (pImage->cLogRelErrors++ < MAX_LOG_REL_ERRORS)
704 {
705 va_list va;
706
707 va_start(va, pcszFormat);
708 LogRel(("%N\n", pcszFormat, &va));
709 va_end(va);
710 }
711}
712
713DECLINLINE(bool) iscsiIsClientConnected(PISCSIIMAGE pImage)
714{
715 return pImage->Socket != NIL_VDSOCKET
716 && pImage->pIfNet->pfnIsClientConnected(pImage->Socket);
717}
718
719/**
720 * Calculates the hash for the given ITT used
721 * to look up the command in the table.
722 */
723DECLINLINE(uint32_t) iscsiIttHash(uint32_t Itt)
724{
725 return Itt % ISCSI_CMD_WAITING_ENTRIES;
726}
727
728static PISCSICMD iscsiCmdGetFromItt(PISCSIIMAGE pImage, uint32_t Itt)
729{
730 PISCSICMD pIScsiCmd = NULL;
731
732 pIScsiCmd = pImage->aCmdsWaiting[iscsiIttHash(Itt)];
733
734 while ( pIScsiCmd
735 && pIScsiCmd->Itt != Itt)
736 pIScsiCmd = pIScsiCmd->pNext;
737
738 return pIScsiCmd;
739}
740
741static void iscsiCmdInsert(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd)
742{
743 PISCSICMD pIScsiCmdOld;
744 uint32_t idx = iscsiIttHash(pIScsiCmd->Itt);
745
746 Assert(!pIScsiCmd->pNext);
747
748 pIScsiCmdOld = pImage->aCmdsWaiting[idx];
749 pIScsiCmd->pNext = pIScsiCmdOld;
750 pImage->aCmdsWaiting[idx] = pIScsiCmd;
751 pImage->cCmdsWaiting++;
752}
753
754static PISCSICMD iscsiCmdRemove(PISCSIIMAGE pImage, uint32_t Itt)
755{
756 PISCSICMD pIScsiCmd = NULL;
757 PISCSICMD pIScsiCmdPrev = NULL;
758 uint32_t idx = iscsiIttHash(Itt);
759
760 pIScsiCmd = pImage->aCmdsWaiting[idx];
761
762 while ( pIScsiCmd
763 && pIScsiCmd->Itt != Itt)
764 {
765 pIScsiCmdPrev = pIScsiCmd;
766 pIScsiCmd = pIScsiCmd->pNext;
767 }
768
769 if (pIScsiCmd)
770 {
771 if (pIScsiCmdPrev)
772 {
773 Assert(!pIScsiCmd->pNext || VALID_PTR(pIScsiCmd->pNext));
774 pIScsiCmdPrev->pNext = pIScsiCmd->pNext;
775 }
776 else
777 {
778 pImage->aCmdsWaiting[idx] = pIScsiCmd->pNext;
779 Assert(!pImage->aCmdsWaiting[idx] || VALID_PTR(pImage->aCmdsWaiting[idx]));
780 }
781 pImage->cCmdsWaiting--;
782 }
783
784 return pIScsiCmd;
785}
786
787/**
788 * Removes all commands from the table and returns the
789 * list head
790 *
791 * @returns Pointer to the head of the command list.
792 * @param pImage iSCSI connection to use.
793 */
794static PISCSICMD iscsiCmdRemoveAll(PISCSIIMAGE pImage)
795{
796 PISCSICMD pIScsiCmdHead = NULL;
797
798 for (unsigned idx = 0; idx < RT_ELEMENTS(pImage->aCmdsWaiting); idx++)
799 {
800 PISCSICMD pHead;
801 PISCSICMD pTail;
802
803 pHead = pImage->aCmdsWaiting[idx];
804 pImage->aCmdsWaiting[idx] = NULL;
805
806 if (pHead)
807 {
808 /* Get the tail. */
809 pTail = pHead;
810 while (pTail->pNext)
811 pTail = pTail->pNext;
812
813 /* Concatenate. */
814 pTail->pNext = pIScsiCmdHead;
815 pIScsiCmdHead = pHead;
816 }
817 }
818 pImage->cCmdsWaiting = 0;
819
820 return pIScsiCmdHead;
821}
822
823/**
824 * Dumps an iSCSI packet if enabled.
825 *
826 * @returns nothing.
827 * @param pImage The iSCSI image instance data.
828 * @param paISCSISegs Pointer to the segments array.
829 * @param cnISCSISegs Number of segments in the array.
830 * @param rc Status code for this packet.
831 * @param fRequest Flag whether this is request or response packet.
832 */
833static void iscsiDumpPacket(PISCSIIMAGE pImage, PISCSIREQ paISCSISegs, unsigned cnISCSISegs, int rc, bool fRequest)
834{
835 if (pImage->fDumpMalformedPackets)
836 {
837 LogRel(("iSCSI{%s}: Dumping %s packet completed with status code %Rrc\n", pImage->pszTargetName, fRequest ? "request" : "response", rc));
838 for (unsigned i = 0; i < cnISCSISegs; i++)
839 {
840 if (paISCSISegs[i].cbSeg)
841 {
842 LogRel(("iSCSI{%s}: Segment %u, size %zu\n"
843 "%.*Rhxd\n",
844 pImage->pszTargetName, i, paISCSISegs[i].cbSeg,
845 paISCSISegs[i].cbSeg, paISCSISegs[i].pcvSeg));
846 }
847 }
848 }
849}
850
851static int iscsiTransportConnect(PISCSIIMAGE pImage)
852{
853 int rc;
854 if (!pImage->pszHostname)
855 return VERR_NET_DEST_ADDRESS_REQUIRED;
856
857 rc = pImage->pIfNet->pfnClientConnect(pImage->Socket, pImage->pszHostname, pImage->uPort, pImage->uReadTimeout);
858 if (RT_FAILURE(rc))
859 {
860 if ( rc == VERR_NET_CONNECTION_REFUSED
861 || rc == VERR_NET_CONNECTION_RESET
862 || rc == VERR_NET_UNREACHABLE
863 || rc == VERR_NET_HOST_UNREACHABLE
864 || rc == VERR_NET_CONNECTION_TIMED_OUT)
865 {
866 /* Standardize return value for no connection. */
867 rc = VERR_NET_CONNECTION_REFUSED;
868 }
869 return rc;
870 }
871
872 /* Disable Nagle algorithm, we want things to be sent immediately. */
873 pImage->pIfNet->pfnSetSendCoalescing(pImage->Socket, false);
874
875 /* Make initiator name and ISID unique on this host. */
876 RTNETADDR LocalAddr;
877 rc = pImage->pIfNet->pfnGetLocalAddress(pImage->Socket, &LocalAddr);
878 if (RT_FAILURE(rc))
879 return rc;
880 if ( LocalAddr.uPort == RTNETADDR_PORT_NA
881 || LocalAddr.uPort > 65535)
882 return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED;
883 pImage->ISID &= ~65535ULL;
884 pImage->ISID |= LocalAddr.uPort;
885 /* Eliminate the port so that it isn't included below. */
886 LocalAddr.uPort = RTNETADDR_PORT_NA;
887 if (pImage->fAutomaticInitiatorName)
888 {
889 if (pImage->pszInitiatorName)
890 RTStrFree(pImage->pszInitiatorName);
891 RTStrAPrintf(&pImage->pszInitiatorName, "%s:01:%RTnaddr",
892 s_iscsiDefaultInitiatorBasename, &LocalAddr);
893 if (!pImage->pszInitiatorName)
894 return VERR_NO_MEMORY;
895 }
896 LogRel(("iSCSI: connect from initiator %s with source port %u\n", pImage->pszInitiatorName, pImage->ISID & 65535));
897 return VINF_SUCCESS;
898}
899
900
901static int iscsiTransportClose(PISCSIIMAGE pImage)
902{
903 int rc;
904
905 LogFlowFunc(("(%s:%d)\n", pImage->pszHostname, pImage->uPort));
906 if (iscsiIsClientConnected(pImage))
907 {
908 LogRel(("iSCSI: disconnect from initiator %s with source port %u\n", pImage->pszInitiatorName, pImage->ISID & 65535));
909 rc = pImage->pIfNet->pfnClientClose(pImage->Socket);
910 }
911 else
912 rc = VINF_SUCCESS;
913 LogFlowFunc(("returns %Rrc\n", rc));
914 return rc;
915}
916
917
918static int iscsiTransportRead(PISCSIIMAGE pImage, PISCSIRES paResponse, unsigned int cnResponse)
919{
920 int rc = VINF_SUCCESS;
921 unsigned int i = 0;
922 size_t cbToRead, cbActuallyRead, residual, cbSegActual = 0, cbAHSLength, cbDataLength;
923 char *pDst;
924
925 LogFlowFunc(("cnResponse=%d (%s:%d)\n", cnResponse, pImage->pszHostname, pImage->uPort));
926 if (!iscsiIsClientConnected(pImage))
927 {
928 /* Reconnecting makes no sense in this case, as there will be nothing
929 * to receive. We would just run into a timeout. */
930 rc = VERR_BROKEN_PIPE;
931 }
932
933 if (RT_SUCCESS(rc) && paResponse[0].cbSeg >= ISCSI_BHS_SIZE)
934 {
935 cbToRead = 0;
936 residual = ISCSI_BHS_SIZE; /* Do not read more than the BHS length before the true PDU length is known. */
937 cbSegActual = residual;
938 pDst = (char *)paResponse[i].pvSeg;
939 uint64_t u64Timeout = RTTimeMilliTS() + pImage->uReadTimeout;
940 do
941 {
942 int64_t cMilliesRemaining = u64Timeout - RTTimeMilliTS();
943 if (cMilliesRemaining <= 0)
944 {
945 rc = VERR_TIMEOUT;
946 break;
947 }
948 Assert(cMilliesRemaining < 1000000);
949 rc = pImage->pIfNet->pfnSelectOne(pImage->Socket, cMilliesRemaining);
950 if (RT_FAILURE(rc))
951 break;
952 rc = pImage->pIfNet->pfnRead(pImage->Socket, pDst, residual, &cbActuallyRead);
953 if (RT_FAILURE(rc))
954 break;
955 if (cbActuallyRead == 0)
956 {
957 /* The other end has closed the connection. */
958 iscsiTransportClose(pImage);
959 pImage->state = ISCSISTATE_FREE;
960 rc = VERR_NET_CONNECTION_RESET;
961 break;
962 }
963 if (cbToRead == 0)
964 {
965 /* Currently reading the BHS. */
966 residual -= cbActuallyRead;
967 pDst += cbActuallyRead;
968 if (residual <= 40)
969 {
970 /* Enough data read to figure out the actual PDU size. */
971 uint32_t word1 = RT_N2H_U32(((uint32_t *)(paResponse[0].pvSeg))[1]);
972 cbAHSLength = (word1 & 0xff000000) >> 24;
973 cbAHSLength = ((cbAHSLength - 1) | 3) + 1; /* Add padding. */
974 cbDataLength = word1 & 0x00ffffff;
975 cbDataLength = ((cbDataLength - 1) | 3) + 1; /* Add padding. */
976 cbToRead = residual + cbAHSLength + cbDataLength;
977 residual += paResponse[0].cbSeg - ISCSI_BHS_SIZE;
978 if (residual > cbToRead)
979 residual = cbToRead;
980 cbSegActual = ISCSI_BHS_SIZE + cbAHSLength + cbDataLength;
981 /* Check whether we are already done with this PDU (no payload). */
982 if (cbToRead == 0)
983 break;
984 }
985 }
986 else
987 {
988 cbToRead -= cbActuallyRead;
989 if (cbToRead == 0)
990 break;
991 pDst += cbActuallyRead;
992 residual -= cbActuallyRead;
993 }
994 if (residual == 0)
995 {
996 i++;
997 if (i >= cnResponse)
998 {
999 /* No space left in receive buffers. */
1000 rc = VERR_BUFFER_OVERFLOW;
1001 break;
1002 }
1003 pDst = (char *)paResponse[i].pvSeg;
1004 residual = paResponse[i].cbSeg;
1005 if (residual > cbToRead)
1006 residual = cbToRead;
1007 cbSegActual = residual;
1008 }
1009 LogFlowFunc(("cbToRead=%u residual=%u cbSegActual=%u cbActuallRead=%u\n",
1010 cbToRead, residual, cbSegActual, cbActuallyRead));
1011 } while (true);
1012 }
1013 else
1014 {
1015 if (RT_SUCCESS(rc))
1016 rc = VERR_BUFFER_OVERFLOW;
1017 }
1018 if (RT_SUCCESS(rc))
1019 {
1020 paResponse[i].cbSeg = cbSegActual;
1021 for (i++; i < cnResponse; i++)
1022 paResponse[i].cbSeg = 0;
1023 }
1024
1025 if (RT_UNLIKELY( RT_FAILURE(rc)
1026 && ( rc == VERR_NET_CONNECTION_RESET
1027 || rc == VERR_NET_CONNECTION_ABORTED
1028 || rc == VERR_NET_CONNECTION_RESET_BY_PEER
1029 || rc == VERR_NET_CONNECTION_REFUSED
1030 || rc == VERR_BROKEN_PIPE)))
1031 {
1032 /* Standardize return value for broken connection. */
1033 rc = VERR_BROKEN_PIPE;
1034 }
1035
1036 LogFlowFunc(("returns %Rrc\n", rc));
1037 return rc;
1038}
1039
1040
1041static int iscsiTransportWrite(PISCSIIMAGE pImage, PISCSIREQ paRequest, unsigned int cnRequest)
1042{
1043 int rc = VINF_SUCCESS;
1044 unsigned int i;
1045
1046 LogFlowFunc(("cnRequest=%d (%s:%d)\n", cnRequest, pImage->pszHostname, pImage->uPort));
1047 if (!iscsiIsClientConnected(pImage))
1048 {
1049 /* Attempt to reconnect if the connection was previously broken. */
1050 rc = iscsiTransportConnect(pImage);
1051 }
1052
1053 if (RT_SUCCESS(rc))
1054 {
1055 /* Construct scatter/gather buffer for entire request, worst case
1056 * needs twice as many entries to allow for padding. */
1057 unsigned cBuf = 0;
1058 for (i = 0; i < cnRequest; i++)
1059 {
1060 cBuf++;
1061 if (paRequest[i].cbSeg & 3)
1062 cBuf++;
1063 }
1064 Assert(cBuf < ISCSI_SG_SEGMENTS_MAX);
1065 RTSGBUF buf;
1066 RTSGSEG aSeg[ISCSI_SG_SEGMENTS_MAX];
1067 static char aPad[4] = { 0, 0, 0, 0 };
1068 RTSgBufInit(&buf, &aSeg[0], cBuf);
1069 unsigned iBuf = 0;
1070 for (i = 0; i < cnRequest; i++)
1071 {
1072 /* Actual data chunk. */
1073 aSeg[iBuf].pvSeg = (void *)paRequest[i].pcvSeg;
1074 aSeg[iBuf].cbSeg = paRequest[i].cbSeg;
1075 iBuf++;
1076 /* Insert proper padding before the next chunk. */
1077 if (paRequest[i].cbSeg & 3)
1078 {
1079 aSeg[iBuf].pvSeg = &aPad[0];
1080 aSeg[iBuf].cbSeg = 4 - (paRequest[i].cbSeg & 3);
1081 iBuf++;
1082 }
1083 }
1084 /* Send out the request, the socket is set to send data immediately,
1085 * avoiding unnecessary delays. */
1086 rc = pImage->pIfNet->pfnSgWrite(pImage->Socket, &buf);
1087
1088 }
1089
1090 if (RT_UNLIKELY( RT_FAILURE(rc)
1091 && ( rc == VERR_NET_CONNECTION_RESET
1092 || rc == VERR_NET_CONNECTION_ABORTED
1093 || rc == VERR_NET_CONNECTION_RESET_BY_PEER
1094 || rc == VERR_NET_CONNECTION_REFUSED
1095 || rc == VERR_BROKEN_PIPE)))
1096 {
1097 /* Standardize return value for broken connection. */
1098 rc = VERR_BROKEN_PIPE;
1099 }
1100
1101 LogFlowFunc(("returns %Rrc\n", rc));
1102 return rc;
1103}
1104
1105
1106static int iscsiTransportOpen(PISCSIIMAGE pImage)
1107{
1108 int rc = VINF_SUCCESS;
1109 size_t cbHostname = 0; /* shut up gcc */
1110 const char *pcszPort = NULL; /* shut up gcc */
1111 char *pszPortEnd;
1112 uint16_t uPort;
1113
1114 /* Clean up previous connection data. */
1115 iscsiTransportClose(pImage);
1116 if (pImage->pszHostname)
1117 {
1118 RTMemFree(pImage->pszHostname);
1119 pImage->pszHostname = NULL;
1120 pImage->uPort = 0;
1121 }
1122
1123 /* Locate the port number via the colon separating the hostname from the port. */
1124 if (*pImage->pszTargetAddress)
1125 {
1126 if (*pImage->pszTargetAddress != '[')
1127 {
1128 /* Normal hostname or IPv4 dotted decimal. */
1129 pcszPort = strchr(pImage->pszTargetAddress, ':');
1130 if (pcszPort != NULL)
1131 {
1132 cbHostname = pcszPort - pImage->pszTargetAddress;
1133 pcszPort++;
1134 }
1135 else
1136 cbHostname = strlen(pImage->pszTargetAddress);
1137 }
1138 else
1139 {
1140 /* IPv6 literal address. Contains colons, so skip to closing square bracket. */
1141 pcszPort = strchr(pImage->pszTargetAddress, ']');
1142 if (pcszPort != NULL)
1143 {
1144 pcszPort++;
1145 cbHostname = pcszPort - pImage->pszTargetAddress;
1146 if (*pcszPort == '\0')
1147 pcszPort = NULL;
1148 else if (*pcszPort != ':')
1149 rc = VERR_PARSE_ERROR;
1150 else
1151 pcszPort++;
1152 }
1153 else
1154 rc = VERR_PARSE_ERROR;
1155 }
1156 }
1157 else
1158 rc = VERR_PARSE_ERROR;
1159
1160 /* Now split address into hostname and port. */
1161 if (RT_SUCCESS(rc))
1162 {
1163 pImage->pszHostname = (char *)RTMemAlloc(cbHostname + 1);
1164 if (!pImage->pszHostname)
1165 rc = VERR_NO_MEMORY;
1166 else
1167 {
1168 if (pImage->pszTargetAddress[0] == '[')
1169 memcpy(pImage->pszHostname, pImage->pszTargetAddress + 1, cbHostname);
1170 else
1171 memcpy(pImage->pszHostname, pImage->pszTargetAddress, cbHostname);
1172 pImage->pszHostname[cbHostname] = '\0';
1173 if (pcszPort != NULL)
1174 {
1175 rc = RTStrToUInt16Ex(pcszPort, &pszPortEnd, 0, &uPort);
1176 /* Note that RT_SUCCESS() macro to check the rc value is not strict enough in this case. */
1177 if (rc == VINF_SUCCESS && *pszPortEnd == '\0' && uPort != 0)
1178 {
1179 pImage->uPort = uPort;
1180 }
1181 else
1182 {
1183 rc = VERR_PARSE_ERROR;
1184 }
1185 }
1186 else
1187 pImage->uPort = ISCSI_DEFAULT_PORT;
1188 }
1189 }
1190
1191 if (RT_SUCCESS(rc))
1192 {
1193 if (!iscsiIsClientConnected(pImage))
1194 rc = iscsiTransportConnect(pImage);
1195 }
1196 else
1197 {
1198 if (pImage->pszHostname)
1199 {
1200 RTMemFree(pImage->pszHostname);
1201 pImage->pszHostname = NULL;
1202 }
1203 pImage->uPort = 0;
1204 }
1205
1206 LogFlowFunc(("returns %Rrc\n", rc));
1207 return rc;
1208}
1209
1210
1211/**
1212 * Attach to an iSCSI target. Performs all operations necessary to enter
1213 * Full Feature Phase.
1214 *
1215 * @returns VBox status code.
1216 * @param pImage The iSCSI connection state to be used.
1217 */
1218static DECLCALLBACK(int) iscsiAttach(void *pvUser)
1219{
1220 int rc = VINF_SUCCESS; /* (MSC is used uninitialized) */
1221 uint32_t itt;
1222 uint32_t csg, nsg, substate;
1223 uint64_t isid_tsih;
1224 uint8_t bBuf[4096]; /* Should be large enough even for large authentication values. */
1225 size_t cbBuf;
1226 bool transit;
1227 uint8_t pbChallenge[1024]; /* RFC3720 specifies this as maximum. */
1228 size_t cbChallenge = 0; /* shut up gcc */
1229 uint8_t bChapIdx = 0; /* (MSC is used uninitialized) */
1230 uint8_t aResponse[RTMD5HASHSIZE];
1231 uint32_t cnISCSIReq = 0;
1232 ISCSIREQ aISCSIReq[4];
1233 uint32_t aReqBHS[12];
1234 uint32_t cnISCSIRes = 0;
1235 ISCSIRES aISCSIRes[2];
1236 uint32_t aResBHS[12];
1237 unsigned cRetries = 5;
1238 char *pszNext;
1239 PISCSIIMAGE pImage = (PISCSIIMAGE)pvUser;
1240
1241 bool fParameterNeg = true;;
1242 pImage->cbRecvDataLength = ISCSI_DATA_LENGTH_MAX;
1243 pImage->cbSendDataLength = RT_MIN(ISCSI_DATA_LENGTH_MAX, pImage->cbWriteSplit);
1244 char szMaxDataLength[16];
1245 RTStrPrintf(szMaxDataLength, sizeof(szMaxDataLength), "%u", ISCSI_DATA_LENGTH_MAX);
1246 ISCSIPARAMETER aParameterNeg[] =
1247 {
1248 { "HeaderDigest", "None", 0 },
1249 { "DataDigest", "None", 0 },
1250 { "MaxConnections", "1", 0 },
1251 { "InitialR2T", "No", 0 },
1252 { "ImmediateData", "Yes", 0 },
1253 { "MaxRecvDataSegmentLength", szMaxDataLength, 0 },
1254 { "MaxBurstLength", szMaxDataLength, 0 },
1255 { "FirstBurstLength", szMaxDataLength, 0 },
1256 { "DefaultTime2Wait", "0", 0 },
1257 { "DefaultTime2Retain", "60", 0 },
1258 { "DataPDUInOrder", "Yes", 0 },
1259 { "DataSequenceInOrder", "Yes", 0 },
1260 { "ErrorRecoveryLevel", "0", 0 },
1261 { "MaxOutstandingR2T", "1", 0 }
1262 };
1263
1264 LogFlowFunc(("entering\n"));
1265
1266 Assert(pImage->state == ISCSISTATE_FREE);
1267
1268 /*
1269 * If there were too many logins without any successful I/O just fail
1270 * and assume the target is not working properly.
1271 */
1272 if (ASMAtomicReadU32(&pImage->cLoginsSinceIo) == 3)
1273 return VERR_BROKEN_PIPE;
1274
1275 RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
1276
1277 /* Make 100% sure the connection isn't reused for a new login. */
1278 iscsiTransportClose(pImage);
1279
1280restart:
1281 if (!cRetries)
1282 {
1283 /*
1284 * Prevent the iSCSI initiator to go into normal state if we are here,
1285 * even if there is no error code set.
1286 */
1287 if (RT_SUCCESS(rc))
1288 {
1289 AssertMsgFailed(("Success status code set while out of retries\n"));
1290 rc = VERR_IPE_UNEXPECTED_STATUS;
1291 }
1292 goto out;
1293 }
1294
1295 if (!iscsiIsClientConnected(pImage))
1296 {
1297 rc = iscsiTransportOpen(pImage);
1298 if (RT_FAILURE(rc))
1299 goto out;
1300 }
1301
1302 pImage->state = ISCSISTATE_IN_LOGIN;
1303 pImage->ITT = 1;
1304 pImage->FirstRecvPDU = true;
1305 pImage->CmdSN = 1;
1306 pImage->ExpCmdSN = 0;
1307 pImage->MaxCmdSN = 1;
1308 pImage->ExpStatSN = 0;
1309
1310 /*
1311 * Send login request to target.
1312 */
1313 itt = iscsiNewITT(pImage);
1314 csg = 0;
1315 nsg = 0;
1316 substate = 0;
1317 isid_tsih = pImage->ISID << 16; /* TSIH field currently always 0 */
1318
1319 do {
1320 transit = false;
1321 cbBuf = 0;
1322 /* Handle all cases with a single switch statement. */
1323 switch (csg << 8 | substate)
1324 {
1325 case 0x0000: /* security negotiation, step 0: propose authentication. */
1326 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "SessionType", "Normal", 0);
1327 if (RT_FAILURE(rc))
1328 goto out;
1329 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "InitiatorName", pImage->pszInitiatorName, 0);
1330 if (RT_FAILURE(rc))
1331 goto out;
1332 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "TargetName", pImage->pszTargetName, 0);
1333 if (RT_FAILURE(rc))
1334 goto out;
1335 if (pImage->pszInitiatorUsername == NULL)
1336 {
1337 /* No authentication. Immediately switch to next phase. */
1338 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "AuthMethod", "None", 0);
1339 if (RT_FAILURE(rc))
1340 goto out;
1341 nsg = 1;
1342 transit = true;
1343 }
1344 else
1345 {
1346 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "AuthMethod", "CHAP,None", 0);
1347 if (RT_FAILURE(rc))
1348 goto out;
1349 }
1350 break;
1351 case 0x0001: /* security negotiation, step 1: propose CHAP_MD5 variant. */
1352 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_A", "5", 0);
1353 if (RT_FAILURE(rc))
1354 goto out;
1355 break;
1356 case 0x0002: /* security negotiation, step 2: send authentication info. */
1357 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_N", pImage->pszInitiatorUsername, 0);
1358 if (RT_FAILURE(rc))
1359 goto out;
1360 chap_md5_compute_response(aResponse, bChapIdx, pbChallenge, cbChallenge,
1361 pImage->pbInitiatorSecret, pImage->cbInitiatorSecret);
1362 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_R", (const char *)aResponse, RTMD5HASHSIZE);
1363 if (RT_FAILURE(rc))
1364 goto out;
1365 nsg = 1;
1366 transit = true;
1367 break;
1368 case 0x0100: /* login operational negotiation, step 0: set parameters. */
1369 if (fParameterNeg)
1370 {
1371 for (unsigned i = 0; i < RT_ELEMENTS(aParameterNeg); i++)
1372 {
1373 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf,
1374 aParameterNeg[i].pszParamName,
1375 aParameterNeg[i].pszParamValue,
1376 aParameterNeg[i].cbParamValue);
1377 if (RT_FAILURE(rc))
1378 goto out;
1379 }
1380 fParameterNeg = false;
1381 }
1382
1383 nsg = 3;
1384 transit = true;
1385 break;
1386 case 0x0300: /* full feature phase. */
1387 default:
1388 /* Should never come here. */
1389 AssertMsgFailed(("send: Undefined login state %d substate %d\n", csg, substate));
1390 break;
1391 }
1392
1393 aReqBHS[0] = RT_H2N_U32( ISCSI_IMMEDIATE_DELIVERY_BIT
1394 | (csg << ISCSI_CSG_SHIFT)
1395 | (transit ? (nsg << ISCSI_NSG_SHIFT | ISCSI_TRANSIT_BIT) : 0)
1396 | ISCSI_MY_VERSION /* Minimum version. */
1397 | (ISCSI_MY_VERSION << 8) /* Maximum version. */
1398 | ISCSIOP_LOGIN_REQ); /* C=0 */
1399 aReqBHS[1] = RT_H2N_U32((uint32_t)cbBuf); /* TotalAHSLength=0 */
1400 aReqBHS[2] = RT_H2N_U32(isid_tsih >> 32);
1401 aReqBHS[3] = RT_H2N_U32(isid_tsih & 0xffffffff);
1402 aReqBHS[4] = itt;
1403 aReqBHS[5] = RT_H2N_U32(1 << 16); /* CID=1,reserved */
1404 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
1405 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
1406 aReqBHS[8] = 0; /* reserved */
1407 aReqBHS[9] = 0; /* reserved */
1408 aReqBHS[10] = 0; /* reserved */
1409 aReqBHS[11] = 0; /* reserved */
1410
1411 cnISCSIReq = 0;
1412 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
1413 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
1414 cnISCSIReq++;
1415
1416 aISCSIReq[cnISCSIReq].pcvSeg = bBuf;
1417 aISCSIReq[cnISCSIReq].cbSeg = cbBuf;
1418 cnISCSIReq++;
1419
1420 rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_NO_REATTACH);
1421 if (RT_SUCCESS(rc))
1422 {
1423 ISCSIOPCODE cmd;
1424 ISCSILOGINSTATUSCLASS loginStatusClass;
1425
1426 cnISCSIRes = 0;
1427 aISCSIRes[cnISCSIRes].pvSeg = aResBHS;
1428 aISCSIRes[cnISCSIRes].cbSeg = sizeof(aResBHS);
1429 cnISCSIRes++;
1430 aISCSIRes[cnISCSIRes].pvSeg = bBuf;
1431 aISCSIRes[cnISCSIRes].cbSeg = sizeof(bBuf);
1432 cnISCSIRes++;
1433
1434 rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes, ISCSIPDU_NO_REATTACH);
1435 if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
1436 {
1437 /*
1438 * We lost connection to the target while receiving the answer,
1439 * start from the beginning.
1440 */
1441 cRetries--;
1442 goto restart;
1443 }
1444 else if (RT_FAILURE(rc))
1445 break;
1446 /** @todo collect partial login responses with Continue bit set. */
1447 Assert(aISCSIRes[0].pvSeg == aResBHS);
1448 Assert(aISCSIRes[0].cbSeg >= ISCSI_BHS_SIZE);
1449 Assert((RT_N2H_U32(aResBHS[0]) & ISCSI_CONTINUE_BIT) == 0);
1450
1451 cmd = (ISCSIOPCODE)(RT_N2H_U32(aResBHS[0]) & ISCSIOP_MASK);
1452 if (cmd == ISCSIOP_LOGIN_RES)
1453 {
1454 if ((RT_N2H_U32(aResBHS[0]) & 0xff) != ISCSI_MY_VERSION)
1455 {
1456 iscsiTransportClose(pImage);
1457 rc = VERR_PARSE_ERROR;
1458 break; /* Give up immediately, as a RFC violation in version fields is very serious. */
1459 }
1460
1461 loginStatusClass = (ISCSILOGINSTATUSCLASS)(RT_N2H_U32(aResBHS[9]) >> 24);
1462 switch (loginStatusClass)
1463 {
1464 case ISCSI_LOGIN_STATUS_CLASS_SUCCESS:
1465 uint32_t targetCSG;
1466 uint32_t targetNSG;
1467 bool targetTransit;
1468
1469 if (pImage->FirstRecvPDU)
1470 {
1471 pImage->FirstRecvPDU = false;
1472 pImage->ExpStatSN = RT_N2H_U32(aResBHS[6]) + 1;
1473 }
1474
1475 targetCSG = (RT_N2H_U32(aResBHS[0]) & ISCSI_CSG_MASK) >> ISCSI_CSG_SHIFT;
1476 targetNSG = (RT_N2H_U32(aResBHS[0]) & ISCSI_NSG_MASK) >> ISCSI_NSG_SHIFT;
1477 targetTransit = !!(RT_N2H_U32(aResBHS[0]) & ISCSI_TRANSIT_BIT);
1478
1479 /* Handle all cases with a single switch statement. */
1480 switch (csg << 8 | substate)
1481 {
1482 case 0x0000: /* security negotiation, step 0: receive final authentication. */
1483 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1484 if (RT_FAILURE(rc))
1485 break;
1486
1487 const char *pcszAuthMethod;
1488
1489 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "AuthMethod", &pcszAuthMethod);
1490 if (RT_FAILURE(rc))
1491 {
1492 rc = VERR_PARSE_ERROR;
1493 break;
1494 }
1495 if (strcmp(pcszAuthMethod, "None") == 0)
1496 {
1497 /* Authentication offered, but none required. Skip to operational parameters. */
1498 csg = 1;
1499 nsg = 1;
1500 transit = true;
1501 substate = 0;
1502 break;
1503 }
1504 else if (strcmp(pcszAuthMethod, "CHAP") == 0 && targetNSG == 0 && !targetTransit)
1505 {
1506 /* CHAP authentication required, continue with next substate. */
1507 substate++;
1508 break;
1509 }
1510
1511 /* Unknown auth method or login response PDU headers incorrect. */
1512 rc = VERR_PARSE_ERROR;
1513 break;
1514 case 0x0001: /* security negotiation, step 1: receive final CHAP variant and challenge. */
1515 {
1516 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1517 if (RT_FAILURE(rc))
1518 break;
1519
1520 const char *pcszChapAuthMethod;
1521 const char *pcszChapIdxTarget;
1522 const char *pcszChapChallengeStr;
1523
1524 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_A", &pcszChapAuthMethod);
1525 if (RT_FAILURE(rc))
1526 {
1527 rc = VERR_PARSE_ERROR;
1528 break;
1529 }
1530 if (strcmp(pcszChapAuthMethod, "5") != 0)
1531 {
1532 rc = VERR_PARSE_ERROR;
1533 break;
1534 }
1535 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_I", &pcszChapIdxTarget);
1536 if (RT_FAILURE(rc))
1537 {
1538 rc = VERR_PARSE_ERROR;
1539 break;
1540 }
1541 rc = RTStrToUInt8Ex(pcszChapIdxTarget, &pszNext, 0, &bChapIdx);
1542/** @todo r=bird: Unsafe use of pszNext on failure. The code should probably
1543 * use RTStrToUInt8Full and check for rc != VINF_SUCCESS. */
1544 if (rc > VINF_SUCCESS || *pszNext != '\0')
1545 {
1546 rc = VERR_PARSE_ERROR;
1547 break;
1548 }
1549 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_C", &pcszChapChallengeStr);
1550 if (RT_FAILURE(rc))
1551 {
1552 rc = VERR_PARSE_ERROR;
1553 break;
1554 }
1555 cbChallenge = sizeof(pbChallenge);
1556 rc = iscsiStrToBinary(pcszChapChallengeStr, pbChallenge, &cbChallenge);
1557 if (RT_FAILURE(rc))
1558 break;
1559 substate++;
1560 transit = true;
1561 break;
1562 }
1563 case 0x0002: /* security negotiation, step 2: check authentication success. */
1564 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1565 if (RT_FAILURE(rc))
1566 break;
1567
1568 if (targetCSG == 0 && targetNSG == 1 && targetTransit)
1569 {
1570 /* Target wants to continue in login operational state, authentication success. */
1571 csg = 1;
1572 nsg = 3;
1573 substate = 0;
1574 break;
1575 }
1576 rc = VERR_PARSE_ERROR;
1577 break;
1578 case 0x0100: /* login operational negotiation, step 0: check results. */
1579 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1580 if (RT_FAILURE(rc))
1581 break;
1582
1583 if (targetCSG == 1 && targetNSG == 3 && targetTransit)
1584 {
1585 /* Target wants to continue in full feature phase, login finished. */
1586 csg = 3;
1587 nsg = 3;
1588 substate = 0;
1589 break;
1590 }
1591 else if (targetCSG == 1 && targetNSG == 1 && !targetTransit)
1592 {
1593 /* Target wants to negotiate certain parameters and
1594 * stay in login operational negotiation. */
1595 csg = 1;
1596 nsg = 3;
1597 substate = 0;
1598 }
1599 rc = VERR_PARSE_ERROR;
1600 break;
1601 case 0x0300: /* full feature phase. */
1602 default:
1603 AssertMsgFailed(("recv: Undefined login state %d substate %d\n", csg, substate));
1604 rc = VERR_PARSE_ERROR;
1605 break;
1606 }
1607 break;
1608 case ISCSI_LOGIN_STATUS_CLASS_REDIRECTION:
1609 const char *pcszTargetRedir;
1610
1611 /* Target has moved to some other location, as indicated in the TargetAddress key. */
1612 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "TargetAddress", &pcszTargetRedir);
1613 if (RT_FAILURE(rc))
1614 {
1615 rc = VERR_PARSE_ERROR;
1616 break;
1617 }
1618 if (pImage->pszTargetAddress)
1619 RTMemFree(pImage->pszTargetAddress);
1620 {
1621 size_t cb = strlen(pcszTargetRedir) + 1;
1622 pImage->pszTargetAddress = (char *)RTMemAlloc(cb);
1623 if (!pImage->pszTargetAddress)
1624 {
1625 rc = VERR_NO_MEMORY;
1626 break;
1627 }
1628 memcpy(pImage->pszTargetAddress, pcszTargetRedir, cb);
1629 }
1630 rc = iscsiTransportOpen(pImage);
1631 goto restart;
1632 case ISCSI_LOGIN_STATUS_CLASS_INITIATOR_ERROR:
1633 {
1634 const char *pszDetail = NULL;
1635
1636 switch ((RT_N2H_U32(aResBHS[9]) >> 16) & 0xff)
1637 {
1638 case 0x00:
1639 pszDetail = "Miscelleanous iSCSI intiaitor error";
1640 break;
1641 case 0x01:
1642 pszDetail = "Authentication failure";
1643 break;
1644 case 0x02:
1645 pszDetail = "Authorization failure";
1646 break;
1647 case 0x03:
1648 pszDetail = "Not found";
1649 break;
1650 case 0x04:
1651 pszDetail = "Target removed";
1652 break;
1653 case 0x05:
1654 pszDetail = "Unsupported version";
1655 break;
1656 case 0x06:
1657 pszDetail = "Too many connections";
1658 break;
1659 case 0x07:
1660 pszDetail = "Missing parameter";
1661 break;
1662 case 0x08:
1663 pszDetail = "Can't include in session";
1664 break;
1665 case 0x09:
1666 pszDetail = "Session type not supported";
1667 break;
1668 case 0x0a:
1669 pszDetail = "Session does not exist";
1670 break;
1671 case 0x0b:
1672 pszDetail = "Invalid request type during login";
1673 break;
1674 default:
1675 pszDetail = "Unknown status detail";
1676 }
1677 LogRel(("iSCSI: login to target failed with: %s\n", pszDetail));
1678 iscsiTransportClose(pImage);
1679 rc = VERR_IO_GEN_FAILURE;
1680 goto out;
1681 }
1682 case ISCSI_LOGIN_STATUS_CLASS_TARGET_ERROR:
1683 iscsiTransportClose(pImage);
1684 rc = VINF_EOF;
1685 break;
1686 default:
1687 rc = VERR_PARSE_ERROR;
1688 }
1689
1690 if (csg == 3)
1691 {
1692 /*
1693 * Finished login, continuing with Full Feature Phase.
1694 */
1695 rc = VINF_SUCCESS;
1696 break;
1697 }
1698 }
1699 else
1700 {
1701 AssertMsgFailed(("%s: ignoring unexpected PDU with first word = %#08x\n", __FUNCTION__, RT_N2H_U32(aResBHS[0])));
1702 }
1703 }
1704 else
1705 break;
1706 } while (true);
1707
1708out:
1709 if (RT_FAILURE(rc))
1710 {
1711 /*
1712 * Dump the last request and response of we are supposed to do so and there is a request
1713 * or response.
1714 */
1715 if (cnISCSIReq)
1716 iscsiDumpPacket(pImage, aISCSIReq, cnISCSIReq, VINF_SUCCESS, true /* fRequest */);
1717
1718 if (cnISCSIRes)
1719 iscsiDumpPacket(pImage, (PISCSIREQ)aISCSIRes, cnISCSIRes, rc, false /* fRequest */);
1720
1721 /*
1722 * Close connection to target.
1723 */
1724 iscsiTransportClose(pImage);
1725 pImage->state = ISCSISTATE_FREE;
1726 }
1727 else
1728 pImage->state = ISCSISTATE_NORMAL;
1729
1730 if (RT_SUCCESS(rc))
1731 ASMAtomicIncU32(&pImage->cLoginsSinceIo);
1732
1733 RTSemMutexRelease(pImage->Mutex);
1734
1735 LogFlowFunc(("returning %Rrc\n", rc));
1736 LogRel(("iSCSI: login to target %s %s (%Rrc)\n", pImage->pszTargetName, RT_SUCCESS(rc) ? "successful" : "failed", rc));
1737 return rc;
1738}
1739
1740
1741/**
1742 * Detach from an iSCSI target.
1743 *
1744 * @returns VBox status code.
1745 * @param pImage The iSCSI connection state to be used.
1746 */
1747static DECLCALLBACK(int) iscsiDetach(void *pvUser)
1748{
1749 int rc;
1750 uint32_t itt;
1751 uint32_t cnISCSIReq = 0;
1752 ISCSIREQ aISCSIReq[4];
1753 uint32_t aReqBHS[12];
1754 PISCSIIMAGE pImage = (PISCSIIMAGE)pvUser;
1755
1756 LogFlowFunc(("entering\n"));
1757
1758 RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
1759
1760 if (pImage->state != ISCSISTATE_FREE && pImage->state != ISCSISTATE_IN_LOGOUT)
1761 {
1762 pImage->state = ISCSISTATE_IN_LOGOUT;
1763
1764 /*
1765 * Send logout request to target.
1766 */
1767 itt = iscsiNewITT(pImage);
1768 aReqBHS[0] = RT_H2N_U32(ISCSI_FINAL_BIT | ISCSIOP_LOGOUT_REQ); /* I=0,F=1,Reason=close session */
1769 aReqBHS[1] = RT_H2N_U32(0); /* TotalAHSLength=0,DataSementLength=0 */
1770 aReqBHS[2] = 0; /* reserved */
1771 aReqBHS[3] = 0; /* reserved */
1772 aReqBHS[4] = itt;
1773 aReqBHS[5] = 0; /* reserved */
1774 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
1775 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
1776 aReqBHS[8] = 0; /* reserved */
1777 aReqBHS[9] = 0; /* reserved */
1778 aReqBHS[10] = 0; /* reserved */
1779 aReqBHS[11] = 0; /* reserved */
1780 pImage->CmdSN++;
1781
1782 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
1783 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
1784 cnISCSIReq++;
1785
1786 rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_NO_REATTACH);
1787 if (RT_SUCCESS(rc))
1788 {
1789 /*
1790 * Read logout response from target.
1791 */
1792 ISCSIRES aISCSIRes;
1793 uint32_t aResBHS[12];
1794
1795 aISCSIRes.pvSeg = aResBHS;
1796 aISCSIRes.cbSeg = sizeof(aResBHS);
1797 rc = iscsiRecvPDU(pImage, itt, &aISCSIRes, 1, ISCSIPDU_NO_REATTACH);
1798 if (RT_SUCCESS(rc))
1799 {
1800 if (RT_N2H_U32(aResBHS[0]) != (ISCSI_FINAL_BIT | ISCSIOP_LOGOUT_RES))
1801 AssertMsgFailed(("iSCSI Logout response invalid\n"));
1802 }
1803 else
1804 AssertMsgFailed(("iSCSI Logout response error, rc=%Rrc\n", rc));
1805 }
1806 else
1807 AssertMsgFailed(("Could not send iSCSI Logout request, rc=%Rrc\n", rc));
1808 }
1809
1810 if (pImage->state != ISCSISTATE_FREE)
1811 {
1812 /*
1813 * Close connection to target.
1814 */
1815 rc = iscsiTransportClose(pImage);
1816 if (RT_FAILURE(rc))
1817 AssertMsgFailed(("Could not close connection to target, rc=%Rrc\n", rc));
1818 }
1819
1820 pImage->state = ISCSISTATE_FREE;
1821
1822 RTSemMutexRelease(pImage->Mutex);
1823
1824 LogFlowFunc(("leaving\n"));
1825 LogRel(("iSCSI: logout to target %s\n", pImage->pszTargetName));
1826 return VINF_SUCCESS;
1827}
1828
1829
1830/**
1831 * Perform a command on an iSCSI target. Target must be already in
1832 * Full Feature Phase.
1833 *
1834 * @returns VBox status code.
1835 * @param pImage The iSCSI connection state to be used.
1836 * @param pRequest Command descriptor. Contains all information about
1837 * the command, its transfer directions and pointers
1838 * to the buffer(s) used for transferring data and
1839 * status information.
1840 */
1841static int iscsiCommand(PISCSIIMAGE pImage, PSCSIREQ pRequest)
1842{
1843 int rc;
1844 uint32_t itt;
1845 uint32_t cbData;
1846 uint32_t cnISCSIReq = 0;
1847 ISCSIREQ aISCSIReq[4];
1848 uint32_t aReqBHS[12];
1849
1850 uint32_t *pDst = NULL;
1851 size_t cbBufLength;
1852 uint32_t aStatus[256]; /**< Plenty of buffer for status information. */
1853 uint32_t ExpDataSN = 0;
1854 bool final = false;
1855
1856
1857 LogFlowFunc(("entering, CmdSN=%d\n", pImage->CmdSN));
1858
1859 Assert(pRequest->enmXfer != SCSIXFER_TO_FROM_TARGET); /**< @todo not yet supported, would require AHS. */
1860 Assert(pRequest->cbI2TData <= 0xffffff); /* larger transfers would require R2T support. */
1861 Assert(pRequest->cbCDB <= 16); /* would cause buffer overrun below. */
1862
1863 /* If not in normal state, then the transport connection was dropped. Try
1864 * to reestablish by logging in, the target might be responsive again. */
1865 if (pImage->state == ISCSISTATE_FREE)
1866 rc = iscsiAttach(pImage);
1867
1868 /* If still not in normal state, then the underlying transport connection
1869 * cannot be established. Get out before bad things happen (and make
1870 * sure the caller suspends the VM again). */
1871 if (pImage->state != ISCSISTATE_NORMAL)
1872 {
1873 rc = VERR_NET_CONNECTION_REFUSED;
1874 goto out;
1875 }
1876
1877 /*
1878 * Send SCSI command to target with all I2T data included.
1879 */
1880 cbData = 0;
1881 if (pRequest->enmXfer == SCSIXFER_FROM_TARGET)
1882 cbData = (uint32_t)pRequest->cbT2IData;
1883 else
1884 cbData = (uint32_t)pRequest->cbI2TData;
1885
1886 RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
1887
1888 itt = iscsiNewITT(pImage);
1889 memset(aReqBHS, 0, sizeof(aReqBHS));
1890 aReqBHS[0] = RT_H2N_U32( ISCSI_FINAL_BIT | ISCSI_TASK_ATTR_SIMPLE | ISCSIOP_SCSI_CMD
1891 | (pRequest->enmXfer << 21)); /* I=0,F=1,Attr=Simple */
1892 aReqBHS[1] = RT_H2N_U32(0x00000000 | ((uint32_t)pRequest->cbI2TData & 0xffffff)); /* TotalAHSLength=0 */
1893 aReqBHS[2] = RT_H2N_U32(pImage->LUN >> 32);
1894 aReqBHS[3] = RT_H2N_U32(pImage->LUN & 0xffffffff);
1895 aReqBHS[4] = itt;
1896 aReqBHS[5] = RT_H2N_U32(cbData);
1897 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
1898 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
1899 memcpy(aReqBHS + 8, pRequest->abCDB, pRequest->cbCDB);
1900 pImage->CmdSN++;
1901
1902 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
1903 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
1904 cnISCSIReq++;
1905
1906 if ( pRequest->enmXfer == SCSIXFER_TO_TARGET
1907 || pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET)
1908 {
1909 Assert(pRequest->cI2TSegs == 1);
1910 aISCSIReq[cnISCSIReq].pcvSeg = pRequest->paI2TSegs[0].pvSeg;
1911 aISCSIReq[cnISCSIReq].cbSeg = pRequest->paI2TSegs[0].cbSeg; /* Padding done by transport. */
1912 cnISCSIReq++;
1913 }
1914
1915 rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_DEFAULT);
1916 if (RT_FAILURE(rc))
1917 goto out_release;
1918
1919 /* Place SCSI request in queue. */
1920 pImage->paCurrReq = aISCSIReq;
1921 pImage->cnCurrReq = cnISCSIReq;
1922
1923 /*
1924 * Read SCSI response/data in PDUs from target.
1925 */
1926 if ( pRequest->enmXfer == SCSIXFER_FROM_TARGET
1927 || pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET)
1928 {
1929 Assert(pRequest->cT2ISegs == 1);
1930 pDst = (uint32_t *)pRequest->paT2ISegs[0].pvSeg;
1931 cbBufLength = pRequest->paT2ISegs[0].cbSeg;
1932 }
1933 else
1934 cbBufLength = 0;
1935
1936 do {
1937 uint32_t cnISCSIRes = 0;
1938 ISCSIRES aISCSIRes[4];
1939 uint32_t aResBHS[12];
1940
1941 aISCSIRes[cnISCSIRes].pvSeg = aResBHS;
1942 aISCSIRes[cnISCSIRes].cbSeg = sizeof(aResBHS);
1943 cnISCSIRes++;
1944 if (cbBufLength != 0 &&
1945 ( pRequest->enmXfer == SCSIXFER_FROM_TARGET
1946 || pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET))
1947 {
1948 aISCSIRes[cnISCSIRes].pvSeg = pDst;
1949 aISCSIRes[cnISCSIRes].cbSeg = cbBufLength;
1950 cnISCSIRes++;
1951 }
1952 /* Always reserve space for the status - it's impossible to tell
1953 * beforehand whether this will be the final PDU or not. */
1954 aISCSIRes[cnISCSIRes].pvSeg = aStatus;
1955 aISCSIRes[cnISCSIRes].cbSeg = sizeof(aStatus);
1956 cnISCSIRes++;
1957
1958 rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes, ISCSIPDU_DEFAULT);
1959 if (RT_FAILURE(rc))
1960 break;
1961
1962 final = !!(RT_N2H_U32(aResBHS[0]) & ISCSI_FINAL_BIT);
1963 ISCSIOPCODE cmd = (ISCSIOPCODE)(RT_N2H_U32(aResBHS[0]) & ISCSIOP_MASK);
1964 if (cmd == ISCSIOP_SCSI_RES)
1965 {
1966 /* This is the final PDU which delivers the status (and may be omitted if
1967 * the last Data-In PDU included successful completion status). Note
1968 * that ExpStatSN has been bumped already in iscsiRecvPDU. */
1969 if (!final || ((RT_N2H_U32(aResBHS[0]) & 0x0000ff00) != 0) || (RT_N2H_U32(aResBHS[6]) != pImage->ExpStatSN - 1))
1970 {
1971 /* SCSI Response in the wrong place or with a (target) failure. */
1972 rc = VERR_PARSE_ERROR;
1973 break;
1974 }
1975 /* The following is a bit tricky, as in error situations we may
1976 * get the status only instead of the result data plus optional
1977 * status. Thus the status may have ended up partially in the
1978 * data area. */
1979 pRequest->status = RT_N2H_U32(aResBHS[0]) & 0x000000ff;
1980 cbData = RT_N2H_U32(aResBHS[1]) & 0x00ffffff;
1981 if (cbData >= 2)
1982 {
1983 uint32_t cbStat = RT_N2H_U32(((uint32_t *)aISCSIRes[1].pvSeg)[0]) >> 16;
1984 if (cbStat + 2 > cbData)
1985 {
1986 rc = VERR_BUFFER_OVERFLOW;
1987 break;
1988 }
1989 /* Truncate sense data if it doesn't fit into the buffer. */
1990 pRequest->cbSense = RT_MIN(cbStat, pRequest->cbSense);
1991 memcpy(pRequest->abSense,
1992 ((const char *)aISCSIRes[1].pvSeg) + 2,
1993 RT_MIN(aISCSIRes[1].cbSeg - 2, pRequest->cbSense));
1994 if ( cnISCSIRes > 2 && aISCSIRes[2].cbSeg
1995 && (ssize_t)pRequest->cbSense - aISCSIRes[1].cbSeg + 2 > 0)
1996 {
1997 memcpy((char *)pRequest->abSense + aISCSIRes[1].cbSeg - 2,
1998 aISCSIRes[2].pvSeg,
1999 pRequest->cbSense - aISCSIRes[1].cbSeg + 2);
2000 }
2001 }
2002 else if (cbData == 1)
2003 {
2004 rc = VERR_PARSE_ERROR;
2005 break;
2006 }
2007 else
2008 pRequest->cbSense = 0;
2009 break;
2010 }
2011 else if (cmd == ISCSIOP_SCSI_DATA_IN)
2012 {
2013 /* A Data-In PDU carries some data that needs to be added to the received
2014 * data in response to the command. There may be both partial and complete
2015 * Data-In PDUs, so collect data until the status is included or the status
2016 * is sent in a separate SCSI Result frame (see above). */
2017 if (final && aISCSIRes[2].cbSeg != 0)
2018 {
2019 /* The received PDU is partially stored in the buffer for status.
2020 * Must not happen under normal circumstances and is a target error. */
2021 rc = VERR_BUFFER_OVERFLOW;
2022 break;
2023 }
2024 uint32_t len = RT_N2H_U32(aResBHS[1]) & 0x00ffffff;
2025 pDst = (uint32_t *)((char *)pDst + len);
2026 cbBufLength -= len;
2027 ExpDataSN++;
2028 if (final && (RT_N2H_U32(aResBHS[0]) & ISCSI_STATUS_BIT) != 0)
2029 {
2030 pRequest->status = RT_N2H_U32(aResBHS[0]) & 0x000000ff;
2031 pRequest->cbSense = 0;
2032 break;
2033 }
2034 }
2035 else
2036 {
2037 rc = VERR_PARSE_ERROR;
2038 break;
2039 }
2040 } while (true);
2041
2042 /* Remove SCSI request from queue. */
2043 pImage->paCurrReq = NULL;
2044 pImage->cnCurrReq = 0;
2045
2046out_release:
2047 if (rc == VERR_TIMEOUT)
2048 {
2049 /* Drop connection in case the target plays dead. Much better than
2050 * delaying the next requests until the timed out command actually
2051 * finishes. Also keep in mind that command shouldn't take longer than
2052 * about 30-40 seconds, or the guest will lose its patience. */
2053 iscsiTransportClose(pImage);
2054 pImage->state = ISCSISTATE_FREE;
2055 rc = VERR_BROKEN_PIPE;
2056 }
2057 RTSemMutexRelease(pImage->Mutex);
2058
2059out:
2060 if (RT_SUCCESS(rc))
2061 ASMAtomicWriteU32(&pImage->cLoginsSinceIo, 0);
2062 LogFlowFunc(("returns %Rrc\n", rc));
2063 return rc;
2064}
2065
2066
2067/**
2068 * Generate a new Initiator Task Tag.
2069 *
2070 * @returns Initiator Task Tag.
2071 * @param pImage The iSCSI connection state to be used.
2072 */
2073static uint32_t iscsiNewITT(PISCSIIMAGE pImage)
2074{
2075 uint32_t next_itt;
2076
2077 next_itt = pImage->ITT++;
2078 if (pImage->ITT == ISCSI_TASK_TAG_RSVD)
2079 pImage->ITT = 0;
2080 return RT_H2N_U32(next_itt);
2081}
2082
2083
2084/**
2085 * Send an iSCSI request. The request can consist of several segments, which
2086 * are padded to 4 byte boundaries and concatenated.
2087 *
2088 * @returns VBOX status
2089 * @param pImage The iSCSI connection state to be used.
2090 * @param paReq Pointer to array of iSCSI request sections.
2091 * @param cnReq Number of valid iSCSI request sections in the array.
2092 * @param uFlags Flags controlling the exact send semantics.
2093 */
2094static int iscsiSendPDU(PISCSIIMAGE pImage, PISCSIREQ paReq, uint32_t cnReq,
2095 uint32_t uFlags)
2096{
2097 int rc = VINF_SUCCESS;
2098 /** @todo return VERR_VD_ISCSI_INVALID_STATE in the appropriate situations,
2099 * needs cleaning up of timeout/disconnect handling a bit, as otherwise
2100 * too many incorrect errors are signalled. */
2101 Assert(cnReq >= 1);
2102 Assert(paReq[0].cbSeg >= ISCSI_BHS_SIZE);
2103
2104 for (uint32_t i = 0; i < pImage->cISCSIRetries; i++)
2105 {
2106 rc = iscsiTransportWrite(pImage, paReq, cnReq);
2107 if (RT_SUCCESS(rc))
2108 break;
2109 if ( (uFlags & ISCSIPDU_NO_REATTACH)
2110 || (rc != VERR_BROKEN_PIPE && rc != VERR_NET_CONNECTION_REFUSED))
2111 break;
2112 /* No point in reestablishing the connection for a logout */
2113 if (pImage->state == ISCSISTATE_IN_LOGOUT)
2114 break;
2115 RTThreadSleep(500);
2116 if (pImage->state != ISCSISTATE_IN_LOGIN)
2117 {
2118 /* Attempt to re-login when a connection fails, but only when not
2119 * currently logging in. */
2120 rc = iscsiAttach(pImage);
2121 if (RT_FAILURE(rc))
2122 break;
2123 }
2124 }
2125 return rc;
2126}
2127
2128
2129/**
2130 * Wait for an iSCSI response with a matching Initiator Target Tag. The response is
2131 * split into several segments, as requested by the caller-provided buffer specification.
2132 * Remember that the response can be split into several PDUs by the sender, so make
2133 * sure that all parts are collected and processed appropriately by the caller.
2134 *
2135 * @returns VBOX status
2136 * @param pImage The iSCSI connection state to be used.
2137 * @param paRes Pointer to array of iSCSI response sections.
2138 * @param cnRes Number of valid iSCSI response sections in the array.
2139 * @param fRecvFlags PDU receive flags.
2140 */
2141static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes,
2142 uint32_t fRecvFlags)
2143{
2144 int rc = VINF_SUCCESS;
2145 ISCSIRES aResBuf;
2146
2147 for (uint32_t i = 0; i < pImage->cISCSIRetries; i++)
2148 {
2149 aResBuf.pvSeg = pImage->pvRecvPDUBuf;
2150 aResBuf.cbSeg = pImage->cbRecvPDUBuf;
2151 rc = iscsiTransportRead(pImage, &aResBuf, 1);
2152 if (RT_FAILURE(rc))
2153 {
2154 if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
2155 {
2156 /* No point in reestablishing the connection for a logout */
2157 if (pImage->state == ISCSISTATE_IN_LOGOUT)
2158 break;
2159 /* Connection broken while waiting for a response - wait a while and
2160 * try to restart by re-sending the original request (if any).
2161 * This also handles the connection reestablishment (login etc.). */
2162 RTThreadSleep(500);
2163 if ( pImage->state != ISCSISTATE_IN_LOGIN
2164 && !(fRecvFlags & ISCSIPDU_NO_REATTACH))
2165 {
2166 /* Attempt to re-login when a connection fails, but only when not
2167 * currently logging in. */
2168 rc = iscsiAttach(pImage);
2169 if (RT_FAILURE(rc))
2170 break;
2171
2172 if (pImage->paCurrReq != NULL)
2173 {
2174 rc = iscsiSendPDU(pImage, pImage->paCurrReq, pImage->cnCurrReq, ISCSIPDU_DEFAULT);
2175 if (RT_FAILURE(rc))
2176 break;
2177 }
2178 }
2179 }
2180 else
2181 {
2182 /* Signal other errors (VERR_BUFFER_OVERFLOW etc.) to the caller. */
2183 break;
2184 }
2185 }
2186 else
2187 {
2188 ISCSIOPCODE cmd;
2189 const uint32_t *pcvResSeg = (const uint32_t *)aResBuf.pvSeg;
2190
2191 /* Check whether the received PDU is valid, and update the internal state of
2192 * the iSCSI connection/session. */
2193 rc = iscsiValidatePDU(&aResBuf, 1);
2194 if (RT_FAILURE(rc))
2195 {
2196 iscsiDumpPacket(pImage, (PISCSIREQ)&aResBuf, 1, rc, false /* fRequest */);
2197 continue;
2198 }
2199 cmd = (ISCSIOPCODE)(RT_N2H_U32(pcvResSeg[0]) & ISCSIOP_MASK);
2200 switch (cmd)
2201 {
2202 case ISCSIOP_SCSI_RES:
2203 case ISCSIOP_SCSI_TASKMGMT_RES:
2204 case ISCSIOP_SCSI_DATA_IN:
2205 case ISCSIOP_R2T:
2206 case ISCSIOP_ASYN_MSG:
2207 case ISCSIOP_TEXT_RES:
2208 case ISCSIOP_LOGIN_RES:
2209 case ISCSIOP_LOGOUT_RES:
2210 case ISCSIOP_REJECT:
2211 case ISCSIOP_NOP_IN:
2212 if (serial_number_less(pImage->MaxCmdSN, RT_N2H_U32(pcvResSeg[8])))
2213 pImage->MaxCmdSN = RT_N2H_U32(pcvResSeg[8]);
2214 if (serial_number_less(pImage->ExpCmdSN, RT_N2H_U32(pcvResSeg[7])))
2215 pImage->ExpCmdSN = RT_N2H_U32(pcvResSeg[7]);
2216 break;
2217 default:
2218 rc = VERR_PARSE_ERROR;
2219 iscsiDumpPacket(pImage, (PISCSIREQ)&aResBuf, 1, rc, false /* fRequest */);
2220 }
2221 if (RT_FAILURE(rc))
2222 continue;
2223 if ( !pImage->FirstRecvPDU
2224 && (cmd != ISCSIOP_SCSI_DATA_IN || (RT_N2H_U32(pcvResSeg[0]) & ISCSI_STATUS_BIT))
2225 && ( cmd != ISCSIOP_LOGIN_RES
2226 || (ISCSILOGINSTATUSCLASS)((RT_N2H_U32(pcvResSeg[9]) >> 24) == ISCSI_LOGIN_STATUS_CLASS_SUCCESS)))
2227 {
2228 if (pImage->ExpStatSN == RT_N2H_U32(pcvResSeg[6]))
2229 {
2230 /* StatSN counter is not advanced on R2T and on a target SN update NOP-In. */
2231 if ( (cmd != ISCSIOP_R2T)
2232 && ((cmd != ISCSIOP_NOP_IN) || (RT_N2H_U32(pcvResSeg[4]) != ISCSI_TASK_TAG_RSVD)))
2233 pImage->ExpStatSN++;
2234 }
2235 else
2236 {
2237 rc = VERR_PARSE_ERROR;
2238 iscsiDumpPacket(pImage, (PISCSIREQ)&aResBuf, 1, rc, false /* fRequest */);
2239 continue;
2240 }
2241 }
2242 /* Finally check whether the received PDU matches what the caller wants. */
2243 if ( itt == pcvResSeg[4]
2244 && itt != ISCSI_TASK_TAG_RSVD)
2245 {
2246 /* Copy received PDU (one segment) to caller-provided buffers. */
2247 uint32_t j;
2248 size_t cbSeg;
2249 const uint8_t *pSrc;
2250
2251 pSrc = (const uint8_t *)aResBuf.pvSeg;
2252 cbSeg = aResBuf.cbSeg;
2253 for (j = 0; j < cnRes; j++)
2254 {
2255 if (cbSeg > paRes[j].cbSeg)
2256 {
2257 memcpy(paRes[j].pvSeg, pSrc, paRes[j].cbSeg);
2258 pSrc += paRes[j].cbSeg;
2259 cbSeg -= paRes[j].cbSeg;
2260 }
2261 else
2262 {
2263 memcpy(paRes[j].pvSeg, pSrc, cbSeg);
2264 paRes[j].cbSeg = cbSeg;
2265 cbSeg = 0;
2266 break;
2267 }
2268 }
2269 if (cbSeg != 0)
2270 {
2271 rc = VERR_BUFFER_OVERFLOW;
2272 break;
2273 }
2274 for (j++; j < cnRes; j++)
2275 paRes[j].cbSeg = 0;
2276 break;
2277 }
2278 else if ( cmd == ISCSIOP_NOP_IN
2279 && RT_N2H_U32(pcvResSeg[5]) != ISCSI_TASK_TAG_RSVD)
2280 {
2281 uint32_t cnISCSIReq;
2282 ISCSIREQ aISCSIReq[4];
2283 uint32_t aReqBHS[12];
2284
2285 aReqBHS[0] = RT_H2N_U32(ISCSI_IMMEDIATE_DELIVERY_BIT | ISCSI_FINAL_BIT | ISCSIOP_NOP_OUT);
2286 aReqBHS[1] = RT_H2N_U32(0); /* TotalAHSLength=0,DataSementLength=0 */
2287 aReqBHS[2] = pcvResSeg[2]; /* copy LUN from NOP-In */
2288 aReqBHS[3] = pcvResSeg[3]; /* copy LUN from NOP-In */
2289 aReqBHS[4] = RT_H2N_U32(ISCSI_TASK_TAG_RSVD); /* ITT, reply */
2290 aReqBHS[5] = pcvResSeg[5]; /* copy TTT from NOP-In */
2291 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
2292 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
2293 aReqBHS[8] = 0; /* reserved */
2294 aReqBHS[9] = 0; /* reserved */
2295 aReqBHS[10] = 0; /* reserved */
2296 aReqBHS[11] = 0; /* reserved */
2297
2298 cnISCSIReq = 0;
2299 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
2300 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
2301 cnISCSIReq++;
2302
2303 iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_NO_REATTACH);
2304 /* Break if the caller wanted to process the NOP-in only. */
2305 if (itt == ISCSI_TASK_TAG_RSVD)
2306 break;
2307 }
2308 }
2309 }
2310
2311 LogFlowFunc(("returns rc=%Rrc\n", rc));
2312 return rc;
2313}
2314
2315
2316/**
2317 * Reset the PDU buffer
2318 *
2319 * @param pImage The iSCSI connection state to be used.
2320 */
2321static void iscsiRecvPDUReset(PISCSIIMAGE pImage)
2322{
2323 pImage->cbRecvPDUResidual = ISCSI_BHS_SIZE;
2324 pImage->fRecvPDUBHS = true;
2325 pImage->pbRecvPDUBufCur = (uint8_t *)pImage->pvRecvPDUBuf;
2326}
2327
2328static void iscsiPDUTxAdd(PISCSIIMAGE pImage, PISCSIPDUTX pIScsiPDUTx, bool fFront)
2329{
2330 if (!fFront)
2331 {
2332 /* Insert PDU at the tail of the list. */
2333 if (!pImage->pIScsiPDUTxHead)
2334 pImage->pIScsiPDUTxHead = pIScsiPDUTx;
2335 else
2336 pImage->pIScsiPDUTxTail->pNext = pIScsiPDUTx;
2337 pImage->pIScsiPDUTxTail = pIScsiPDUTx;
2338 }
2339 else
2340 {
2341 /* Insert PDU at the beginning of the list. */
2342 pIScsiPDUTx->pNext = pImage->pIScsiPDUTxHead;
2343 pImage->pIScsiPDUTxHead = pIScsiPDUTx;
2344 if (!pImage->pIScsiPDUTxTail)
2345 pImage->pIScsiPDUTxTail = pIScsiPDUTx;
2346 }
2347}
2348
2349/**
2350 * Receives a PDU in a non blocking way.
2351 *
2352 * @returns VBOX status code.
2353 * @param pImage The iSCSI connection state to be used.
2354 */
2355static int iscsiRecvPDUAsync(PISCSIIMAGE pImage)
2356{
2357 size_t cbActuallyRead = 0;
2358 int rc = VINF_SUCCESS;
2359
2360 LogFlowFunc(("pImage=%#p\n", pImage));
2361
2362 /* Check if we are in the middle of a PDU receive. */
2363 if (pImage->cbRecvPDUResidual == 0)
2364 {
2365 /*
2366 * We are receiving a new PDU, don't read more than the BHS initially
2367 * until we know the real size of the PDU.
2368 */
2369 iscsiRecvPDUReset(pImage);
2370 LogFlow(("Receiving new PDU\n"));
2371 }
2372
2373 rc = pImage->pIfNet->pfnReadNB(pImage->Socket, pImage->pbRecvPDUBufCur,
2374 pImage->cbRecvPDUResidual, &cbActuallyRead);
2375 if (RT_SUCCESS(rc) && cbActuallyRead == 0)
2376 rc = VERR_BROKEN_PIPE;
2377
2378 if (RT_SUCCESS(rc))
2379 {
2380 LogFlow(("Received %zu bytes\n", cbActuallyRead));
2381 pImage->cbRecvPDUResidual -= cbActuallyRead;
2382 pImage->pbRecvPDUBufCur += cbActuallyRead;
2383
2384 /* Check if we received everything we wanted. */
2385 if ( !pImage->cbRecvPDUResidual
2386 && pImage->fRecvPDUBHS)
2387 {
2388 size_t cbAHSLength, cbDataLength;
2389
2390 /* If we were reading the BHS first get the actual PDU size now. */
2391 uint32_t word1 = RT_N2H_U32(((uint32_t *)(pImage->pvRecvPDUBuf))[1]);
2392 cbAHSLength = (word1 & 0xff000000) >> 24;
2393 cbAHSLength = ((cbAHSLength - 1) | 3) + 1; /* Add padding. */
2394 cbDataLength = word1 & 0x00ffffff;
2395 cbDataLength = ((cbDataLength - 1) | 3) + 1; /* Add padding. */
2396 pImage->cbRecvPDUResidual = cbAHSLength + cbDataLength;
2397 pImage->fRecvPDUBHS = false; /* Start receiving the rest of the PDU. */
2398 }
2399
2400 if (!pImage->cbRecvPDUResidual)
2401 {
2402 /* We received the complete PDU with or without any payload now. */
2403 LogFlow(("Received complete PDU\n"));
2404 ISCSIRES aResBuf;
2405 aResBuf.pvSeg = pImage->pvRecvPDUBuf;
2406 aResBuf.cbSeg = pImage->cbRecvPDUBuf;
2407 rc = iscsiRecvPDUProcess(pImage, &aResBuf, 1);
2408 }
2409 }
2410 else
2411 LogFlowFunc(("Reading from the socket returned with rc=%Rrc\n", rc));
2412
2413 return rc;
2414}
2415
2416static int iscsiSendPDUAsync(PISCSIIMAGE pImage)
2417{
2418 size_t cbSent = 0;
2419 int rc = VINF_SUCCESS;
2420
2421 LogFlowFunc(("pImage=%#p\n", pImage));
2422
2423 do
2424 {
2425 /*
2426 * If there is no PDU active, get the first one from the list.
2427 * Check that we are allowed to transfer the PDU by comparing the
2428 * command sequence number and the maximum sequence number allowed by the target.
2429 */
2430 if (!pImage->pIScsiPDUTxCur)
2431 {
2432 if ( !pImage->pIScsiPDUTxHead
2433 || serial_number_greater(pImage->pIScsiPDUTxHead->CmdSN, pImage->MaxCmdSN))
2434 break;
2435
2436 pImage->pIScsiPDUTxCur = pImage->pIScsiPDUTxHead;
2437 pImage->pIScsiPDUTxHead = pImage->pIScsiPDUTxCur->pNext;
2438 if (!pImage->pIScsiPDUTxHead)
2439 pImage->pIScsiPDUTxTail = NULL;
2440 }
2441
2442 /* Send as much as we can. */
2443 rc = pImage->pIfNet->pfnSgWriteNB(pImage->Socket, &pImage->pIScsiPDUTxCur->SgBuf, &cbSent);
2444 LogFlow(("SgWriteNB returned rc=%Rrc cbSent=%zu\n", rc, cbSent));
2445 if (RT_SUCCESS(rc))
2446 {
2447 LogFlow(("Sent %zu bytes for PDU %#p\n", cbSent, pImage->pIScsiPDUTxCur));
2448 pImage->pIScsiPDUTxCur->cbSgLeft -= cbSent;
2449 RTSgBufAdvance(&pImage->pIScsiPDUTxCur->SgBuf, cbSent);
2450 if (!pImage->pIScsiPDUTxCur->cbSgLeft)
2451 {
2452 /* PDU completed, free it and place the command on the waiting for response list. */
2453 if (pImage->pIScsiPDUTxCur->pIScsiCmd)
2454 {
2455 LogFlow(("Sent complete PDU, placing on waiting list\n"));
2456 iscsiCmdInsert(pImage, pImage->pIScsiPDUTxCur->pIScsiCmd);
2457 }
2458 RTMemFree(pImage->pIScsiPDUTxCur);
2459 pImage->pIScsiPDUTxCur = NULL;
2460 }
2461 }
2462 } while ( RT_SUCCESS(rc)
2463 && !pImage->pIScsiPDUTxCur);
2464
2465 if (rc == VERR_TRY_AGAIN)
2466 rc = VINF_SUCCESS;
2467
2468 /* Add the write poll flag if we still have something to send, clear it otherwise. */
2469 if (pImage->pIScsiPDUTxCur)
2470 pImage->fPollEvents |= VD_INTERFACETCPNET_EVT_WRITE;
2471 else
2472 pImage->fPollEvents &= ~VD_INTERFACETCPNET_EVT_WRITE;
2473
2474 LogFlowFunc(("rc=%Rrc pIScsiPDUTxCur=%#p\n", rc, pImage->pIScsiPDUTxCur));
2475 return rc;
2476}
2477
2478/**
2479 * Process a received PDU.
2480 *
2481 * @return VBOX status code.
2482 * @param pImage The iSCSI connection state to be used.
2483 * @param paRes Pointer to the array of iSCSI response sections.
2484 * @param cnRes Number of valid iSCSI response sections in the array.
2485 */
2486static int iscsiRecvPDUProcess(PISCSIIMAGE pImage, PISCSIRES paRes, uint32_t cnRes)
2487{
2488 int rc = VINF_SUCCESS;
2489
2490 LogFlowFunc(("pImage=%#p paRes=%#p cnRes=%u\n", pImage, paRes, cnRes));
2491
2492 /* Validate the PDU first. */
2493 rc = iscsiValidatePDU(paRes, cnRes);
2494 if (RT_SUCCESS(rc))
2495 {
2496 ISCSIOPCODE cmd;
2497 const uint32_t *pcvResSeg = (const uint32_t *)paRes[0].pvSeg;
2498
2499 Assert(paRes[0].cbSeg > 9 * sizeof(uint32_t));
2500
2501 do
2502 {
2503 cmd = (ISCSIOPCODE)(RT_N2H_U32(pcvResSeg[0]) & ISCSIOP_MASK);
2504 switch (cmd)
2505 {
2506 case ISCSIOP_SCSI_RES:
2507 case ISCSIOP_SCSI_TASKMGMT_RES:
2508 case ISCSIOP_SCSI_DATA_IN:
2509 case ISCSIOP_R2T:
2510 case ISCSIOP_ASYN_MSG:
2511 case ISCSIOP_TEXT_RES:
2512 case ISCSIOP_LOGIN_RES:
2513 case ISCSIOP_LOGOUT_RES:
2514 case ISCSIOP_REJECT:
2515 case ISCSIOP_NOP_IN:
2516 if (serial_number_less(pImage->MaxCmdSN, RT_N2H_U32(pcvResSeg[8])))
2517 pImage->MaxCmdSN = RT_N2H_U32(pcvResSeg[8]);
2518 if (serial_number_less(pImage->ExpCmdSN, RT_N2H_U32(pcvResSeg[7])))
2519 pImage->ExpCmdSN = RT_N2H_U32(pcvResSeg[7]);
2520 break;
2521 default:
2522 rc = VERR_PARSE_ERROR;
2523 iscsiDumpPacket(pImage, (PISCSIREQ)paRes, cnRes, rc, false /* fRequest */);
2524 }
2525
2526 if (RT_FAILURE(rc))
2527 break;
2528
2529 if ( !pImage->FirstRecvPDU
2530 && (cmd != ISCSIOP_SCSI_DATA_IN || (RT_N2H_U32(pcvResSeg[0]) & ISCSI_STATUS_BIT)))
2531 {
2532 if (pImage->ExpStatSN == RT_N2H_U32(pcvResSeg[6]))
2533 {
2534 /* StatSN counter is not advanced on R2T and on a target SN update NOP-In. */
2535 if ( (cmd != ISCSIOP_R2T)
2536 && ((cmd != ISCSIOP_NOP_IN) || (RT_N2H_U32(pcvResSeg[4]) != ISCSI_TASK_TAG_RSVD)))
2537 pImage->ExpStatSN++;
2538 }
2539 else
2540 {
2541 rc = VERR_PARSE_ERROR;
2542 iscsiDumpPacket(pImage, (PISCSIREQ)paRes, cnRes, rc, false /* fRequest */);
2543 break;
2544 }
2545 }
2546
2547 if (pcvResSeg[4] != ISCSI_TASK_TAG_RSVD)
2548 {
2549 /*
2550 * This is a response from the target for a request from the initiator.
2551 * Get the request and update its state.
2552 */
2553 rc = iscsiRecvPDUUpdateRequest(pImage, paRes, cnRes);
2554 /* Try to send more PDUs now that we updated the MaxCmdSN field */
2555 if ( RT_SUCCESS(rc)
2556 && !pImage->pIScsiPDUTxCur)
2557 rc = iscsiSendPDUAsync(pImage);
2558 }
2559 else
2560 {
2561 /* This is a target initiated request (we handle only NOP-In request at the moment). */
2562 if ( cmd == ISCSIOP_NOP_IN
2563 && RT_N2H_U32(pcvResSeg[5]) != ISCSI_TASK_TAG_RSVD)
2564 {
2565 PISCSIPDUTX pIScsiPDUTx;
2566 uint32_t cnISCSIReq;
2567 uint32_t *paReqBHS;
2568
2569 LogFlowFunc(("Sending NOP-Out\n"));
2570
2571 /* Allocate a new PDU initialize it and put onto the waiting list. */
2572 pIScsiPDUTx = (PISCSIPDUTX)RTMemAllocZ(sizeof(ISCSIPDUTX));
2573 if (!pIScsiPDUTx)
2574 {
2575 rc = VERR_NO_MEMORY;
2576 break;
2577 }
2578 paReqBHS = &pIScsiPDUTx->aBHS[0];
2579 paReqBHS[0] = RT_H2N_U32(ISCSI_IMMEDIATE_DELIVERY_BIT | ISCSI_FINAL_BIT | ISCSIOP_NOP_OUT);
2580 paReqBHS[1] = RT_H2N_U32(0); /* TotalAHSLength=0,DataSementLength=0 */
2581 paReqBHS[2] = pcvResSeg[2]; /* copy LUN from NOP-In */
2582 paReqBHS[3] = pcvResSeg[3]; /* copy LUN from NOP-In */
2583 paReqBHS[4] = RT_H2N_U32(ISCSI_TASK_TAG_RSVD); /* ITT, reply */
2584 paReqBHS[5] = pcvResSeg[5]; /* copy TTT from NOP-In */
2585 paReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
2586 paReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
2587 paReqBHS[8] = 0; /* reserved */
2588 paReqBHS[9] = 0; /* reserved */
2589 paReqBHS[10] = 0; /* reserved */
2590 paReqBHS[11] = 0; /* reserved */
2591
2592 cnISCSIReq = 0;
2593 pIScsiPDUTx->aISCSIReq[cnISCSIReq].pvSeg = paReqBHS;
2594 pIScsiPDUTx->aISCSIReq[cnISCSIReq].cbSeg = sizeof(pIScsiPDUTx->aBHS);
2595 cnISCSIReq++;
2596 pIScsiPDUTx->cbSgLeft = sizeof(pIScsiPDUTx->aBHS);
2597 RTSgBufInit(&pIScsiPDUTx->SgBuf, pIScsiPDUTx->aISCSIReq, cnISCSIReq);
2598
2599 /*
2600 * Link the PDU to the list.
2601 * Insert at the front of the list to send the response as soon as possible
2602 * to avoid frequent reconnects for a slow connection when there are many PDUs
2603 * waiting.
2604 */
2605 iscsiPDUTxAdd(pImage, pIScsiPDUTx, true /* fFront */);
2606
2607 /* Start transfer of a PDU if there is no one active at the moment. */
2608 if (!pImage->pIScsiPDUTxCur)
2609 rc = iscsiSendPDUAsync(pImage);
2610 }
2611 }
2612 } while (0);
2613 }
2614 else
2615 iscsiDumpPacket(pImage, (PISCSIREQ)paRes, cnRes, rc, false /* fRequest */);
2616
2617 return rc;
2618}
2619
2620/**
2621 * Check the static (not dependent on the connection/session state) validity of an iSCSI response PDU.
2622 *
2623 * @returns VBOX status
2624 * @param paRes Pointer to array of iSCSI response sections.
2625 * @param cnRes Number of valid iSCSI response sections in the array.
2626 */
2627static int iscsiValidatePDU(PISCSIRES paRes, uint32_t cnRes)
2628{
2629 RT_NOREF1(cnRes);
2630 const uint32_t *pcrgResBHS;
2631 uint32_t hw0;
2632 Assert(cnRes >= 1);
2633 Assert(paRes[0].cbSeg >= ISCSI_BHS_SIZE);
2634
2635 LogFlowFunc(("paRes=%#p cnRes=%u\n", paRes, cnRes));
2636
2637 pcrgResBHS = (const uint32_t *)(paRes[0].pvSeg);
2638 hw0 = RT_N2H_U32(pcrgResBHS[0]);
2639 switch (hw0 & ISCSIOP_MASK)
2640 {
2641 case ISCSIOP_NOP_IN:
2642 /* NOP-In responses must not be split into several PDUs nor it may contain
2643 * ping data for target-initiated pings nor may both task tags be valid task tags. */
2644 if ( (hw0 & ISCSI_FINAL_BIT) == 0
2645 || ( RT_N2H_U32(pcrgResBHS[4]) == ISCSI_TASK_TAG_RSVD
2646 && RT_N2H_U32(pcrgResBHS[1]) != 0)
2647 || ( RT_N2H_U32(pcrgResBHS[4]) != ISCSI_TASK_TAG_RSVD
2648 && RT_N2H_U32(pcrgResBHS[5]) != ISCSI_TASK_TAG_RSVD))
2649 return VERR_PARSE_ERROR;
2650 break;
2651 case ISCSIOP_SCSI_RES:
2652 /* SCSI responses must not be split into several PDUs nor must the residual
2653 * bits be contradicting each other nor may the residual bits be set for PDUs
2654 * containing anything else but a completed command response. Underflow
2655 * is no reason for declaring a PDU as invalid, as the target may choose
2656 * to return less data than we assume to get. */
2657 if ( (hw0 & ISCSI_FINAL_BIT) == 0
2658 || ((hw0 & ISCSI_BI_READ_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_BI_READ_RESIDUAL_UNFL_BIT))
2659 || ((hw0 & ISCSI_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_RESIDUAL_UNFL_BIT))
2660 || ( ((hw0 & ISCSI_SCSI_RESPONSE_MASK) == 0)
2661 && ((hw0 & ISCSI_SCSI_STATUS_MASK) == SCSI_STATUS_OK)
2662 && (hw0 & ( ISCSI_BI_READ_RESIDUAL_OVFL_BIT | ISCSI_BI_READ_RESIDUAL_UNFL_BIT
2663 | ISCSI_RESIDUAL_OVFL_BIT))))
2664 return VERR_PARSE_ERROR;
2665 else
2666 LogFlowFunc(("good SCSI response, first word %#08x\n", RT_N2H_U32(pcrgResBHS[0])));
2667 break;
2668 case ISCSIOP_LOGIN_RES:
2669 /* Login responses must not contain contradicting transit and continue bits. */
2670 if ((hw0 & ISCSI_CONTINUE_BIT) && ((hw0 & ISCSI_TRANSIT_BIT) != 0))
2671 return VERR_PARSE_ERROR;
2672 break;
2673 case ISCSIOP_TEXT_RES:
2674 /* Text responses must not contain contradicting final and continue bits nor
2675 * may the final bit be set for PDUs containing a target transfer tag other than
2676 * the reserved transfer tag (and vice versa). */
2677 if ( (((hw0 & ISCSI_CONTINUE_BIT) && (hw0 & ISCSI_FINAL_BIT) != 0))
2678 || (((hw0 & ISCSI_FINAL_BIT) && (RT_N2H_U32(pcrgResBHS[5]) != ISCSI_TASK_TAG_RSVD)))
2679 || (((hw0 & ISCSI_FINAL_BIT) == 0) && (RT_N2H_U32(pcrgResBHS[5]) == ISCSI_TASK_TAG_RSVD)))
2680 return VERR_PARSE_ERROR;
2681 break;
2682 case ISCSIOP_SCSI_DATA_IN:
2683 /* SCSI Data-in responses must not contain contradicting residual bits when
2684 * status bit is set. */
2685 if ((hw0 & ISCSI_STATUS_BIT) && (hw0 & ISCSI_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_RESIDUAL_UNFL_BIT))
2686 return VERR_PARSE_ERROR;
2687 break;
2688 case ISCSIOP_LOGOUT_RES:
2689 /* Logout responses must not have the final bit unset and may not contain any
2690 * data or additional header segments. */
2691 if ( ((hw0 & ISCSI_FINAL_BIT) == 0)
2692 || (RT_N2H_U32(pcrgResBHS[1]) != 0))
2693 return VERR_PARSE_ERROR;
2694 break;
2695 case ISCSIOP_ASYN_MSG:
2696 /* Asynchronous Messages must not have the final bit unset and may not contain
2697 * an initiator task tag. */
2698 if ( ((hw0 & ISCSI_FINAL_BIT) == 0)
2699 || (RT_N2H_U32(pcrgResBHS[4]) != ISCSI_TASK_TAG_RSVD))
2700 return VERR_PARSE_ERROR;
2701 break;
2702 case ISCSIOP_SCSI_TASKMGMT_RES:
2703 case ISCSIOP_R2T:
2704 case ISCSIOP_REJECT:
2705 default:
2706 /* Do some logging, ignore PDU. */
2707 LogFlowFunc(("ignore unhandled PDU, first word %#08x\n", RT_N2H_U32(pcrgResBHS[0])));
2708 return VERR_PARSE_ERROR;
2709 }
2710 /* A target must not send PDUs with MaxCmdSN less than ExpCmdSN-1. */
2711
2712 if (serial_number_less(RT_N2H_U32(pcrgResBHS[8]), RT_N2H_U32(pcrgResBHS[7])-1))
2713 return VERR_PARSE_ERROR;
2714
2715 return VINF_SUCCESS;
2716}
2717
2718
2719/**
2720 * Prepares a PDU to transfer for the given command and adds it to the list.
2721 */
2722static int iscsiPDUTxPrepare(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd)
2723{
2724 int rc = VINF_SUCCESS;
2725 uint32_t *paReqBHS;
2726 size_t cbData = 0;
2727 size_t cbSegs = 0;
2728 PSCSIREQ pScsiReq;
2729 PISCSIPDUTX pIScsiPDU = NULL;
2730
2731 LogFlowFunc(("pImage=%#p pIScsiCmd=%#p\n", pImage, pIScsiCmd));
2732
2733 Assert(pIScsiCmd->enmCmdType == ISCSICMDTYPE_REQ);
2734
2735 pIScsiCmd->Itt = iscsiNewITT(pImage);
2736 pScsiReq = pIScsiCmd->CmdType.ScsiReq.pScsiReq;
2737
2738 if (pScsiReq->cT2ISegs)
2739 RTSgBufInit(&pScsiReq->SgBufT2I, pScsiReq->paT2ISegs, pScsiReq->cT2ISegs);
2740
2741 /*
2742 * Allocate twice as much entries as required for padding (worst case).
2743 * The additional segment is for the BHS.
2744 */
2745 size_t cI2TSegs = 2*(pScsiReq->cI2TSegs + 1);
2746 pIScsiPDU = (PISCSIPDUTX)RTMemAllocZ(RT_OFFSETOF(ISCSIPDUTX, aISCSIReq[cI2TSegs]));
2747 if (!pIScsiPDU)
2748 return VERR_NO_MEMORY;
2749
2750 pIScsiPDU->pIScsiCmd = pIScsiCmd;
2751
2752 if (pScsiReq->enmXfer == SCSIXFER_FROM_TARGET)
2753 cbData = (uint32_t)pScsiReq->cbT2IData;
2754 else
2755 cbData = (uint32_t)pScsiReq->cbI2TData;
2756
2757 paReqBHS = pIScsiPDU->aBHS;
2758
2759 /* Setup the BHS. */
2760 paReqBHS[0] = RT_H2N_U32( ISCSI_FINAL_BIT | ISCSI_TASK_ATTR_SIMPLE | ISCSIOP_SCSI_CMD
2761 | (pScsiReq->enmXfer << 21)); /* I=0,F=1,Attr=Simple */
2762 paReqBHS[1] = RT_H2N_U32(0x00000000 | ((uint32_t)pScsiReq->cbI2TData & 0xffffff)); /* TotalAHSLength=0 */
2763 paReqBHS[2] = RT_H2N_U32(pImage->LUN >> 32);
2764 paReqBHS[3] = RT_H2N_U32(pImage->LUN & 0xffffffff);
2765 paReqBHS[4] = pIScsiCmd->Itt;
2766 paReqBHS[5] = RT_H2N_U32((uint32_t)cbData); Assert((uint32_t)cbData == cbData);
2767 paReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
2768 paReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
2769 memcpy(paReqBHS + 8, pScsiReq->abCDB, pScsiReq->cbCDB);
2770
2771 pIScsiPDU->CmdSN = pImage->CmdSN;
2772 pImage->CmdSN++;
2773
2774 /* Setup the S/G buffers. */
2775 uint32_t cnISCSIReq = 0;
2776 pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg = sizeof(pIScsiPDU->aBHS);
2777 pIScsiPDU->aISCSIReq[cnISCSIReq].pvSeg = pIScsiPDU->aBHS;
2778 cnISCSIReq++;
2779 cbSegs = sizeof(pIScsiPDU->aBHS);
2780 /* Padding is not necessary for the BHS. */
2781
2782 if (pScsiReq->cbI2TData)
2783 {
2784 for (unsigned cSeg = 0; cSeg < pScsiReq->cI2TSegs; cSeg++)
2785 {
2786 Assert(cnISCSIReq < cI2TSegs);
2787 pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg = pScsiReq->paI2TSegs[cSeg].cbSeg;
2788 pIScsiPDU->aISCSIReq[cnISCSIReq].pvSeg = pScsiReq->paI2TSegs[cSeg].pvSeg;
2789 cbSegs += pScsiReq->paI2TSegs[cSeg].cbSeg;
2790 cnISCSIReq++;
2791
2792 /* Add padding if necessary. */
2793 if (pScsiReq->paI2TSegs[cSeg].cbSeg & 3)
2794 {
2795 Assert(cnISCSIReq < cI2TSegs);
2796 pIScsiPDU->aISCSIReq[cnISCSIReq].pvSeg = &pImage->aPadding[0];
2797 pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg = 4 - (pScsiReq->paI2TSegs[cSeg].cbSeg & 3);
2798 cbSegs += pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg;
2799 cnISCSIReq++;
2800 }
2801 }
2802 }
2803
2804 pIScsiPDU->cISCSIReq = cnISCSIReq;
2805 pIScsiPDU->cbSgLeft = cbSegs;
2806 RTSgBufInit(&pIScsiPDU->SgBuf, pIScsiPDU->aISCSIReq, cnISCSIReq);
2807
2808 /* Link the PDU to the list. */
2809 iscsiPDUTxAdd(pImage, pIScsiPDU, false /* fFront */);
2810
2811 /* Start transfer of a PDU if there is no one active at the moment. */
2812 if (!pImage->pIScsiPDUTxCur)
2813 rc = iscsiSendPDUAsync(pImage);
2814
2815 return rc;
2816}
2817
2818
2819/**
2820 * Updates the state of a request from the PDU we received.
2821 *
2822 * @return VBox status code.
2823 * @param pImage iSCSI connection state to use.
2824 * @param paRes Pointer to array of iSCSI response sections.
2825 * @param cnRes Number of valid iSCSI response sections in the array.
2826 */
2827static int iscsiRecvPDUUpdateRequest(PISCSIIMAGE pImage, PISCSIRES paRes, uint32_t cnRes)
2828{
2829 int rc = VINF_SUCCESS;
2830 PISCSICMD pIScsiCmd;
2831 uint32_t *paResBHS;
2832
2833 LogFlowFunc(("pImage=%#p paRes=%#p cnRes=%u\n", pImage, paRes, cnRes));
2834
2835 Assert(cnRes == 1);
2836 Assert(paRes[0].cbSeg >= ISCSI_BHS_SIZE);
2837
2838 paResBHS = (uint32_t *)paRes[0].pvSeg;
2839
2840 pIScsiCmd = iscsiCmdGetFromItt(pImage, paResBHS[4]);
2841
2842 if (pIScsiCmd)
2843 {
2844 bool final = false;
2845 PSCSIREQ pScsiReq;
2846
2847 LogFlow(("Found SCSI command %#p for Itt=%#u\n", pIScsiCmd, paResBHS[4]));
2848
2849 Assert(pIScsiCmd->enmCmdType == ISCSICMDTYPE_REQ);
2850 pScsiReq = pIScsiCmd->CmdType.ScsiReq.pScsiReq;
2851
2852 final = !!(RT_N2H_U32(paResBHS[0]) & ISCSI_FINAL_BIT);
2853 ISCSIOPCODE cmd = (ISCSIOPCODE)(RT_N2H_U32(paResBHS[0]) & ISCSIOP_MASK);
2854 if (cmd == ISCSIOP_SCSI_RES)
2855 {
2856 /* This is the final PDU which delivers the status (and may be omitted if
2857 * the last Data-In PDU included successful completion status). Note
2858 * that ExpStatSN has been bumped already in iscsiRecvPDU. */
2859 if (!final || ((RT_N2H_U32(paResBHS[0]) & 0x0000ff00) != 0) || (RT_N2H_U32(paResBHS[6]) != pImage->ExpStatSN - 1))
2860 {
2861 /* SCSI Response in the wrong place or with a (target) failure. */
2862 LogFlow(("Wrong ExpStatSN value in PDU\n"));
2863 rc = VERR_PARSE_ERROR;
2864 }
2865 else
2866 {
2867 pScsiReq->status = RT_N2H_U32(paResBHS[0]) & 0x000000ff;
2868 size_t cbData = RT_N2H_U32(paResBHS[1]) & 0x00ffffff;
2869 void *pvSense = (uint8_t *)paRes[0].pvSeg + ISCSI_BHS_SIZE;
2870
2871 if (cbData >= 2)
2872 {
2873 uint32_t cbStat = RT_N2H_U32(((uint32_t *)pvSense)[0]) >> 16;
2874 if (cbStat + 2 > cbData)
2875 {
2876 rc = VERR_BUFFER_OVERFLOW;
2877 }
2878 else
2879 {
2880 /* Truncate sense data if it doesn't fit into the buffer. */
2881 pScsiReq->cbSense = RT_MIN(cbStat, pScsiReq->cbSense);
2882 memcpy(pScsiReq->abSense, (uint8_t *)pvSense + 2,
2883 RT_MIN(paRes[0].cbSeg - ISCSI_BHS_SIZE - 2, pScsiReq->cbSense));
2884 }
2885 }
2886 else if (cbData == 1)
2887 rc = VERR_PARSE_ERROR;
2888 else
2889 pScsiReq->cbSense = 0;
2890 }
2891 iscsiCmdComplete(pImage, pIScsiCmd, rc);
2892 }
2893 else if (cmd == ISCSIOP_SCSI_DATA_IN)
2894 {
2895 /* A Data-In PDU carries some data that needs to be added to the received
2896 * data in response to the command. There may be both partial and complete
2897 * Data-In PDUs, so collect data until the status is included or the status
2898 * is sent in a separate SCSI Result frame (see above). */
2899 size_t cbData = RT_N2H_U32(paResBHS[1]) & 0x00ffffff;
2900 void *pvData = (uint8_t *)paRes[0].pvSeg + ISCSI_BHS_SIZE;
2901
2902 if (final && cbData > pScsiReq->cbT2IData)
2903 {
2904 /* The received PDU is bigger than what we requested.
2905 * Must not happen under normal circumstances and is a target error. */
2906 rc = VERR_BUFFER_OVERFLOW;
2907 }
2908 else
2909 {
2910 /* Copy data from the received PDU into the T2I segments. */
2911 size_t cbCopied = RTSgBufCopyFromBuf(&pScsiReq->SgBufT2I, pvData, cbData);
2912 Assert(cbCopied == cbData); NOREF(cbCopied);
2913
2914 if (final && (RT_N2H_U32(paResBHS[0]) & ISCSI_STATUS_BIT) != 0)
2915 {
2916 pScsiReq->status = RT_N2H_U32(paResBHS[0]) & 0x000000ff;
2917 pScsiReq->cbSense = 0;
2918 iscsiCmdComplete(pImage, pIScsiCmd, VINF_SUCCESS);
2919 }
2920 }
2921 }
2922 else
2923 rc = VERR_PARSE_ERROR;
2924 }
2925
2926 /* Log any errors here but ignore the PDU. */
2927 if (RT_FAILURE(rc))
2928 {
2929 LogRel(("iSCSI: Received malformed PDU from target %s (rc=%Rrc), ignoring\n", pImage->pszTargetName, rc));
2930 iscsiDumpPacket(pImage, (PISCSIREQ)paRes, cnRes, rc, false /* fRequest */);
2931 rc = VINF_SUCCESS;
2932 }
2933
2934 return rc;
2935}
2936
2937/**
2938 * Appends a key-value pair to the buffer. Normal ASCII strings (cbValue == 0) and large binary values
2939 * of a given length (cbValue > 0) are directly supported. Other value types must be converted to ASCII
2940 * by the caller. Strings must be in UTF-8 encoding.
2941 *
2942 * @returns VBOX status
2943 * @param pbBuf Pointer to the key-value buffer.
2944 * @param cbBuf Length of the key-value buffer.
2945 * @param pcbBufCurr Currently used portion of the key-value buffer.
2946 * @param pszKey Pointer to a string containing the key.
2947 * @param pszValue Pointer to either a string containing the value or to a large binary value.
2948 * @param cbValue Length of the binary value if applicable.
2949 */
2950static int iscsiTextAddKeyValue(uint8_t *pbBuf, size_t cbBuf, size_t *pcbBufCurr, const char *pcszKey,
2951 const char *pcszValue, size_t cbValue)
2952{
2953 size_t cbBufTmp = *pcbBufCurr;
2954 size_t cbKey = strlen(pcszKey);
2955 size_t cbValueEnc;
2956 uint8_t *pbCurr;
2957
2958 if (cbValue == 0)
2959 cbValueEnc = strlen(pcszValue);
2960 else
2961 cbValueEnc = cbValue * 2 + 2; /* 2 hex bytes per byte, 2 bytes prefix */
2962
2963 if (cbBuf < cbBufTmp + cbKey + 1 + cbValueEnc + 1)
2964 {
2965 /* Buffer would overflow, signal error. */
2966 return VERR_BUFFER_OVERFLOW;
2967 }
2968
2969 /*
2970 * Append a key=value pair (zero terminated string) to the end of the buffer.
2971 */
2972 pbCurr = pbBuf + cbBufTmp;
2973 memcpy(pbCurr, pcszKey, cbKey);
2974 pbCurr += cbKey;
2975 *pbCurr++ = '=';
2976 if (cbValue == 0)
2977 {
2978 memcpy(pbCurr, pcszValue, cbValueEnc);
2979 pbCurr += cbValueEnc;
2980 }
2981 else
2982 {
2983 *pbCurr++ = '0';
2984 *pbCurr++ = 'x';
2985 for (uint32_t i = 0; i < cbValue; i++)
2986 {
2987 uint8_t b;
2988 b = pcszValue[i];
2989 *pbCurr++ = NUM_2_HEX(b >> 4);
2990 *pbCurr++ = NUM_2_HEX(b & 0xf);
2991 }
2992 }
2993 *pbCurr = '\0';
2994 *pcbBufCurr = cbBufTmp + cbKey + 1 + cbValueEnc + 1;
2995
2996 return VINF_SUCCESS;
2997}
2998
2999
3000/**
3001 * Retrieve the value for a given key from the key=value buffer.
3002 *
3003 * @returns VBox status code.
3004 * @param pbBuf Buffer containing key=value pairs.
3005 * @param cbBuf Length of buffer with key=value pairs.
3006 * @param pszKey Pointer to key for which to retrieve the value.
3007 * @param ppszValue Pointer to value string pointer.
3008 */
3009static int iscsiTextGetKeyValue(const uint8_t *pbBuf, size_t cbBuf, const char *pcszKey, const char **ppcszValue)
3010{
3011 size_t cbKey = strlen(pcszKey);
3012
3013 while (cbBuf != 0)
3014 {
3015 size_t cbKeyValNull = strlen((const char *)pbBuf) + 1;
3016
3017 if (strncmp(pcszKey, (const char *)pbBuf, cbKey) == 0 && pbBuf[cbKey] == '=')
3018 {
3019 *ppcszValue = (const char *)(pbBuf + cbKey + 1);
3020 return VINF_SUCCESS;
3021 }
3022 pbBuf += cbKeyValNull;
3023 cbBuf -= cbKeyValNull;
3024 }
3025 return VERR_INVALID_NAME;
3026}
3027
3028
3029/**
3030 * Convert a long-binary value from a value string to the binary representation.
3031 *
3032 * @returns VBOX status
3033 * @param pszValue Pointer to a string containing the textual value representation.
3034 * @param pbValue Pointer to the value buffer for the binary value.
3035 * @param pcbValue In: length of value buffer, out: actual length of binary value.
3036 */
3037static int iscsiStrToBinary(const char *pcszValue, uint8_t *pbValue, size_t *pcbValue)
3038{
3039 size_t cbValue = *pcbValue;
3040 char c1, c2, c3, c4;
3041 Assert(cbValue >= 1);
3042
3043 if (strlen(pcszValue) < 3)
3044 return VERR_PARSE_ERROR;
3045 if (*pcszValue++ != '0')
3046 return VERR_PARSE_ERROR;
3047 switch (*pcszValue++)
3048 {
3049 case 'x':
3050 case 'X':
3051 if (strlen(pcszValue) & 1)
3052 {
3053 c1 = *pcszValue++;
3054 *pbValue++ = HEX_2_NUM(c1);
3055 cbValue--;
3056 }
3057 while (*pcszValue != '\0')
3058 {
3059 if (cbValue == 0)
3060 return VERR_BUFFER_OVERFLOW;
3061 c1 = *pcszValue++;
3062 if ((c1 < '0' || c1 > '9') && (c1 < 'a' || c1 > 'f') && (c1 < 'A' || c1 > 'F'))
3063 return VERR_PARSE_ERROR;
3064 c2 = *pcszValue++;
3065 if ((c2 < '0' || c2 > '9') && (c2 < 'a' || c2 > 'f') && (c2 < 'A' || c2 > 'F'))
3066 return VERR_PARSE_ERROR;
3067 *pbValue++ = (HEX_2_NUM(c1) << 4) | HEX_2_NUM(c2);
3068 cbValue--;
3069 }
3070 *pcbValue -= cbValue;
3071 break;
3072 case 'b':
3073 case 'B':
3074 if ((strlen(pcszValue) & 3) != 0)
3075 return VERR_PARSE_ERROR;
3076 while (*pcszValue != '\0')
3077 {
3078 uint32_t temp;
3079 if (cbValue == 0)
3080 return VERR_BUFFER_OVERFLOW;
3081 c1 = *pcszValue++;
3082 if ((c1 < 'A' || c1 > 'Z') && (c1 < 'a' || c1 >'z') && (c1 < '0' || c1 > '9') && (c1 != '+') && (c1 != '/'))
3083 return VERR_PARSE_ERROR;
3084 c2 = *pcszValue++;
3085 if ((c2 < 'A' || c2 > 'Z') && (c2 < 'a' || c2 >'z') && (c2 < '0' || c2 > '9') && (c2 != '+') && (c2 != '/'))
3086 return VERR_PARSE_ERROR;
3087 c3 = *pcszValue++;
3088 if ((c3 < 'A' || c3 > 'Z') && (c3 < 'a' || c3 >'z') && (c3 < '0' || c3 > '9') && (c3 != '+') && (c3 != '/') && (c3 != '='))
3089 return VERR_PARSE_ERROR;
3090 c4 = *pcszValue++;
3091 if ( (c3 == '=' && c4 != '=')
3092 || ((c4 < 'A' || c4 > 'Z') && (c4 < 'a' || c4 >'z') && (c4 < '0' || c4 > '9') && (c4 != '+') && (c4 != '/') && (c4 != '=')))
3093 return VERR_PARSE_ERROR;
3094 temp = (B64_2_NUM(c1) << 18) | (B64_2_NUM(c2) << 12);
3095 if (c3 == '=') {
3096 if (*pcszValue != '\0')
3097 return VERR_PARSE_ERROR;
3098 *pbValue++ = temp >> 16;
3099 cbValue--;
3100 } else {
3101 temp |= B64_2_NUM(c3) << 6;
3102 if (c4 == '=') {
3103 if (*pcszValue != '\0')
3104 return VERR_PARSE_ERROR;
3105 if (cbValue < 2)
3106 return VERR_BUFFER_OVERFLOW;
3107 *pbValue++ = temp >> 16;
3108 *pbValue++ = (temp >> 8) & 0xff;
3109 cbValue -= 2;
3110 }
3111 else
3112 {
3113 temp |= B64_2_NUM(c4);
3114 if (cbValue < 3)
3115 return VERR_BUFFER_OVERFLOW;
3116 *pbValue++ = temp >> 16;
3117 *pbValue++ = (temp >> 8) & 0xff;
3118 *pbValue++ = temp & 0xff;
3119 cbValue -= 3;
3120 }
3121 }
3122 }
3123 *pcbValue -= cbValue;
3124 break;
3125 default:
3126 return VERR_PARSE_ERROR;
3127 }
3128 return VINF_SUCCESS;
3129}
3130
3131
3132/**
3133 * Retrieve the relevant parameter values and update the initiator state.
3134 *
3135 * @returns VBox status code.
3136 * @param pImage Current iSCSI initiator state.
3137 * @param pbBuf Buffer containing key=value pairs.
3138 * @param cbBuf Length of buffer with key=value pairs.
3139 */
3140static int iscsiUpdateParameters(PISCSIIMAGE pImage, const uint8_t *pbBuf, size_t cbBuf)
3141{
3142 int rc;
3143 const char *pcszMaxRecvDataSegmentLength = NULL;
3144 const char *pcszMaxBurstLength = NULL;
3145 const char *pcszFirstBurstLength = NULL;
3146 rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "MaxRecvDataSegmentLength", &pcszMaxRecvDataSegmentLength);
3147 if (rc == VERR_INVALID_NAME)
3148 rc = VINF_SUCCESS;
3149 if (RT_FAILURE(rc))
3150 return VERR_PARSE_ERROR;
3151 rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "MaxBurstLength", &pcszMaxBurstLength);
3152 if (rc == VERR_INVALID_NAME)
3153 rc = VINF_SUCCESS;
3154 if (RT_FAILURE(rc))
3155 return VERR_PARSE_ERROR;
3156 rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "FirstBurstLength", &pcszFirstBurstLength);
3157 if (rc == VERR_INVALID_NAME)
3158 rc = VINF_SUCCESS;
3159 if (RT_FAILURE(rc))
3160 return VERR_PARSE_ERROR;
3161 if (pcszMaxRecvDataSegmentLength)
3162 {
3163 uint32_t cb = pImage->cbSendDataLength;
3164 rc = RTStrToUInt32Full(pcszMaxRecvDataSegmentLength, 0, &cb);
3165 AssertRC(rc);
3166 pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
3167 }
3168 if (pcszMaxBurstLength)
3169 {
3170 uint32_t cb = pImage->cbSendDataLength;
3171 rc = RTStrToUInt32Full(pcszMaxBurstLength, 0, &cb);
3172 AssertRC(rc);
3173 pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
3174 }
3175 if (pcszFirstBurstLength)
3176 {
3177 uint32_t cb = pImage->cbSendDataLength;
3178 rc = RTStrToUInt32Full(pcszFirstBurstLength, 0, &cb);
3179 AssertRC(rc);
3180 pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
3181 }
3182 return VINF_SUCCESS;
3183}
3184
3185
3186static bool serial_number_less(uint32_t s1, uint32_t s2)
3187{
3188 return (s1 < s2 && s2 - s1 < 0x80000000) || (s1 > s2 && s1 - s2 > 0x80000000);
3189}
3190
3191static bool serial_number_greater(uint32_t s1, uint32_t s2)
3192{
3193 return (s1 < s2 && s2 - s1 > 0x80000000) || (s1 > s2 && s1 - s2 < 0x80000000);
3194}
3195
3196
3197#ifdef IMPLEMENT_TARGET_AUTH
3198static void chap_md5_generate_challenge(uint8_t *pbChallenge, size_t *pcbChallenge)
3199{
3200 uint8_t cbChallenge;
3201
3202 cbChallenge = RTrand_U8(CHAP_MD5_CHALLENGE_MIN, CHAP_MD5_CHALLENGE_MAX);
3203 RTrand_bytes(pbChallenge, cbChallenge);
3204 *pcbChallenge = cbChallenge;
3205}
3206#endif
3207
3208
3209static void chap_md5_compute_response(uint8_t *pbResponse, uint8_t id, const uint8_t *pbChallenge, size_t cbChallenge,
3210 const uint8_t *pbSecret, size_t cbSecret)
3211{
3212 RTMD5CONTEXT ctx;
3213 uint8_t bId;
3214
3215 bId = id;
3216 RTMd5Init(&ctx);
3217 RTMd5Update(&ctx, &bId, 1);
3218 RTMd5Update(&ctx, pbSecret, cbSecret);
3219 RTMd5Update(&ctx, pbChallenge, cbChallenge);
3220 RTMd5Final(pbResponse, &ctx);
3221}
3222
3223/**
3224 * Internal. - Wrapper around the extended select callback of the net interface.
3225 */
3226DECLINLINE(int) iscsiIoThreadWait(PISCSIIMAGE pImage, RTMSINTERVAL cMillies, uint32_t fEvents, uint32_t *pfEvents)
3227{
3228 return pImage->pIfNet->pfnSelectOneEx(pImage->Socket, fEvents, pfEvents, cMillies);
3229}
3230
3231/**
3232 * Internal. - Pokes a thread waiting for I/O.
3233 */
3234DECLINLINE(int) iscsiIoThreadPoke(PISCSIIMAGE pImage)
3235{
3236 return pImage->pIfNet->pfnPoke(pImage->Socket);
3237}
3238
3239/**
3240 * Internal. - Get the next request from the queue.
3241 */
3242DECLINLINE(PISCSICMD) iscsiCmdGet(PISCSIIMAGE pImage)
3243{
3244 int rc;
3245 PISCSICMD pIScsiCmd = NULL;
3246
3247 rc = RTSemMutexRequest(pImage->MutexReqQueue, RT_INDEFINITE_WAIT);
3248 AssertRC(rc);
3249
3250 pIScsiCmd = pImage->pScsiReqQueue;
3251 if (pIScsiCmd)
3252 {
3253 pImage->pScsiReqQueue = pIScsiCmd->pNext;
3254 pIScsiCmd->pNext = NULL;
3255 }
3256
3257 rc = RTSemMutexRelease(pImage->MutexReqQueue);
3258 AssertRC(rc);
3259
3260 return pIScsiCmd;
3261}
3262
3263
3264/**
3265 * Internal. - Adds the given command to the queue.
3266 */
3267DECLINLINE(int) iscsiCmdPut(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd)
3268{
3269 int rc = RTSemMutexRequest(pImage->MutexReqQueue, RT_INDEFINITE_WAIT);
3270 AssertRC(rc);
3271
3272 pIScsiCmd->pNext = pImage->pScsiReqQueue;
3273 pImage->pScsiReqQueue = pIScsiCmd;
3274
3275 rc = RTSemMutexRelease(pImage->MutexReqQueue);
3276 AssertRC(rc);
3277
3278 iscsiIoThreadPoke(pImage);
3279
3280 return rc;
3281}
3282
3283/**
3284 * Internal. - Completes the request with the appropriate action.
3285 * Synchronous requests are completed with waking up the thread
3286 * and asynchronous ones by continuing the associated I/O context.
3287 */
3288static void iscsiCmdComplete(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd, int rcCmd)
3289{
3290 LogFlowFunc(("pImage=%#p pIScsiCmd=%#p rcCmd=%Rrc\n", pImage, pIScsiCmd, rcCmd));
3291
3292 /* Remove from the table first. */
3293 iscsiCmdRemove(pImage, pIScsiCmd->Itt);
3294
3295 /* Call completion callback. */
3296 pIScsiCmd->pfnComplete(pImage, rcCmd, pIScsiCmd->pvUser);
3297
3298 /* Free command structure. */
3299#ifdef DEBUG
3300 memset(pIScsiCmd, 0xff, sizeof(ISCSICMD));
3301#endif
3302 RTMemFree(pIScsiCmd);
3303}
3304
3305/**
3306 * Clears all RX/TX PDU states and returns the command for the current
3307 * pending TX PDU if existing.
3308 *
3309 * @returns Pointer to the iSCSI command for the current PDU transmitted or NULL
3310 * if none is waiting.
3311 * @param pImage iSCSI connection state.
3312 */
3313static PISCSICMD iscsiPDURxTxClear(PISCSIIMAGE pImage)
3314{
3315 PISCSICMD pIScsiCmdHead = NULL;
3316 PISCSIPDUTX pIScsiPDUTx = NULL;
3317
3318 /* Reset PDU we are receiving. */
3319 iscsiRecvPDUReset(pImage);
3320
3321 /*
3322 * Abort all PDUs we are about to transmit,
3323 * the command need a new Itt if the relogin is successful.
3324 */
3325 while (pImage->pIScsiPDUTxHead)
3326 {
3327 pIScsiPDUTx = pImage->pIScsiPDUTxHead;
3328 pImage->pIScsiPDUTxHead = pIScsiPDUTx->pNext;
3329
3330 PISCSICMD pIScsiCmd = pIScsiPDUTx->pIScsiCmd;
3331 if (pIScsiCmd)
3332 {
3333 /* Place on command list. */
3334 pIScsiCmd->pNext = pIScsiCmdHead;
3335 pIScsiCmdHead = pIScsiCmd;
3336 }
3337 RTMemFree(pIScsiPDUTx);
3338 }
3339
3340 /* Clear the tail pointer (safety precaution). */
3341 pImage->pIScsiPDUTxTail = NULL;
3342
3343 /* Clear the current PDU too. */
3344 if (pImage->pIScsiPDUTxCur)
3345 {
3346 pIScsiPDUTx = pImage->pIScsiPDUTxCur;
3347
3348 pImage->pIScsiPDUTxCur = NULL;
3349 PISCSICMD pIScsiCmd = pIScsiPDUTx->pIScsiCmd;
3350 if (pIScsiCmd)
3351 {
3352 pIScsiCmd->pNext = pIScsiCmdHead;
3353 pIScsiCmdHead = pIScsiCmd;
3354 }
3355 RTMemFree(pIScsiPDUTx);
3356 }
3357
3358 return pIScsiCmdHead;
3359}
3360
3361/**
3362 * Rests the iSCSI connection state and returns a list of iSCSI commands pending
3363 * when this was called.
3364 *
3365 * @returns Pointer to the head of the pending iSCSI command list.
3366 * @param pImage iSCSI connection state.
3367 */
3368static PISCSICMD iscsiReset(PISCSIIMAGE pImage)
3369{
3370 PISCSICMD pIScsiCmdHead = NULL;
3371 PISCSICMD pIScsiCmdCur = NULL;
3372
3373 /* Clear all in flight PDUs. */
3374 pIScsiCmdHead = iscsiPDURxTxClear(pImage);
3375
3376 /*
3377 * Get all commands which are waiting for a response
3378 * They need to be resend too after a successful reconnect.
3379 */
3380 PISCSICMD pIScsiCmd = iscsiCmdRemoveAll(pImage);
3381 if (pIScsiCmd)
3382 {
3383 pIScsiCmdCur = pIScsiCmd;
3384 while (pIScsiCmdCur->pNext)
3385 pIScsiCmdCur = pIScsiCmdCur->pNext;
3386
3387 /*
3388 * Place them in front of the list because they are the oldest requests
3389 * and need to be processed first to minimize the risk to time out.
3390 */
3391 pIScsiCmdCur->pNext = pIScsiCmdHead;
3392 pIScsiCmdHead = pIScsiCmd;
3393 }
3394
3395 return pIScsiCmdHead;
3396}
3397
3398/**
3399 * Reattaches the to the target after an error aborting
3400 * pending commands and resending them.
3401 *
3402 * @param pImage iSCSI connection state.
3403 */
3404static void iscsiReattach(PISCSIIMAGE pImage)
3405{
3406 /* Close connection. */
3407 iscsiTransportClose(pImage);
3408 pImage->state = ISCSISTATE_FREE;
3409
3410 /* Reset the state and get the currently pending commands. */
3411 PISCSICMD pIScsiCmdHead = iscsiReset(pImage);
3412
3413 /* Try to attach. */
3414 int rc = iscsiAttach(pImage);
3415 if (RT_SUCCESS(rc))
3416 {
3417 /* Phew, we have a connection again.
3418 * Prepare new PDUs for the aborted commands.
3419 */
3420 while (pIScsiCmdHead)
3421 {
3422 PISCSICMD pIScsiCmd = pIScsiCmdHead;
3423 pIScsiCmdHead = pIScsiCmdHead->pNext;
3424
3425 pIScsiCmd->pNext = NULL;
3426
3427 rc = iscsiPDUTxPrepare(pImage, pIScsiCmd);
3428 if (RT_FAILURE(rc))
3429 break;
3430 }
3431
3432 if (RT_FAILURE(rc))
3433 {
3434 /* Another error, just give up and report an error. */
3435 PISCSICMD pIScsiCmd = iscsiReset(pImage);
3436
3437 /* Concatenate both lists together so we can abort all requests below. */
3438 if (pIScsiCmd)
3439 {
3440 PISCSICMD pIScsiCmdCur = pIScsiCmd;
3441 while (pIScsiCmdCur->pNext)
3442 pIScsiCmdCur = pIScsiCmdCur->pNext;
3443
3444 pIScsiCmdCur->pNext = pIScsiCmdHead;
3445 pIScsiCmdHead = pIScsiCmd;
3446 }
3447 }
3448 }
3449
3450 if (RT_FAILURE(rc))
3451 {
3452 /*
3453 * Still no luck, complete commands with error so the caller
3454 * has a chance to inform the user and maybe resend the command.
3455 */
3456 while (pIScsiCmdHead)
3457 {
3458 PISCSICMD pIScsiCmd = pIScsiCmdHead;
3459 pIScsiCmdHead = pIScsiCmdHead->pNext;
3460
3461 iscsiCmdComplete(pImage, pIScsiCmd, VERR_BROKEN_PIPE);
3462 }
3463 }
3464}
3465
3466/**
3467 * Internal. Main iSCSI I/O worker.
3468 */
3469static DECLCALLBACK(int) iscsiIoThreadWorker(RTTHREAD hThreadSelf, void *pvUser)
3470{
3471 RT_NOREF1(hThreadSelf);
3472 PISCSIIMAGE pImage = (PISCSIIMAGE)pvUser;
3473
3474 /* Initialize the initial event mask. */
3475 pImage->fPollEvents = VD_INTERFACETCPNET_EVT_READ | VD_INTERFACETCPNET_EVT_ERROR;
3476
3477 while (pImage->fRunning)
3478 {
3479 uint32_t fEvents;
3480 int rc;
3481
3482 fEvents = 0;
3483
3484 /* Wait for work or for data from the target. */
3485 RTMSINTERVAL msWait;
3486
3487 if (pImage->cCmdsWaiting)
3488 {
3489 pImage->fPollEvents &= ~VD_INTERFACETCPNET_HINT_INTERRUPT;
3490 msWait = pImage->uReadTimeout;
3491 }
3492 else
3493 {
3494 pImage->fPollEvents |= VD_INTERFACETCPNET_HINT_INTERRUPT;
3495 msWait = RT_INDEFINITE_WAIT;
3496 }
3497
3498 LogFlow(("Waiting for events fPollEvents=%#x\n", pImage->fPollEvents));
3499 rc = iscsiIoThreadWait(pImage, msWait, pImage->fPollEvents, &fEvents);
3500 if (rc == VERR_INTERRUPTED)
3501 {
3502 /* Check the queue. */
3503 PISCSICMD pIScsiCmd = iscsiCmdGet(pImage);
3504
3505 while (pIScsiCmd)
3506 {
3507 switch (pIScsiCmd->enmCmdType)
3508 {
3509 case ISCSICMDTYPE_REQ:
3510 {
3511 if ( !iscsiIsClientConnected(pImage)
3512 && pImage->fTryReconnect)
3513 {
3514 pImage->fTryReconnect = false;
3515 iscsiReattach(pImage);
3516 }
3517
3518 /* If there is no connection complete the command with an error. */
3519 if (RT_LIKELY(iscsiIsClientConnected(pImage)))
3520 {
3521 rc = iscsiPDUTxPrepare(pImage, pIScsiCmd);
3522 if (RT_FAILURE(rc))
3523 iscsiReattach(pImage);
3524 }
3525 else
3526 iscsiCmdComplete(pImage, pIScsiCmd, VERR_NET_CONNECTION_REFUSED);
3527 break;
3528 }
3529 case ISCSICMDTYPE_EXEC:
3530 {
3531 rc = pIScsiCmd->CmdType.Exec.pfnExec(pIScsiCmd->CmdType.Exec.pvUser);
3532 iscsiCmdComplete(pImage, pIScsiCmd, rc);
3533 break;
3534 }
3535 default:
3536 AssertMsgFailed(("Invalid command type %d\n", pIScsiCmd->enmCmdType));
3537 }
3538
3539 pIScsiCmd = iscsiCmdGet(pImage);
3540 }
3541 }
3542 else if (rc == VERR_TIMEOUT && pImage->cCmdsWaiting)
3543 {
3544 /*
3545 * We are waiting for a response from the target but
3546 * it didn't answered yet.
3547 * We assume the connection is broken and try to reconnect.
3548 */
3549 LogFlow(("Timed out while waiting for an answer from the target, reconnecting\n"));
3550 iscsiReattach(pImage);
3551 }
3552 else if (RT_SUCCESS(rc) || rc == VERR_TIMEOUT)
3553 {
3554 Assert(pImage->state == ISCSISTATE_NORMAL);
3555 LogFlow(("Got socket events %#x\n", fEvents));
3556
3557 if (fEvents & VD_INTERFACETCPNET_EVT_READ)
3558 {
3559 /* Continue or start a new PDU receive task */
3560 LogFlow(("There is data on the socket\n"));
3561 rc = iscsiRecvPDUAsync(pImage);
3562 if (rc == VERR_BROKEN_PIPE)
3563 iscsiReattach(pImage);
3564 else if (RT_FAILURE(rc))
3565 iscsiLogRel(pImage, "iSCSI: Handling incoming request failed %Rrc\n", rc);
3566 }
3567
3568 if (fEvents & VD_INTERFACETCPNET_EVT_WRITE)
3569 {
3570 LogFlow(("The socket is writable\n"));
3571 rc = iscsiSendPDUAsync(pImage);
3572 if (RT_FAILURE(rc))
3573 {
3574 /*
3575 * Something unexpected happened, log the error and try to reset everything
3576 * by reattaching to the target.
3577 */
3578 iscsiLogRel(pImage, "iSCSI: Sending PDU failed %Rrc\n", rc);
3579 iscsiReattach(pImage);
3580 }
3581 }
3582
3583 if (fEvents & VD_INTERFACETCPNET_EVT_ERROR)
3584 {
3585 LogFlow(("An error ocurred\n"));
3586 iscsiReattach(pImage);
3587 }
3588 }
3589 else
3590 iscsiLogRel(pImage, "iSCSI: Waiting for I/O failed rc=%Rrc\n", rc);
3591 }
3592
3593 return VINF_SUCCESS;
3594}
3595
3596/**
3597 * Internal. - Enqueues a request asynchronously.
3598 */
3599static int iscsiCommandAsync(PISCSIIMAGE pImage, PSCSIREQ pScsiReq,
3600 PFNISCSICMDCOMPLETED pfnComplete, void *pvUser)
3601{
3602 int rc;
3603
3604 if (pImage->fExtendedSelectSupported)
3605 {
3606 PISCSICMD pIScsiCmd = (PISCSICMD)RTMemAllocZ(sizeof(ISCSICMD));
3607 if (!pIScsiCmd)
3608 return VERR_NO_MEMORY;
3609
3610 /* Init the command structure. */
3611 pIScsiCmd->pNext = NULL;
3612 pIScsiCmd->enmCmdType = ISCSICMDTYPE_REQ;
3613 pIScsiCmd->pfnComplete = pfnComplete;
3614 pIScsiCmd->pvUser = pvUser;
3615 pIScsiCmd->CmdType.ScsiReq.pScsiReq = pScsiReq;
3616
3617 rc = iscsiCmdPut(pImage, pIScsiCmd);
3618 if (RT_FAILURE(rc))
3619 RTMemFree(pIScsiCmd);
3620 }
3621 else
3622 rc = VERR_NOT_SUPPORTED;
3623
3624 return rc;
3625}
3626
3627static DECLCALLBACK(void) iscsiCommandCompleteSync(PISCSIIMAGE pImage, int rcReq, void *pvUser)
3628{
3629 RT_NOREF1(pImage);
3630 PISCSICMDSYNC pIScsiCmdSync = (PISCSICMDSYNC)pvUser;
3631
3632 pIScsiCmdSync->rcCmd = rcReq;
3633 int rc = RTSemEventSignal(pIScsiCmdSync->EventSem);
3634 AssertRC(rc);
3635}
3636
3637/**
3638 * Internal. - Enqueues a request in a synchronous fashion
3639 * i.e. returns when the request completed.
3640 */
3641static int iscsiCommandSync(PISCSIIMAGE pImage, PSCSIREQ pScsiReq, bool fRetry, int rcSense)
3642{
3643 int rc;
3644
3645 if (pImage->fExtendedSelectSupported)
3646 {
3647 ISCSICMDSYNC IScsiCmdSync;
3648
3649 /* Create event semaphore. */
3650 rc = RTSemEventCreate(&IScsiCmdSync.EventSem);
3651 if (RT_FAILURE(rc))
3652 return rc;
3653
3654 if (fRetry)
3655 {
3656 for (unsigned i = 0; i < 10; i++)
3657 {
3658 rc = iscsiCommandAsync(pImage, pScsiReq, iscsiCommandCompleteSync, &IScsiCmdSync);
3659 if (RT_FAILURE(rc))
3660 break;
3661
3662 rc = RTSemEventWait(IScsiCmdSync.EventSem, RT_INDEFINITE_WAIT);
3663 AssertRC(rc);
3664 rc = IScsiCmdSync.rcCmd;
3665
3666 if ( (RT_SUCCESS(rc) && !pScsiReq->cbSense)
3667 || RT_FAILURE(rc))
3668 break;
3669 rc = rcSense;
3670 }
3671 }
3672 else
3673 {
3674 rc = iscsiCommandAsync(pImage, pScsiReq, iscsiCommandCompleteSync, &IScsiCmdSync);
3675 if (RT_SUCCESS(rc))
3676 {
3677 rc = RTSemEventWait(IScsiCmdSync.EventSem, RT_INDEFINITE_WAIT);
3678 AssertRC(rc);
3679 rc = IScsiCmdSync.rcCmd;
3680
3681 if (RT_FAILURE(rc) || pScsiReq->cbSense > 0)
3682 rc = rcSense;
3683 }
3684 }
3685
3686 RTSemEventDestroy(IScsiCmdSync.EventSem);
3687 }
3688 else
3689 {
3690 if (fRetry)
3691 {
3692 rc = VINF_SUCCESS; /* (MSC incorrectly thinks it can be uninitialized) */
3693 for (unsigned i = 0; i < 10; i++)
3694 {
3695 rc = iscsiCommand(pImage, pScsiReq);
3696 if ( (RT_SUCCESS(rc) && !pScsiReq->cbSense)
3697 || RT_FAILURE(rc))
3698 break;
3699 rc = rcSense;
3700 }
3701 }
3702 else
3703 {
3704 rc = iscsiCommand(pImage, pScsiReq);
3705 if (RT_FAILURE(rc) || pScsiReq->cbSense > 0)
3706 rc = rcSense;
3707 }
3708 }
3709
3710 return rc;
3711}
3712
3713
3714/**
3715 * Internal. - Executes a given function in a synchronous fashion
3716 * on the I/O thread if available.
3717 */
3718static int iscsiExecSync(PISCSIIMAGE pImage, PFNISCSIEXEC pfnExec, void *pvUser)
3719{
3720 int rc;
3721
3722 if (pImage->fExtendedSelectSupported)
3723 {
3724 ISCSICMDSYNC IScsiCmdSync;
3725 PISCSICMD pIScsiCmd = (PISCSICMD)RTMemAllocZ(sizeof(ISCSICMD));
3726 if (!pIScsiCmd)
3727 return VERR_NO_MEMORY;
3728
3729 /* Create event semaphore. */
3730 rc = RTSemEventCreate(&IScsiCmdSync.EventSem);
3731 if (RT_FAILURE(rc))
3732 {
3733 RTMemFree(pIScsiCmd);
3734 return rc;
3735 }
3736
3737 /* Init the command structure. */
3738 pIScsiCmd->pNext = NULL;
3739 pIScsiCmd->enmCmdType = ISCSICMDTYPE_EXEC;
3740 pIScsiCmd->pfnComplete = iscsiCommandCompleteSync;
3741 pIScsiCmd->pvUser = &IScsiCmdSync;
3742 pIScsiCmd->CmdType.Exec.pfnExec = pfnExec;
3743 pIScsiCmd->CmdType.Exec.pvUser = pvUser;
3744
3745 rc = iscsiCmdPut(pImage, pIScsiCmd);
3746 if (RT_FAILURE(rc))
3747 RTMemFree(pIScsiCmd);
3748 else
3749 {
3750 rc = RTSemEventWait(IScsiCmdSync.EventSem, RT_INDEFINITE_WAIT);
3751 AssertRC(rc);
3752 rc = IScsiCmdSync.rcCmd;
3753 }
3754
3755 RTSemEventDestroy(IScsiCmdSync.EventSem);
3756 }
3757 else
3758 {
3759 /* No I/O thread, execute in the current thread. */
3760 rc = pfnExec(pvUser);
3761 }
3762
3763 return rc;
3764}
3765
3766
3767static DECLCALLBACK(void) iscsiCommandAsyncComplete(PISCSIIMAGE pImage, int rcReq, void *pvUser)
3768{
3769 bool fComplete = true;
3770 size_t cbTransfered = 0;
3771 PSCSIREQ pScsiReq = (PSCSIREQ)pvUser;
3772
3773 if (RT_SUCCESS(rcReq))
3774 ASMAtomicWriteU32(&pImage->cLoginsSinceIo, 0);
3775
3776 if ( RT_SUCCESS(rcReq)
3777 && pScsiReq->cbSense > 0)
3778 {
3779 /* Try again if possible. */
3780 if (pScsiReq->cSenseRetries > 0)
3781 {
3782 pScsiReq->cSenseRetries--;
3783 pScsiReq->cbSense = sizeof(pScsiReq->abSense);
3784 int rc = iscsiCommandAsync(pImage, pScsiReq, iscsiCommandAsyncComplete, pScsiReq);
3785 if (RT_SUCCESS(rc))
3786 fComplete = false;
3787 else
3788 rcReq = pScsiReq->rcSense;
3789 }
3790 else
3791 rcReq = pScsiReq->rcSense;
3792 }
3793
3794 if (fComplete)
3795 {
3796 if (pScsiReq->enmXfer == SCSIXFER_FROM_TARGET)
3797 cbTransfered = pScsiReq->cbT2IData;
3798 else if (pScsiReq->enmXfer == SCSIXFER_TO_TARGET)
3799 cbTransfered = pScsiReq->cbI2TData;
3800 else
3801 AssertMsg(pScsiReq->enmXfer == SCSIXFER_NONE, ("To/From transfers are not supported yet\n"));
3802
3803 /* Continue I/O context. */
3804 pImage->pIfIo->pfnIoCtxCompleted(pImage->pIfIo->Core.pvUser,
3805 pScsiReq->pIoCtx, rcReq,
3806 cbTransfered);
3807
3808 RTMemFree(pScsiReq);
3809 }
3810}
3811
3812
3813/**
3814 * Internal. Free all allocated space for representing an image, and optionally
3815 * delete the image from disk.
3816 */
3817static int iscsiFreeImage(PISCSIIMAGE pImage, bool fDelete)
3818{
3819 int rc = VINF_SUCCESS;
3820 Assert(!fDelete); NOREF(fDelete); /* This MUST be false, the flag isn't supported. */
3821
3822 /* Freeing a never allocated image (e.g. because the open failed) is
3823 * not signalled as an error. After all nothing bad happens. */
3824 if (pImage)
3825 {
3826 if (pImage->Mutex != NIL_RTSEMMUTEX)
3827 {
3828 /* Detaching only makes sense when the mutex is there. Otherwise the
3829 * failure happened long before we could attach to the target. */
3830 iscsiExecSync(pImage, iscsiDetach, pImage);
3831 RTSemMutexDestroy(pImage->Mutex);
3832 pImage->Mutex = NIL_RTSEMMUTEX;
3833 }
3834 if (pImage->hThreadIo != NIL_RTTHREAD)
3835 {
3836 ASMAtomicXchgBool(&pImage->fRunning, false);
3837 rc = iscsiIoThreadPoke(pImage);
3838 AssertRC(rc);
3839
3840 /* Wait for the thread to terminate. */
3841 rc = RTThreadWait(pImage->hThreadIo, RT_INDEFINITE_WAIT, NULL);
3842 AssertRC(rc);
3843 }
3844 /* Destroy the socket. */
3845 if (pImage->Socket != NIL_VDSOCKET)
3846 {
3847 pImage->pIfNet->pfnSocketDestroy(pImage->Socket);
3848 }
3849 if (pImage->MutexReqQueue != NIL_RTSEMMUTEX)
3850 {
3851 RTSemMutexDestroy(pImage->MutexReqQueue);
3852 pImage->MutexReqQueue = NIL_RTSEMMUTEX;
3853 }
3854 if (pImage->pszTargetName)
3855 {
3856 RTMemFree(pImage->pszTargetName);
3857 pImage->pszTargetName = NULL;
3858 }
3859 if (pImage->pszTargetAddress)
3860 {
3861 RTMemFree(pImage->pszTargetAddress);
3862 pImage->pszTargetAddress = NULL;
3863 }
3864 if (pImage->pszInitiatorName)
3865 {
3866 if (pImage->fAutomaticInitiatorName)
3867 RTStrFree(pImage->pszInitiatorName);
3868 else
3869 RTMemFree(pImage->pszInitiatorName);
3870 pImage->pszInitiatorName = NULL;
3871 }
3872 if (pImage->pszInitiatorUsername)
3873 {
3874 RTMemFree(pImage->pszInitiatorUsername);
3875 pImage->pszInitiatorUsername = NULL;
3876 }
3877 if (pImage->pbInitiatorSecret)
3878 {
3879 RTMemFree(pImage->pbInitiatorSecret);
3880 pImage->pbInitiatorSecret = NULL;
3881 }
3882 if (pImage->pszTargetUsername)
3883 {
3884 RTMemFree(pImage->pszTargetUsername);
3885 pImage->pszTargetUsername = NULL;
3886 }
3887 if (pImage->pbTargetSecret)
3888 {
3889 RTMemFree(pImage->pbTargetSecret);
3890 pImage->pbTargetSecret = NULL;
3891 }
3892 if (pImage->pvRecvPDUBuf)
3893 {
3894 RTMemFree(pImage->pvRecvPDUBuf);
3895 pImage->pvRecvPDUBuf = NULL;
3896 }
3897 if (pImage->pszHostname)
3898 {
3899 RTMemFree(pImage->pszHostname);
3900 pImage->pszHostname = NULL;
3901 }
3902
3903 pImage->cbRecvPDUResidual = 0;
3904 }
3905
3906 LogFlowFunc(("returns %Rrc\n", rc));
3907 return rc;
3908}
3909
3910/**
3911 * Internal: Open an image, constructing all necessary data structures.
3912 */
3913static int iscsiOpenImage(PISCSIIMAGE pImage, unsigned uOpenFlags)
3914{
3915 int rc;
3916 char *pszLUN = NULL, *pszLUNInitial = NULL;
3917 bool fLunEncoded = false;
3918 uint32_t uWriteSplitDef = 0;
3919 uint32_t uTimeoutDef = 0;
3920 uint64_t uCfgTmp = 0;
3921 bool fHostIPDef = false;
3922 bool fDumpMalformedPacketsDef = false;
3923 rc = RTStrToUInt32Full(s_iscsiConfigDefaultWriteSplit, 0, &uWriteSplitDef);
3924 AssertRC(rc);
3925 rc = RTStrToUInt32Full(s_iscsiConfigDefaultTimeout, 0, &uTimeoutDef);
3926 AssertRC(rc);
3927 rc = RTStrToUInt64Full(s_iscsiConfigDefaultHostIPStack, 0, &uCfgTmp);
3928 AssertRC(rc);
3929 fHostIPDef = RT_BOOL(uCfgTmp);
3930
3931 rc = RTStrToUInt64Full(s_iscsiConfigDefaultDumpMalformedPackets, 0, &uCfgTmp);
3932 AssertRC(rc);
3933 fDumpMalformedPacketsDef = RT_BOOL(uCfgTmp);
3934
3935 pImage->uOpenFlags = uOpenFlags;
3936
3937 /* Get error signalling interface. */
3938 pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk);
3939
3940 /* Get TCP network stack interface. */
3941 pImage->pIfNet = VDIfTcpNetGet(pImage->pVDIfsImage);
3942 if (!pImage->pIfNet)
3943 {
3944 rc = vdIfError(pImage->pIfError, VERR_VD_UNKNOWN_INTERFACE,
3945 RT_SRC_POS, N_("iSCSI: TCP network stack interface missing"));
3946 goto out;
3947 }
3948
3949 /* Get configuration interface. */
3950 pImage->pIfConfig = VDIfConfigGet(pImage->pVDIfsImage);
3951 if (!pImage->pIfConfig)
3952 {
3953 rc = vdIfError(pImage->pIfError, VERR_VD_UNKNOWN_INTERFACE,
3954 RT_SRC_POS, N_("iSCSI: configuration interface missing"));
3955 goto out;
3956 }
3957
3958 /* Get I/O interface. */
3959 pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage);
3960 if (!pImage->pIfIo)
3961 {
3962 rc = vdIfError(pImage->pIfError, VERR_VD_UNKNOWN_INTERFACE,
3963 RT_SRC_POS, N_("iSCSI: I/O interface missing"));
3964 goto out;
3965 }
3966
3967 /* This ISID will be adjusted later to make it unique on this host. */
3968 pImage->ISID = 0x800000000000ULL | 0x001234560000ULL;
3969 pImage->cISCSIRetries = 10;
3970 pImage->state = ISCSISTATE_FREE;
3971 pImage->cLoginsSinceIo = 0;
3972 pImage->pvRecvPDUBuf = RTMemAlloc(ISCSI_RECV_PDU_BUFFER_SIZE);
3973 pImage->cbRecvPDUBuf = ISCSI_RECV_PDU_BUFFER_SIZE;
3974 if (pImage->pvRecvPDUBuf == NULL)
3975 {
3976 rc = VERR_NO_MEMORY;
3977 goto out;
3978 }
3979 pImage->Mutex = NIL_RTSEMMUTEX;
3980 pImage->MutexReqQueue = NIL_RTSEMMUTEX;
3981 rc = RTSemMutexCreate(&pImage->Mutex);
3982 if (RT_FAILURE(rc))
3983 goto out;
3984
3985 rc = RTSemMutexCreate(&pImage->MutexReqQueue);
3986 if (RT_FAILURE(rc))
3987 goto out;
3988
3989 /* Validate configuration, detect unknown keys. */
3990 if (!VDCFGAreKeysValid(pImage->pIfConfig,
3991 "TargetName\0"
3992 "InitiatorName\0"
3993 "LUN\0"
3994 "TargetAddress\0"
3995 "InitiatorUsername\0"
3996 "InitiatorSecret\0"
3997 "InitiatorSecretEncrypted\0"
3998 "TargetUsername\0"
3999 "TargetSecret\0"
4000 "WriteSplit\0"
4001 "Timeout\0"
4002 "HostIPStack\0"
4003 "DumpMalformedPackets\0"))
4004 {
4005 rc = vdIfError(pImage->pIfError, VERR_VD_UNKNOWN_CFG_VALUES, RT_SRC_POS, N_("iSCSI: configuration error: unknown configuration keys present"));
4006 goto out;
4007 }
4008
4009 /* Query the iSCSI upper level configuration. */
4010 rc = VDCFGQueryStringAlloc(pImage->pIfConfig,
4011 "TargetName", &pImage->pszTargetName);
4012 if (RT_FAILURE(rc))
4013 {
4014 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetName as string"));
4015 goto out;
4016 }
4017 rc = VDCFGQueryStringAlloc(pImage->pIfConfig,
4018 "InitiatorName", &pImage->pszInitiatorName);
4019 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
4020 {
4021 pImage->fAutomaticInitiatorName = true;
4022 rc = VINF_SUCCESS;
4023 }
4024 if (RT_FAILURE(rc))
4025 {
4026 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorName as string"));
4027 goto out;
4028 }
4029 rc = VDCFGQueryStringAllocDef(pImage->pIfConfig,
4030 "LUN", &pszLUN, s_iscsiConfigDefaultLUN);
4031 if (RT_FAILURE(rc))
4032 {
4033 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read LUN as string"));
4034 goto out;
4035 }
4036 pszLUNInitial = pszLUN;
4037 if (!strncmp(pszLUN, "enc", 3))
4038 {
4039 fLunEncoded = true;
4040 pszLUN += 3;
4041 }
4042 rc = RTStrToUInt64Full(pszLUN, 0, &pImage->LUN);
4043 if (RT_FAILURE(rc))
4044 {
4045 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to convert LUN to integer"));
4046 goto out;
4047 }
4048 if (!fLunEncoded)
4049 {
4050 if (pImage->LUN <= 255)
4051 {
4052 pImage->LUN = pImage->LUN << 48; /* uses peripheral device addressing method */
4053 }
4054 else if (pImage->LUN <= 16383)
4055 {
4056 pImage->LUN = (pImage->LUN << 48) | RT_BIT_64(62); /* uses flat space addressing method */
4057 }
4058 else
4059 {
4060 rc = VERR_OUT_OF_RANGE;
4061 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: LUN number out of range (0-16383)"));
4062 goto out;
4063 }
4064 }
4065 rc = VDCFGQueryStringAlloc(pImage->pIfConfig,
4066 "TargetAddress", &pImage->pszTargetAddress);
4067 if (RT_FAILURE(rc))
4068 {
4069 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetAddress as string"));
4070 goto out;
4071 }
4072 pImage->pszInitiatorUsername = NULL;
4073 rc = VDCFGQueryStringAlloc(pImage->pIfConfig,
4074 "InitiatorUsername",
4075 &pImage->pszInitiatorUsername);
4076 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
4077 rc = VINF_SUCCESS;
4078 if (RT_FAILURE(rc))
4079 {
4080 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorUsername as string"));
4081 goto out;
4082 }
4083 pImage->pbInitiatorSecret = NULL;
4084 pImage->cbInitiatorSecret = 0;
4085 rc = VDCFGQueryBytesAlloc(pImage->pIfConfig,
4086 "InitiatorSecret",
4087 (void **)&pImage->pbInitiatorSecret,
4088 &pImage->cbInitiatorSecret);
4089 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
4090 rc = VINF_SUCCESS;
4091 if (RT_FAILURE(rc))
4092 {
4093 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorSecret as byte string"));
4094 goto out;
4095 }
4096 void *pvInitiatorSecretEncrypted;
4097 size_t cbInitiatorSecretEncrypted;
4098 rc = VDCFGQueryBytesAlloc(pImage->pIfConfig,
4099 "InitiatorSecretEncrypted",
4100 &pvInitiatorSecretEncrypted,
4101 &cbInitiatorSecretEncrypted);
4102 if (RT_SUCCESS(rc))
4103 {
4104 RTMemFree(pvInitiatorSecretEncrypted);
4105 if (!pImage->pbInitiatorSecret)
4106 {
4107 /* we have an encrypted initiator secret but not an unencrypted one */
4108 rc = vdIfError(pImage->pIfError, VERR_VD_ISCSI_SECRET_ENCRYPTED, RT_SRC_POS, N_("iSCSI: initiator secret not decrypted"));
4109 goto out;
4110 }
4111 }
4112 pImage->pszTargetUsername = NULL;
4113 rc = VDCFGQueryStringAlloc(pImage->pIfConfig,
4114 "TargetUsername",
4115 &pImage->pszTargetUsername);
4116 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
4117 rc = VINF_SUCCESS;
4118 if (RT_FAILURE(rc))
4119 {
4120 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetUsername as string"));
4121 goto out;
4122 }
4123 pImage->pbTargetSecret = NULL;
4124 pImage->cbTargetSecret = 0;
4125 rc = VDCFGQueryBytesAlloc(pImage->pIfConfig,
4126 "TargetSecret", (void **)&pImage->pbTargetSecret,
4127 &pImage->cbTargetSecret);
4128 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
4129 rc = VINF_SUCCESS;
4130 if (RT_FAILURE(rc))
4131 {
4132 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetSecret as byte string"));
4133 goto out;
4134 }
4135 rc = VDCFGQueryU32Def(pImage->pIfConfig,
4136 "WriteSplit", &pImage->cbWriteSplit,
4137 uWriteSplitDef);
4138 if (RT_FAILURE(rc))
4139 {
4140 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read WriteSplit as U32"));
4141 goto out;
4142 }
4143
4144 pImage->pszHostname = NULL;
4145 pImage->uPort = 0;
4146 pImage->Socket = NIL_VDSOCKET;
4147 /* Query the iSCSI lower level configuration. */
4148 rc = VDCFGQueryU32Def(pImage->pIfConfig,
4149 "Timeout", &pImage->uReadTimeout,
4150 uTimeoutDef);
4151 if (RT_FAILURE(rc))
4152 {
4153 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read Timeout as U32"));
4154 goto out;
4155 }
4156 rc = VDCFGQueryBoolDef(pImage->pIfConfig,
4157 "HostIPStack", &pImage->fHostIP,
4158 fHostIPDef);
4159 if (RT_FAILURE(rc))
4160 {
4161 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read HostIPStack as boolean"));
4162 goto out;
4163 }
4164
4165 rc = VDCFGQueryBoolDef(pImage->pIfConfig,
4166 "DumpMalformedPackets", &pImage->fDumpMalformedPackets,
4167 fDumpMalformedPacketsDef);
4168 if (RT_FAILURE(rc))
4169 {
4170 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read DumpMalformedPackets as boolean"));
4171 goto out;
4172 }
4173
4174 /* Don't actually establish iSCSI transport connection if this is just an
4175 * open to query the image information and the host IP stack isn't used.
4176 * Even trying is rather useless, as in this context the InTnet IP stack
4177 * isn't present. Returning dummies is the best possible result anyway. */
4178 if ((uOpenFlags & VD_OPEN_FLAGS_INFO) && !pImage->fHostIP)
4179 {
4180 LogFunc(("Not opening the transport connection as IntNet IP stack is not available. Will return dummies\n"));
4181 goto out;
4182 }
4183
4184 memset(pImage->aCmdsWaiting, 0, sizeof(pImage->aCmdsWaiting));
4185 pImage->cbRecvPDUResidual = 0;
4186
4187 /* Create the socket structure. */
4188 rc = pImage->pIfNet->pfnSocketCreate(VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT,
4189 &pImage->Socket);
4190 if (RT_SUCCESS(rc))
4191 {
4192 pImage->fExtendedSelectSupported = true;
4193 pImage->fRunning = true;
4194 rc = RTThreadCreate(&pImage->hThreadIo, iscsiIoThreadWorker, pImage, 0,
4195 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "iSCSI-Io");
4196 if (RT_FAILURE(rc))
4197 {
4198 LogFunc(("Creating iSCSI I/O thread failed rc=%Rrc\n", rc));
4199 goto out;
4200 }
4201 }
4202 else if (rc == VERR_NOT_SUPPORTED)
4203 {
4204 /* Async I/O is not supported without extended select. */
4205 if ((uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO))
4206 {
4207 LogFunc(("Extended select is not supported by the interface but async I/O is requested -> %Rrc\n", rc));
4208 goto out;
4209 }
4210 else
4211 {
4212 pImage->fExtendedSelectSupported = false;
4213 rc = pImage->pIfNet->pfnSocketCreate(0, &pImage->Socket);
4214 if (RT_FAILURE(rc))
4215 {
4216 LogFunc(("Creating socket failed -> %Rrc\n", rc));
4217 goto out;
4218 }
4219 }
4220 }
4221 else
4222 {
4223 LogFunc(("Creating socket failed -> %Rrc\n", rc));
4224 goto out;
4225 }
4226
4227 /*
4228 * Attach to the iSCSI target. This implicitly establishes the iSCSI
4229 * transport connection.
4230 */
4231 rc = iscsiExecSync(pImage, iscsiAttach, pImage);
4232 if (RT_FAILURE(rc))
4233 {
4234 LogRel(("iSCSI: could not open target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
4235 goto out;
4236 }
4237 LogFlowFunc(("target '%s' opened successfully\n", pImage->pszTargetName));
4238
4239 SCSIREQ sr;
4240 RTSGSEG DataSeg;
4241 uint8_t data8[8];
4242 uint8_t data12[12];
4243
4244 /*
4245 * Inquire available LUNs - purely dummy request.
4246 */
4247 uint8_t rlundata[16];
4248 RT_ZERO(sr.abCDB);
4249 sr.abCDB[0] = SCSI_REPORT_LUNS;
4250 sr.abCDB[1] = 0; /* reserved */
4251 sr.abCDB[2] = 0; /* reserved */
4252 sr.abCDB[3] = 0; /* reserved */
4253 sr.abCDB[4] = 0; /* reserved */
4254 sr.abCDB[5] = 0; /* reserved */
4255 sr.abCDB[6] = sizeof(rlundata) >> 24;
4256 sr.abCDB[7] = (sizeof(rlundata) >> 16) & 0xff;
4257 sr.abCDB[8] = (sizeof(rlundata) >> 8) & 0xff;
4258 sr.abCDB[9] = sizeof(rlundata) & 0xff;
4259 sr.abCDB[10] = 0; /* reserved */
4260 sr.abCDB[11] = 0; /* control */
4261
4262 DataSeg.pvSeg = rlundata;
4263 DataSeg.cbSeg = sizeof(rlundata);
4264
4265 sr.enmXfer = SCSIXFER_FROM_TARGET;
4266 sr.cbCDB = 12;
4267 sr.cbI2TData = 0;
4268 sr.paI2TSegs = NULL;
4269 sr.cI2TSegs = 0;
4270 sr.cbT2IData = DataSeg.cbSeg;
4271 sr.paT2ISegs = &DataSeg;
4272 sr.cT2ISegs = 1;
4273 sr.cbSense = sizeof(sr.abSense);
4274 rc = iscsiCommandSync(pImage, &sr, false, VERR_INVALID_STATE);
4275 if (RT_FAILURE(rc))
4276 {
4277 LogRel(("iSCSI: Could not get LUN info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
4278 goto out;
4279 }
4280
4281 /*
4282 * Inquire device characteristics - no tapes, scanners etc., please.
4283 */
4284 RT_ZERO(sr.abCDB);
4285 sr.abCDB[0] = SCSI_INQUIRY;
4286 sr.abCDB[1] = 0; /* reserved */
4287 sr.abCDB[2] = 0; /* reserved */
4288 sr.abCDB[3] = 0; /* reserved */
4289 sr.abCDB[4] = sizeof(data8);
4290 sr.abCDB[5] = 0; /* control */
4291
4292 DataSeg.pvSeg = data8;
4293 DataSeg.cbSeg = sizeof(data8);
4294
4295 sr.enmXfer = SCSIXFER_FROM_TARGET;
4296 sr.cbCDB = 6;
4297 sr.cbI2TData = 0;
4298 sr.paI2TSegs = NULL;
4299 sr.cI2TSegs = 0;
4300 sr.cbT2IData = DataSeg.cbSeg;
4301 sr.paT2ISegs = &DataSeg;
4302 sr.cT2ISegs = 1;
4303 sr.cbSense = sizeof(sr.abSense);
4304 rc = iscsiCommandSync(pImage, &sr, true /* fRetry */, VERR_INVALID_STATE);
4305 if (RT_SUCCESS(rc))
4306 {
4307 uint8_t devType = (sr.cbT2IData > 0) ? data8[0] & SCSI_DEVTYPE_MASK : 255;
4308 if (devType != SCSI_DEVTYPE_DISK)
4309 {
4310 rc = vdIfError(pImage->pIfError, VERR_VD_ISCSI_INVALID_TYPE,
4311 RT_SRC_POS, N_("iSCSI: target address %s, target name %s, SCSI LUN %lld reports device type=%u"),
4312 pImage->pszTargetAddress, pImage->pszTargetName,
4313 pImage->LUN, devType);
4314 LogRel(("iSCSI: Unsupported SCSI peripheral device type %d for target %s\n", devType & SCSI_DEVTYPE_MASK, pImage->pszTargetName));
4315 goto out;
4316 }
4317 uint8_t uCmdQueue = (sr.cbT2IData >= 8) ? data8[7] & SCSI_INQUIRY_CMDQUE_MASK : 0;
4318 if (uCmdQueue > 0)
4319 pImage->fCmdQueuingSupported = true;
4320 else if (uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO)
4321 {
4322 rc = VERR_NOT_SUPPORTED;
4323 goto out;
4324 }
4325
4326 LogRel(("iSCSI: target address %s, target name %s, %s command queuing\n",
4327 pImage->pszTargetAddress, pImage->pszTargetName,
4328 pImage->fCmdQueuingSupported ? "supports" : "doesn't support"));
4329 }
4330 else
4331 {
4332 LogRel(("iSCSI: Could not get INQUIRY info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
4333 goto out;
4334 }
4335
4336 /*
4337 * Query write disable bit in the device specific parameter entry in the
4338 * mode parameter header. Refuse read/write opening of read only disks.
4339 */
4340 uint8_t data4[4];
4341 RT_ZERO(sr.abCDB);
4342 sr.abCDB[0] = SCSI_MODE_SENSE_6;
4343 sr.abCDB[1] = 0; /* dbd=0/reserved */
4344 sr.abCDB[2] = 0x3f; /* pc=0/page code=0x3f, ask for all pages */
4345 sr.abCDB[3] = 0; /* subpage code=0, return everything in page_0 format */
4346 sr.abCDB[4] = sizeof(data4); /* allocation length=4 */
4347 sr.abCDB[5] = 0; /* control */
4348
4349 DataSeg.pvSeg = data4;
4350 DataSeg.cbSeg = sizeof(data4);
4351
4352 sr.enmXfer = SCSIXFER_FROM_TARGET;
4353 sr.cbCDB = 6;
4354 sr.cbI2TData = 0;
4355 sr.paI2TSegs = NULL;
4356 sr.cI2TSegs = 0;
4357 sr.cbT2IData = DataSeg.cbSeg;
4358 sr.paT2ISegs = &DataSeg;
4359 sr.cT2ISegs = 1;
4360 sr.cbSense = sizeof(sr.abSense);
4361 rc = iscsiCommandSync(pImage, &sr, true /* fRetry */, VERR_INVALID_STATE);
4362 if (RT_SUCCESS(rc))
4363 {
4364 pImage->fTargetReadOnly = !!(data4[2] & 0x80);
4365 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY) && pImage->fTargetReadOnly)
4366 {
4367 rc = VERR_VD_IMAGE_READ_ONLY;
4368 goto out;
4369 }
4370 }
4371 else
4372 {
4373 LogRel(("iSCSI: Could not get MODE SENSE info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
4374 goto out;
4375 }
4376
4377 /*
4378 * Determine sector size and capacity of the volume immediately.
4379 */
4380 RT_ZERO(data12);
4381 RT_ZERO(sr.abCDB);
4382 sr.abCDB[0] = SCSI_SERVICE_ACTION_IN_16;
4383 sr.abCDB[1] = SCSI_SVC_ACTION_IN_READ_CAPACITY_16; /* subcommand */
4384 sr.abCDB[10+3] = sizeof(data12); /* allocation length (dword) */
4385
4386 DataSeg.pvSeg = data12;
4387 DataSeg.cbSeg = sizeof(data12);
4388
4389 sr.enmXfer = SCSIXFER_FROM_TARGET;
4390 sr.cbCDB = 16;
4391 sr.cbI2TData = 0;
4392 sr.paI2TSegs = NULL;
4393 sr.cI2TSegs = 0;
4394 sr.cbT2IData = DataSeg.cbSeg;
4395 sr.paT2ISegs = &DataSeg;
4396 sr.cT2ISegs = 1;
4397 sr.cbSense = sizeof(sr.abSense);
4398
4399 rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
4400 if (RT_SUCCESS(rc))
4401 {
4402 bool fEnd = false;
4403 uint8_t cMaxRetries = 10;
4404 do
4405 {
4406 switch (sr.status)
4407 {
4408 case SCSI_STATUS_OK:
4409 {
4410 pImage->cVolume = RT_BE2H_U64(*(uint64_t *)&data12[0]);
4411 pImage->cVolume++;
4412 pImage->cbSector = RT_BE2H_U32(*(uint32_t *)&data12[8]);
4413 pImage->cbSize = pImage->cVolume * pImage->cbSector;
4414 if (pImage->cVolume == 0 || pImage->cbSize < pImage->cVolume)
4415 {
4416 rc = vdIfError(pImage->pIfError, VERR_VD_ISCSI_INVALID_TYPE,
4417 RT_SRC_POS, N_("iSCSI: target address %s, target name %s, SCSI LUN %lld reports media sector count=%llu sector size=%u"),
4418 pImage->pszTargetAddress, pImage->pszTargetName,
4419 pImage->LUN, pImage->cVolume, pImage->cbSector);
4420 }
4421 fEnd = true;
4422 break;
4423 }
4424 case SCSI_STATUS_CHECK_CONDITION:
4425 {
4426 if((sr.abSense[2] & 0x0f) == SCSI_SENSE_UNIT_ATTENTION)
4427 {
4428 if( sr.abSense[12] == SCSI_ASC_POWER_ON_RESET_BUS_DEVICE_RESET_OCCURRED
4429 && sr.abSense[13] == SCSI_ASCQ_POWER_ON_RESET_BUS_DEVICE_RESET_OCCURRED)
4430 {
4431/** @todo for future: prepare and send command "REQUEST SENSE" which will
4432return the status of target and will clear any unit attention condition that it reports */
4433 rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
4434 if (RT_FAILURE(rc))
4435 fEnd = true;
4436 cMaxRetries--;
4437 break;
4438
4439 }
4440 }
4441 break;
4442 }
4443 default:
4444 {
4445 rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
4446 if (RT_FAILURE(rc))
4447 fEnd = true;
4448 cMaxRetries--;
4449 break;
4450 }
4451 }
4452 if (!cMaxRetries)
4453 fEnd = true;
4454 } while(!fEnd);
4455 }
4456 else
4457 {
4458 RT_ZERO(data8);
4459 sr.abCDB[0] = SCSI_READ_CAPACITY;
4460 sr.abCDB[1] = 0; /* reserved */
4461 sr.abCDB[2] = 0; /* reserved */
4462 sr.abCDB[3] = 0; /* reserved */
4463 sr.abCDB[4] = 0; /* reserved */
4464 sr.abCDB[5] = 0; /* reserved */
4465 sr.abCDB[6] = 0; /* reserved */
4466 sr.abCDB[7] = 0; /* reserved */
4467 sr.abCDB[8] = 0; /* reserved */
4468 sr.abCDB[9] = 0; /* control */
4469
4470 DataSeg.pvSeg = data8;
4471 DataSeg.cbSeg = sizeof(data8);
4472
4473 sr.enmXfer = SCSIXFER_FROM_TARGET;
4474 sr.cbCDB = 10;
4475 sr.cbI2TData = 0;
4476 sr.paI2TSegs = NULL;
4477 sr.cI2TSegs = 0;
4478 sr.cbT2IData = DataSeg.cbSeg;
4479 sr.paT2ISegs = &DataSeg;
4480 sr.cT2ISegs = 1;
4481 sr.cbSense = sizeof(sr.abSense);
4482 rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
4483 if (RT_SUCCESS(rc))
4484 {
4485 bool fEnd = false;
4486 uint8_t cMaxRetries = 10;
4487 do
4488 {
4489 switch (sr.status)
4490 {
4491 case SCSI_STATUS_OK:
4492 {
4493 pImage->cVolume = (data8[0] << 24) | (data8[1] << 16) | (data8[2] << 8) | data8[3];
4494 pImage->cVolume++;
4495 pImage->cbSector = (data8[4] << 24) | (data8[5] << 16) | (data8[6] << 8) | data8[7];
4496 pImage->cbSize = pImage->cVolume * pImage->cbSector;
4497 if (pImage->cVolume == 0)
4498 {
4499 rc = vdIfError(pImage->pIfError, VERR_VD_ISCSI_INVALID_TYPE,
4500 RT_SRC_POS, N_("iSCSI: fallback capacity detection for target address %s, target name %s, SCSI LUN %lld reports media sector count=%llu sector size=%u"),
4501 pImage->pszTargetAddress, pImage->pszTargetName,
4502 pImage->LUN, pImage->cVolume, pImage->cbSector);
4503 }
4504
4505 fEnd = true;
4506 break;
4507 }
4508 case SCSI_STATUS_CHECK_CONDITION:
4509 {
4510 if((sr.abSense[2] & 0x0f) == SCSI_SENSE_UNIT_ATTENTION)
4511 {
4512 if( sr.abSense[12] == SCSI_ASC_POWER_ON_RESET_BUS_DEVICE_RESET_OCCURRED
4513 && sr.abSense[13] == SCSI_ASCQ_POWER_ON_RESET_BUS_DEVICE_RESET_OCCURRED)
4514 {
4515 /** @todo for future: prepare and send command "REQUEST SENSE" which will
4516 return the status of target and will clear any unit attention condition that it reports */
4517 rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
4518 if (RT_FAILURE(rc))
4519 fEnd = true;
4520 cMaxRetries--;
4521 break;
4522
4523 }
4524 }
4525 break;
4526 }
4527 default:
4528 {
4529 rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
4530 if (RT_FAILURE(rc))
4531 fEnd = true;
4532 cMaxRetries--;
4533 break;
4534 }
4535 }
4536 if (!cMaxRetries)
4537 fEnd = true;
4538 } while(!fEnd);
4539 }
4540 else
4541 {
4542 LogRel(("iSCSI: Could not determine capacity of target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
4543 goto out;
4544 }
4545 }
4546
4547 /*
4548 * Check the read and write cache bits.
4549 * Try to enable the cache if it is disabled.
4550 *
4551 * We already checked that this is a block access device. No need
4552 * to do it again.
4553 */
4554 uint8_t aCachingModePage[32];
4555
4556 memset(aCachingModePage, '\0', sizeof(aCachingModePage));
4557 sr.abCDB[0] = SCSI_MODE_SENSE_6;
4558 sr.abCDB[1] = 0;
4559 sr.abCDB[2] = (0x00 << 6) | (0x08 & 0x3f); /* Current values and caching mode page */
4560 sr.abCDB[3] = 0; /* Sub page code. */
4561 sr.abCDB[4] = sizeof(aCachingModePage) & 0xff;
4562 sr.abCDB[5] = 0;
4563
4564 DataSeg.pvSeg = aCachingModePage;
4565 DataSeg.cbSeg = sizeof(aCachingModePage);
4566
4567 sr.enmXfer = SCSIXFER_FROM_TARGET;
4568 sr.cbCDB = 6;
4569 sr.cbI2TData = 0;
4570 sr.paI2TSegs = NULL;
4571 sr.cI2TSegs = 0;
4572 sr.cbT2IData = DataSeg.cbSeg;
4573 sr.paT2ISegs = &DataSeg;
4574 sr.cT2ISegs = 1;
4575 sr.cbSense = sizeof(sr.abSense);
4576 rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
4577 if ( RT_SUCCESS(rc)
4578 && (sr.status == SCSI_STATUS_OK)
4579 && (aCachingModePage[0] >= 15)
4580 && (aCachingModePage[4 + aCachingModePage[3]] & 0x3f) == 0x08
4581 && (aCachingModePage[4 + aCachingModePage[3] + 1] > 3))
4582 {
4583 uint32_t Offset = 4 + aCachingModePage[3];
4584 /*
4585 * Check if the read and/or the write cache is disabled.
4586 * The write cache is disabled if bit 2 (WCE) is zero and
4587 * the read cache is disabled if bit 0 (RCD) is set.
4588 */
4589 if (!ASMBitTest(&aCachingModePage[Offset + 2], 2) || ASMBitTest(&aCachingModePage[Offset + 2], 0))
4590 {
4591 /*
4592 * Write Cache Enable (WCE) bit is zero or the Read Cache Disable (RCD) is one
4593 * So one of the caches is disabled. Enable both caches.
4594 * The rest is unchanged.
4595 */
4596 ASMBitSet(&aCachingModePage[Offset + 2], 2);
4597 ASMBitClear(&aCachingModePage[Offset + 2], 0);
4598
4599 sr.abCDB[0] = SCSI_MODE_SELECT_6;
4600 sr.abCDB[1] = 0; /* Don't write the page into NV RAM. */
4601 sr.abCDB[2] = 0;
4602 sr.abCDB[3] = 0;
4603 sr.abCDB[4] = sizeof(aCachingModePage) & 0xff;
4604 sr.abCDB[5] = 0;
4605
4606 DataSeg.pvSeg = aCachingModePage;
4607 DataSeg.cbSeg = sizeof(aCachingModePage);
4608
4609 sr.enmXfer = SCSIXFER_TO_TARGET;
4610 sr.cbCDB = 6;
4611 sr.cbI2TData = DataSeg.cbSeg;
4612 sr.paI2TSegs = &DataSeg;
4613 sr.cI2TSegs = 1;
4614 sr.cbT2IData = 0;
4615 sr.paT2ISegs = NULL;
4616 sr.cT2ISegs = 0;
4617 sr.cbSense = sizeof(sr.abSense);
4618 sr.status = 0;
4619 rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
4620 if ( RT_SUCCESS(rc)
4621 && (sr.status == SCSI_STATUS_OK))
4622 {
4623 LogRel(("iSCSI: Enabled read and write cache of target %s\n", pImage->pszTargetName));
4624 }
4625 else
4626 {
4627 /* Log failures but continue. */
4628 LogRel(("iSCSI: Could not enable read and write cache of target %s, rc=%Rrc status=%#x\n",
4629 pImage->pszTargetName, rc, sr.status));
4630 LogRel(("iSCSI: Sense:\n%.*Rhxd\n", sr.cbSense, sr.abSense));
4631 rc = VINF_SUCCESS;
4632 }
4633 }
4634 }
4635 else
4636 {
4637 /* Log errors but continue. */
4638 LogRel(("iSCSI: Could not check write cache of target %s, rc=%Rrc, got mode page %#x\n", pImage->pszTargetName, rc, aCachingModePage[0] & 0x3f));
4639 LogRel(("iSCSI: Sense:\n%.*Rhxd\n", sr.cbSense, sr.abSense));
4640 rc = VINF_SUCCESS;
4641 }
4642
4643out:
4644 if (pszLUNInitial)
4645 RTMemFree(pszLUNInitial);
4646 if (RT_FAILURE(rc))
4647 iscsiFreeImage(pImage, false);
4648 return rc;
4649}
4650
4651
4652/** @copydoc VDIMAGEBACKEND::pfnCheckIfValid */
4653static DECLCALLBACK(int) iscsiCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
4654 PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
4655{
4656 RT_NOREF4(pszFilename, pVDIfsDisk, pVDIfsImage, penmType);
4657 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
4658
4659 /* iSCSI images can't be checked for validity this way, as the filename
4660 * just can't supply enough configuration information. */
4661 int rc = VERR_VD_ISCSI_INVALID_HEADER;
4662
4663 LogFlowFunc(("returns %Rrc\n", rc));
4664 return rc;
4665}
4666
4667/** @copydoc VDIMAGEBACKEND::pfnOpen */
4668static DECLCALLBACK(int) iscsiOpen(const char *pszFilename, unsigned uOpenFlags,
4669 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
4670 VDTYPE enmType, void **ppBackendData)
4671{
4672 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p enmType=%u ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, enmType, ppBackendData));
4673 int rc;
4674 PISCSIIMAGE pImage;
4675
4676 NOREF(enmType); /**< @todo r=klaus make use of the type info. */
4677
4678 /* Check open flags. All valid flags are supported. */
4679 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
4680 {
4681 rc = VERR_INVALID_PARAMETER;
4682 goto out;
4683 }
4684
4685 /* Check remaining arguments. */
4686 if ( !VALID_PTR(pszFilename)
4687 || !*pszFilename
4688 || strchr(pszFilename, '"'))
4689 {
4690 rc = VERR_INVALID_PARAMETER;
4691 goto out;
4692 }
4693
4694 pImage = (PISCSIIMAGE)RTMemAllocZ(sizeof(ISCSIIMAGE));
4695 if (!pImage)
4696 {
4697 rc = VERR_NO_MEMORY;
4698 goto out;
4699 }
4700
4701 pImage->pszFilename = pszFilename;
4702 pImage->pszInitiatorName = NULL;
4703 pImage->pszTargetName = NULL;
4704 pImage->pszTargetAddress = NULL;
4705 pImage->pszInitiatorUsername = NULL;
4706 pImage->pbInitiatorSecret = NULL;
4707 pImage->pszTargetUsername = NULL;
4708 pImage->pbTargetSecret = NULL;
4709 pImage->paCurrReq = NULL;
4710 pImage->pvRecvPDUBuf = NULL;
4711 pImage->pszHostname = NULL;
4712 pImage->pVDIfsDisk = pVDIfsDisk;
4713 pImage->pVDIfsImage = pVDIfsImage;
4714 pImage->cLogRelErrors = 0;
4715
4716 rc = iscsiOpenImage(pImage, uOpenFlags);
4717 if (RT_SUCCESS(rc))
4718 {
4719 LogFlowFunc(("target %s cVolume %d, cbSector %d\n", pImage->pszTargetName, pImage->cVolume, pImage->cbSector));
4720 LogRel(("iSCSI: target address %s, target name %s, SCSI LUN %lld\n", pImage->pszTargetAddress, pImage->pszTargetName, pImage->LUN));
4721 *ppBackendData = pImage;
4722 }
4723 else
4724 RTMemFree(pImage);
4725
4726out:
4727 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
4728 return rc;
4729}
4730
4731/** @copydoc VDIMAGEBACKEND::pfnCreate */
4732static DECLCALLBACK(int) iscsiCreate(const char *pszFilename, uint64_t cbSize,
4733 unsigned uImageFlags, const char *pszComment,
4734 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
4735 PCRTUUID pUuid, unsigned uOpenFlags,
4736 unsigned uPercentStart, unsigned uPercentSpan,
4737 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
4738 PVDINTERFACE pVDIfsOperation, VDTYPE enmType,
4739 void **ppBackendData)
4740{
4741 RT_NOREF8(pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags);
4742 RT_NOREF7(uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, enmType, ppBackendData);
4743 LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p enmType=%u ppBackendData=%#p",
4744 pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, enmType, ppBackendData));
4745 int rc = VERR_NOT_SUPPORTED;
4746
4747 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
4748 return rc;
4749}
4750
4751/** @copydoc VDIMAGEBACKEND::pfnClose */
4752static DECLCALLBACK(int) iscsiClose(void *pBackendData, bool fDelete)
4753{
4754 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
4755 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4756 int rc;
4757
4758 Assert(!fDelete); /* This flag is unsupported. */
4759
4760 rc = iscsiFreeImage(pImage, fDelete);
4761 RTMemFree(pImage);
4762
4763 LogFlowFunc(("returns %Rrc\n", rc));
4764 return rc;
4765}
4766
4767/** @copydoc VDIMAGEBACKEND::pfnRead */
4768static DECLCALLBACK(int) iscsiRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
4769 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
4770{
4771 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4772 int rc = VINF_SUCCESS;
4773
4774 LogFlowFunc(("pBackendData=%p uOffset=%#llx pIoCtx=%#p cbToRead=%u pcbActuallyRead=%p\n",
4775 pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
4776
4777 if ( uOffset + cbToRead > pImage->cbSize
4778 || cbToRead == 0)
4779 return VERR_INVALID_PARAMETER;
4780
4781 /*
4782 * Clip read size to a value which is supported by the target.
4783 */
4784 cbToRead = RT_MIN(cbToRead, pImage->cbRecvDataLength);
4785
4786 unsigned cT2ISegs = 0;
4787 size_t cbSegs = 0;
4788
4789 /* Get the number of segments. */
4790 cbSegs = pImage->pIfIo->pfnIoCtxSegArrayCreate(pImage->pIfIo->Core.pvUser, pIoCtx,
4791 NULL, &cT2ISegs, cbToRead);
4792 Assert(cbSegs == cbToRead);
4793
4794 PSCSIREQ pReq = (PSCSIREQ)RTMemAllocZ(RT_OFFSETOF(SCSIREQ, aSegs[cT2ISegs]));
4795 if (RT_LIKELY(pReq))
4796 {
4797 uint64_t lba;
4798 uint16_t tls;
4799 uint8_t *pbCDB = &pReq->abCDB[0];
4800 size_t cbCDB;
4801
4802 lba = uOffset / pImage->cbSector;
4803 tls = (uint16_t)(cbToRead / pImage->cbSector);
4804
4805 cbSegs = pImage->pIfIo->pfnIoCtxSegArrayCreate(pImage->pIfIo->Core.pvUser, pIoCtx,
4806 &pReq->aSegs[0],
4807 &cT2ISegs, cbToRead);
4808 Assert(cbSegs == cbToRead);
4809
4810 if (pImage->cVolume < _4G)
4811 {
4812 cbCDB = 10;
4813 pbCDB[0] = SCSI_READ_10;
4814 pbCDB[1] = 0; /* reserved */
4815 pbCDB[2] = (lba >> 24) & 0xff;
4816 pbCDB[3] = (lba >> 16) & 0xff;
4817 pbCDB[4] = (lba >> 8) & 0xff;
4818 pbCDB[5] = lba & 0xff;
4819 pbCDB[6] = 0; /* reserved */
4820 pbCDB[7] = (tls >> 8) & 0xff;
4821 pbCDB[8] = tls & 0xff;
4822 pbCDB[9] = 0; /* control */
4823 }
4824 else
4825 {
4826 cbCDB = 16;
4827 pbCDB[0] = SCSI_READ_16;
4828 pbCDB[1] = 0; /* reserved */
4829 pbCDB[2] = (lba >> 56) & 0xff;
4830 pbCDB[3] = (lba >> 48) & 0xff;
4831 pbCDB[4] = (lba >> 40) & 0xff;
4832 pbCDB[5] = (lba >> 32) & 0xff;
4833 pbCDB[6] = (lba >> 24) & 0xff;
4834 pbCDB[7] = (lba >> 16) & 0xff;
4835 pbCDB[8] = (lba >> 8) & 0xff;
4836 pbCDB[9] = lba & 0xff;
4837 pbCDB[10] = 0; /* tls unused */
4838 pbCDB[11] = 0; /* tls unused */
4839 pbCDB[12] = (tls >> 8) & 0xff;
4840 pbCDB[13] = tls & 0xff;
4841 pbCDB[14] = 0; /* reserved */
4842 pbCDB[15] = 0; /* reserved */
4843 }
4844
4845 pReq->enmXfer = SCSIXFER_FROM_TARGET;
4846 pReq->cbCDB = cbCDB;
4847 pReq->cbI2TData = 0;
4848 pReq->paI2TSegs = NULL;
4849 pReq->cI2TSegs = 0;
4850 pReq->cbT2IData = cbToRead;
4851 pReq->paT2ISegs = &pReq->aSegs[pReq->cI2TSegs];
4852 pReq->cbSense = sizeof(pReq->abSense);
4853 pReq->cT2ISegs = cT2ISegs;
4854 pReq->pIoCtx = pIoCtx;
4855 pReq->cSenseRetries = 10;
4856 pReq->rcSense = VERR_READ_ERROR;
4857
4858 if (vdIfIoIntIoCtxIsSynchronous(pImage->pIfIo, pIoCtx))
4859 {
4860 rc = iscsiCommandSync(pImage, pReq, true, VERR_READ_ERROR);
4861 if (RT_FAILURE(rc))
4862 {
4863 LogFlow(("iscsiCommandSync(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
4864 *pcbActuallyRead = 0;
4865 }
4866 else
4867 *pcbActuallyRead = pReq->cbT2IData;
4868 }
4869 else
4870 {
4871 rc = iscsiCommandAsync(pImage, pReq, iscsiCommandAsyncComplete, pReq);
4872 if (RT_FAILURE(rc))
4873 AssertMsgFailed(("iscsiCommandAsync(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
4874 else
4875 {
4876 *pcbActuallyRead = cbToRead;
4877 return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
4878 }
4879 }
4880
4881 RTMemFree(pReq);
4882 }
4883 else
4884 rc = VERR_NO_MEMORY;
4885
4886 LogFlowFunc(("returns rc=%Rrc\n", rc));
4887 return rc;
4888}
4889
4890/** @copydoc VDIMAGEBACKEND::pfnWrite */
4891static DECLCALLBACK(int) iscsiWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
4892 PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
4893 size_t *pcbPostRead, unsigned fWrite)
4894{
4895 RT_NOREF3(pcbPreRead, pcbPostRead, fWrite);
4896 LogFlowFunc(("pBackendData=%p uOffset=%llu pIoCtx=%#p cbToWrite=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
4897 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite));
4898 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4899 int rc = VINF_SUCCESS;
4900
4901 AssertPtr(pImage);
4902 Assert(uOffset % 512 == 0);
4903 Assert(cbToWrite % 512 == 0);
4904
4905 if (uOffset + cbToWrite > pImage->cbSize)
4906 return VERR_INVALID_PARAMETER;
4907
4908 /*
4909 * Clip read size to a value which is supported by the target.
4910 */
4911 cbToWrite = RT_MIN(cbToWrite, pImage->cbSendDataLength);
4912
4913 unsigned cI2TSegs = 0;
4914 size_t cbSegs = 0;
4915
4916 /* Get the number of segments. */
4917 cbSegs = pImage->pIfIo->pfnIoCtxSegArrayCreate(pImage->pIfIo->Core.pvUser, pIoCtx,
4918 NULL, &cI2TSegs, cbToWrite);
4919 Assert(cbSegs == cbToWrite);
4920
4921 PSCSIREQ pReq = (PSCSIREQ)RTMemAllocZ(RT_OFFSETOF(SCSIREQ, aSegs[cI2TSegs]));
4922 if (RT_LIKELY(pReq))
4923 {
4924 uint64_t lba;
4925 uint16_t tls;
4926 uint8_t *pbCDB = &pReq->abCDB[0];
4927 size_t cbCDB;
4928
4929 lba = uOffset / pImage->cbSector;
4930 tls = (uint16_t)(cbToWrite / pImage->cbSector);
4931
4932 cbSegs = pImage->pIfIo->pfnIoCtxSegArrayCreate(pImage->pIfIo->Core.pvUser, pIoCtx,
4933 &pReq->aSegs[0],
4934 &cI2TSegs, cbToWrite);
4935 Assert(cbSegs == cbToWrite);
4936
4937 if (pImage->cVolume < _4G)
4938 {
4939 cbCDB = 10;
4940 pbCDB[0] = SCSI_WRITE_10;
4941 pbCDB[1] = 0; /* reserved */
4942 pbCDB[2] = (lba >> 24) & 0xff;
4943 pbCDB[3] = (lba >> 16) & 0xff;
4944 pbCDB[4] = (lba >> 8) & 0xff;
4945 pbCDB[5] = lba & 0xff;
4946 pbCDB[6] = 0; /* reserved */
4947 pbCDB[7] = (tls >> 8) & 0xff;
4948 pbCDB[8] = tls & 0xff;
4949 pbCDB[9] = 0; /* control */
4950 }
4951 else
4952 {
4953 cbCDB = 16;
4954 pbCDB[0] = SCSI_WRITE_16;
4955 pbCDB[1] = 0; /* reserved */
4956 pbCDB[2] = (lba >> 56) & 0xff;
4957 pbCDB[3] = (lba >> 48) & 0xff;
4958 pbCDB[4] = (lba >> 40) & 0xff;
4959 pbCDB[5] = (lba >> 32) & 0xff;
4960 pbCDB[6] = (lba >> 24) & 0xff;
4961 pbCDB[7] = (lba >> 16) & 0xff;
4962 pbCDB[8] = (lba >> 8) & 0xff;
4963 pbCDB[9] = lba & 0xff;
4964 pbCDB[10] = 0; /* tls unused */
4965 pbCDB[11] = 0; /* tls unused */
4966 pbCDB[12] = (tls >> 8) & 0xff;
4967 pbCDB[13] = tls & 0xff;
4968 pbCDB[14] = 0; /* reserved */
4969 pbCDB[15] = 0; /* reserved */
4970 }
4971
4972 pReq->enmXfer = SCSIXFER_TO_TARGET;
4973 pReq->cbCDB = cbCDB;
4974 pReq->cbI2TData = cbToWrite;
4975 pReq->paI2TSegs = &pReq->aSegs[0];
4976 pReq->cI2TSegs = cI2TSegs;
4977 pReq->cbT2IData = 0;
4978 pReq->paT2ISegs = NULL;
4979 pReq->cT2ISegs = 0;
4980 pReq->cbSense = sizeof(pReq->abSense);
4981 pReq->pIoCtx = pIoCtx;
4982 pReq->cSenseRetries = 10;
4983 pReq->rcSense = VERR_WRITE_ERROR;
4984
4985 if (vdIfIoIntIoCtxIsSynchronous(pImage->pIfIo, pIoCtx))
4986 {
4987 rc = iscsiCommandSync(pImage, pReq, true, VERR_WRITE_ERROR);
4988 if (RT_FAILURE(rc))
4989 {
4990 LogFlow(("iscsiCommandSync(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
4991 *pcbWriteProcess = 0;
4992 }
4993 else
4994 *pcbWriteProcess = cbToWrite;
4995 }
4996 else
4997 {
4998 rc = iscsiCommandAsync(pImage, pReq, iscsiCommandAsyncComplete, pReq);
4999 if (RT_FAILURE(rc))
5000 AssertMsgFailed(("iscsiCommandAsync(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
5001 else
5002 {
5003 *pcbWriteProcess = cbToWrite;
5004 return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
5005 }
5006 }
5007
5008 RTMemFree(pReq);
5009 }
5010 else
5011 rc = VERR_NO_MEMORY;
5012
5013 LogFlowFunc(("returns rc=%Rrc\n", rc));
5014 return rc;
5015}
5016
5017/** @copydoc VDIMAGEBACKEND::pfnFlush */
5018static DECLCALLBACK(int) iscsiFlush(void *pBackendData, PVDIOCTX pIoCtx)
5019{
5020 LogFlowFunc(("pBackendData=%p pIoCtx=%#p\n", pBackendData, pIoCtx));
5021 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5022 int rc = VINF_SUCCESS;
5023
5024 PSCSIREQ pReq = (PSCSIREQ)RTMemAllocZ(sizeof(SCSIREQ));
5025 if (RT_LIKELY(pReq))
5026 {
5027 uint8_t *pbCDB = &pReq->abCDB[0];
5028
5029 pbCDB[0] = SCSI_SYNCHRONIZE_CACHE;
5030 pbCDB[1] = 0; /* reserved */
5031 pbCDB[2] = 0; /* reserved */
5032 pbCDB[3] = 0; /* reserved */
5033 pbCDB[4] = 0; /* reserved */
5034 pbCDB[5] = 0; /* reserved */
5035 pbCDB[6] = 0; /* reserved */
5036 pbCDB[7] = 0; /* reserved */
5037 pbCDB[8] = 0; /* reserved */
5038 pbCDB[9] = 0; /* control */
5039
5040 pReq->enmXfer = SCSIXFER_NONE;
5041 pReq->cbCDB = 10;
5042 pReq->cbI2TData = 0;
5043 pReq->paI2TSegs = NULL;
5044 pReq->cI2TSegs = 0;
5045 pReq->cbT2IData = 0;
5046 pReq->paT2ISegs = NULL;
5047 pReq->cT2ISegs = 0;
5048 pReq->cbSense = sizeof(pReq->abSense);
5049 pReq->pIoCtx = pIoCtx;
5050 pReq->cSenseRetries = 0;
5051 pReq->rcSense = VINF_SUCCESS;
5052
5053 if (vdIfIoIntIoCtxIsSynchronous(pImage->pIfIo, pIoCtx))
5054 {
5055 rc = iscsiCommandSync(pImage, pReq, false, VINF_SUCCESS);
5056 if (RT_FAILURE(rc))
5057 AssertMsgFailed(("iscsiCommand(%s) -> %Rrc\n", pImage->pszTargetName, rc));
5058 }
5059 else
5060 {
5061 rc = iscsiCommandAsync(pImage, pReq, iscsiCommandAsyncComplete, pReq);
5062 if (RT_FAILURE(rc))
5063 AssertMsgFailed(("iscsiCommand(%s) -> %Rrc\n", pImage->pszTargetName, rc));
5064 else
5065 return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
5066 }
5067
5068 RTMemFree(pReq);
5069 }
5070 else
5071 rc = VERR_NO_MEMORY;
5072
5073 LogFlowFunc(("returns rc=%Rrc\n", rc));
5074 return rc;
5075}
5076
5077/** @copydoc VDIMAGEBACKEND::pfnGetVersion */
5078static DECLCALLBACK(unsigned) iscsiGetVersion(void *pBackendData)
5079{
5080 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
5081 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5082
5083 Assert(pImage);
5084 NOREF(pImage);
5085
5086 return 0;
5087}
5088
5089/** @copydoc VDIMAGEBACKEND::pfnGetSectorSize */
5090static DECLCALLBACK(uint32_t) iscsiGetSectorSize(void *pBackendData)
5091{
5092 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
5093 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5094
5095 Assert(pImage);
5096
5097 if (pImage)
5098 return pImage->cbSector;
5099 else
5100 return 0;
5101}
5102
5103/** @copydoc VDIMAGEBACKEND::pfnGetSize */
5104static DECLCALLBACK(uint64_t) iscsiGetSize(void *pBackendData)
5105{
5106 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
5107 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5108
5109 Assert(pImage);
5110
5111 if (pImage)
5112 return pImage->cbSize;
5113 else
5114 return 0;
5115}
5116
5117/** @copydoc VDIMAGEBACKEND::pfnGetFileSize */
5118static DECLCALLBACK(uint64_t) iscsiGetFileSize(void *pBackendData)
5119{
5120 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
5121 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5122
5123 Assert(pImage);
5124 NOREF(pImage);
5125
5126 if (pImage)
5127 return pImage->cbSize;
5128 else
5129 return 0;
5130}
5131
5132/** @copydoc VDIMAGEBACKEND::pfnGetPCHSGeometry */
5133static DECLCALLBACK(int) iscsiGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
5134{
5135 RT_NOREF1(pPCHSGeometry);
5136 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
5137 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5138 int rc;
5139
5140 Assert(pImage);
5141
5142 if (pImage)
5143 rc = VERR_VD_GEOMETRY_NOT_SET;
5144 else
5145 rc = VERR_VD_NOT_OPENED;
5146
5147 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
5148 return rc;
5149}
5150
5151/** @copydoc VDIMAGEBACKEND::pfnSetPCHSGeometry */
5152static DECLCALLBACK(int) iscsiSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
5153{
5154 RT_NOREF1(pPCHSGeometry);
5155 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
5156 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5157 int rc;
5158
5159 Assert(pImage);
5160
5161 if (pImage)
5162 {
5163 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
5164 {
5165 rc = VERR_VD_IMAGE_READ_ONLY;
5166 goto out;
5167 }
5168 rc = VERR_VD_GEOMETRY_NOT_SET;
5169 }
5170 else
5171 rc = VERR_VD_NOT_OPENED;
5172
5173out:
5174 LogFlowFunc(("returns %Rrc\n", rc));
5175 return rc;
5176}
5177
5178/** @copydoc VDIMAGEBACKEND::pfnGetLCHSGeometry */
5179static DECLCALLBACK(int) iscsiGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
5180{
5181 RT_NOREF1(pLCHSGeometry);
5182 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
5183 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5184 int rc;
5185
5186 Assert(pImage);
5187
5188 if (pImage)
5189 rc = VERR_VD_GEOMETRY_NOT_SET;
5190 else
5191 rc = VERR_VD_NOT_OPENED;
5192
5193 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
5194 return rc;
5195}
5196
5197/** @copydoc VDIMAGEBACKEND::pfnSetLCHSGeometry */
5198static DECLCALLBACK(int) iscsiSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
5199{
5200 RT_NOREF1(pLCHSGeometry);
5201 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
5202 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5203 int rc;
5204
5205 Assert(pImage);
5206
5207 if (pImage)
5208 {
5209 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
5210 {
5211 rc = VERR_VD_IMAGE_READ_ONLY;
5212 goto out;
5213 }
5214 rc = VERR_VD_GEOMETRY_NOT_SET;
5215 }
5216 else
5217 rc = VERR_VD_NOT_OPENED;
5218
5219out:
5220 LogFlowFunc(("returns %Rrc\n", rc));
5221 return rc;
5222}
5223
5224/** @copydoc VDIMAGEBACKEND::pfnGetImageFlags */
5225static DECLCALLBACK(unsigned) iscsiGetImageFlags(void *pBackendData)
5226{
5227 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
5228 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5229 unsigned uImageFlags;
5230
5231 Assert(pImage);
5232 NOREF(pImage);
5233
5234 uImageFlags = VD_IMAGE_FLAGS_FIXED;
5235
5236 LogFlowFunc(("returns %#x\n", uImageFlags));
5237 return uImageFlags;
5238}
5239
5240/** @copydoc VDIMAGEBACKEND::pfnGetOpenFlags */
5241static DECLCALLBACK(unsigned) iscsiGetOpenFlags(void *pBackendData)
5242{
5243 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
5244 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5245 unsigned uOpenFlags;
5246
5247 Assert(pImage);
5248
5249 if (pImage)
5250 uOpenFlags = pImage->uOpenFlags;
5251 else
5252 uOpenFlags = 0;
5253
5254 LogFlowFunc(("returns %#x\n", uOpenFlags));
5255 return uOpenFlags;
5256}
5257
5258/** @copydoc VDIMAGEBACKEND::pfnSetOpenFlags */
5259static DECLCALLBACK(int) iscsiSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
5260{
5261 LogFlowFunc(("pBackendData=%#p uOpenFlags=%#x\n", pBackendData, uOpenFlags));
5262 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5263 int rc = VINF_SUCCESS;
5264
5265 /* Image must be opened and the new flags must be valid. */
5266 AssertReturn(pImage && !(uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
5267 | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
5268 | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)),
5269 VERR_INVALID_PARAMETER);
5270
5271 /*
5272 * A read/write -> readonly transition is always possible,
5273 * for the reverse direction check that the target didn't present itself
5274 * as readonly during the first attach.
5275 */
5276 if ( !(uOpenFlags & VD_OPEN_FLAGS_READONLY)
5277 && (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
5278 && pImage->fTargetReadOnly)
5279 rc = VERR_VD_IMAGE_READ_ONLY;
5280 else
5281 {
5282 pImage->uOpenFlags = uOpenFlags;
5283 pImage->fTryReconnect = true;
5284 }
5285
5286 LogFlowFunc(("returns %Rrc\n", rc));
5287 return rc;
5288}
5289
5290/** @copydoc VDIMAGEBACKEND::pfnGetComment */
5291static DECLCALLBACK(int) iscsiGetComment(void *pBackendData, char *pszComment,
5292 size_t cbComment)
5293{
5294 RT_NOREF2(pszComment, cbComment);
5295 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
5296 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5297 int rc;
5298
5299 Assert(pImage);
5300
5301 if (pImage)
5302 rc = VERR_NOT_SUPPORTED;
5303 else
5304 rc = VERR_VD_NOT_OPENED;
5305
5306 LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
5307 return rc;
5308}
5309
5310/** @copydoc VDIMAGEBACKEND::pfnSetComment */
5311static DECLCALLBACK(int) iscsiSetComment(void *pBackendData, const char *pszComment)
5312{
5313 RT_NOREF1(pszComment);
5314 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
5315 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5316 int rc;
5317
5318 Assert(pImage);
5319
5320 if (pImage)
5321 {
5322 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
5323 rc = VERR_NOT_SUPPORTED;
5324 else
5325 rc = VERR_VD_IMAGE_READ_ONLY;
5326 }
5327 else
5328 rc = VERR_VD_NOT_OPENED;
5329
5330 LogFlowFunc(("returns %Rrc\n", rc));
5331 return rc;
5332}
5333
5334/** @copydoc VDIMAGEBACKEND::pfnGetUuid */
5335static DECLCALLBACK(int) iscsiGetUuid(void *pBackendData, PRTUUID pUuid)
5336{
5337 RT_NOREF1(pUuid);
5338 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
5339 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5340 int rc;
5341
5342 Assert(pImage);
5343
5344 if (pImage)
5345 rc = VERR_NOT_SUPPORTED;
5346 else
5347 rc = VERR_VD_NOT_OPENED;
5348
5349 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
5350 return rc;
5351}
5352
5353/** @copydoc VDIMAGEBACKEND::pfnSetUuid */
5354static DECLCALLBACK(int) iscsiSetUuid(void *pBackendData, PCRTUUID pUuid)
5355{
5356 RT_NOREF1(pUuid);
5357 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
5358 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5359 int rc;
5360
5361 LogFlowFunc(("%RTuuid\n", pUuid));
5362 Assert(pImage);
5363
5364 if (pImage)
5365 {
5366 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
5367 rc = VERR_NOT_SUPPORTED;
5368 else
5369 rc = VERR_VD_IMAGE_READ_ONLY;
5370 }
5371 else
5372 rc = VERR_VD_NOT_OPENED;
5373
5374 LogFlowFunc(("returns %Rrc\n", rc));
5375 return rc;
5376}
5377
5378/** @copydoc VDIMAGEBACKEND::pfnGetModificationUuid */
5379static DECLCALLBACK(int) iscsiGetModificationUuid(void *pBackendData, PRTUUID pUuid)
5380{
5381 RT_NOREF1(pUuid);
5382 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
5383 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5384 int rc;
5385
5386 Assert(pImage);
5387
5388 if (pImage)
5389 rc = VERR_NOT_SUPPORTED;
5390 else
5391 rc = VERR_VD_NOT_OPENED;
5392
5393 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
5394 return rc;
5395}
5396
5397/** @copydoc VDIMAGEBACKEND::pfnSetModificationUuid */
5398static DECLCALLBACK(int) iscsiSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
5399{
5400 RT_NOREF1(pUuid);
5401 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
5402 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5403 int rc;
5404
5405 LogFlowFunc(("%RTuuid\n", pUuid));
5406 Assert(pImage);
5407
5408 if (pImage)
5409 {
5410 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
5411 rc = VERR_NOT_SUPPORTED;
5412 else
5413 rc = VERR_VD_IMAGE_READ_ONLY;
5414 }
5415 else
5416 rc = VERR_VD_NOT_OPENED;
5417
5418 LogFlowFunc(("returns %Rrc\n", rc));
5419 return rc;
5420}
5421
5422/** @copydoc VDIMAGEBACKEND::pfnGetParentUuid */
5423static DECLCALLBACK(int) iscsiGetParentUuid(void *pBackendData, PRTUUID pUuid)
5424{
5425 RT_NOREF1(pUuid);
5426 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
5427 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5428 int rc;
5429
5430 Assert(pImage);
5431
5432 if (pImage)
5433 rc = VERR_NOT_SUPPORTED;
5434 else
5435 rc = VERR_VD_NOT_OPENED;
5436
5437 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
5438 return rc;
5439}
5440
5441/** @copydoc VDIMAGEBACKEND::pfnSetParentUuid */
5442static DECLCALLBACK(int) iscsiSetParentUuid(void *pBackendData, PCRTUUID pUuid)
5443{
5444 RT_NOREF1(pUuid);
5445 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
5446 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5447 int rc;
5448
5449 LogFlowFunc(("%RTuuid\n", pUuid));
5450 Assert(pImage);
5451
5452 if (pImage)
5453 {
5454 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
5455 rc = VERR_NOT_SUPPORTED;
5456 else
5457 rc = VERR_VD_IMAGE_READ_ONLY;
5458 }
5459 else
5460 rc = VERR_VD_NOT_OPENED;
5461
5462 LogFlowFunc(("returns %Rrc\n", rc));
5463 return rc;
5464}
5465
5466/** @copydoc VDIMAGEBACKEND::pfnGetParentModificationUuid */
5467static DECLCALLBACK(int) iscsiGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
5468{
5469 RT_NOREF1(pUuid);
5470 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
5471 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5472 int rc;
5473
5474 Assert(pImage);
5475
5476 if (pImage)
5477 rc = VERR_NOT_SUPPORTED;
5478 else
5479 rc = VERR_VD_NOT_OPENED;
5480
5481 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
5482 return rc;
5483}
5484
5485/** @copydoc VDIMAGEBACKEND::pfnSetParentModificationUuid */
5486static DECLCALLBACK(int) iscsiSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
5487{
5488 RT_NOREF1(pUuid);
5489 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
5490 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5491 int rc;
5492
5493 LogFlowFunc(("%RTuuid\n", pUuid));
5494 Assert(pImage);
5495
5496 if (pImage)
5497 {
5498 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
5499 rc = VERR_NOT_SUPPORTED;
5500 else
5501 rc = VERR_VD_IMAGE_READ_ONLY;
5502 }
5503 else
5504 rc = VERR_VD_NOT_OPENED;
5505
5506 LogFlowFunc(("returns %Rrc\n", rc));
5507 return rc;
5508}
5509
5510/** @copydoc VDIMAGEBACKEND::pfnDump */
5511static DECLCALLBACK(void) iscsiDump(void *pBackendData)
5512{
5513 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5514
5515 Assert(pImage);
5516 if (pImage)
5517 {
5518 /** @todo put something useful here */
5519 vdIfErrorMessage(pImage->pIfError, "Header: cVolume=%u\n", pImage->cVolume);
5520 }
5521}
5522
5523/** @copydoc VDIMAGEBACKEND::pfnComposeLocation */
5524static DECLCALLBACK(int) iscsiComposeLocation(PVDINTERFACE pConfig, char **pszLocation)
5525{
5526 char *pszTarget = NULL;
5527 char *pszLUN = NULL;
5528 char *pszAddress = NULL;
5529 int rc = VDCFGQueryStringAlloc(VDIfConfigGet(pConfig), "TargetName", &pszTarget);
5530 if (RT_SUCCESS(rc))
5531 {
5532 rc = VDCFGQueryStringAlloc(VDIfConfigGet(pConfig), "LUN", &pszLUN);
5533 if (RT_SUCCESS(rc))
5534 {
5535 rc = VDCFGQueryStringAlloc(VDIfConfigGet(pConfig), "TargetAddress", &pszAddress);
5536 if (RT_SUCCESS(rc))
5537 {
5538 if (RTStrAPrintf(pszLocation, "iscsi://%s/%s/%s",
5539 pszAddress, pszTarget, pszLUN) < 0)
5540 rc = VERR_NO_MEMORY;
5541 }
5542 }
5543 }
5544 RTMemFree(pszTarget);
5545 RTMemFree(pszLUN);
5546 RTMemFree(pszAddress);
5547 return rc;
5548}
5549
5550/** @copydoc VDIMAGEBACKEND::pfnComposeName */
5551static DECLCALLBACK(int) iscsiComposeName(PVDINTERFACE pConfig, char **pszName)
5552{
5553 char *pszTarget = NULL;
5554 char *pszLUN = NULL;
5555 char *pszAddress = NULL;
5556 int rc = VDCFGQueryStringAlloc(VDIfConfigGet(pConfig), "TargetName", &pszTarget);
5557 if (RT_SUCCESS(rc))
5558 {
5559 rc = VDCFGQueryStringAlloc(VDIfConfigGet(pConfig), "LUN", &pszLUN);
5560 if (RT_SUCCESS(rc))
5561 {
5562 rc = VDCFGQueryStringAlloc(VDIfConfigGet(pConfig), "TargetAddress", &pszAddress);
5563 if (RT_SUCCESS(rc))
5564 {
5565 /** @todo think about a nicer looking location scheme for iSCSI */
5566 if (RTStrAPrintf(pszName, "%s/%s/%s",
5567 pszAddress, pszTarget, pszLUN) < 0)
5568 rc = VERR_NO_MEMORY;
5569 }
5570 }
5571 }
5572 RTMemFree(pszTarget);
5573 RTMemFree(pszLUN);
5574 RTMemFree(pszAddress);
5575
5576 return rc;
5577}
5578
5579
5580const VDIMAGEBACKEND g_ISCSIBackend =
5581{
5582 /* pszBackendName */
5583 "iSCSI",
5584 /* cbSize */
5585 sizeof(VDIMAGEBACKEND),
5586 /* uBackendCaps */
5587 VD_CAP_CONFIG | VD_CAP_TCPNET | VD_CAP_ASYNC,
5588 /* papszFileExtensions */
5589 NULL,
5590 /* paConfigInfo */
5591 s_iscsiConfigInfo,
5592 /* pfnCheckIfValid */
5593 iscsiCheckIfValid,
5594 /* pfnOpen */
5595 iscsiOpen,
5596 /* pfnCreate */
5597 iscsiCreate,
5598 /* pfnRename */
5599 NULL,
5600 /* pfnClose */
5601 iscsiClose,
5602 /* pfnRead */
5603 iscsiRead,
5604 /* pfnWrite */
5605 iscsiWrite,
5606 /* pfnFlush */
5607 iscsiFlush,
5608 /* pfnDiscard */
5609 NULL,
5610 /* pfnGetVersion */
5611 iscsiGetVersion,
5612 /* pfnGetSectorSize */
5613 iscsiGetSectorSize,
5614 /* pfnGetSize */
5615 iscsiGetSize,
5616 /* pfnGetFileSize */
5617 iscsiGetFileSize,
5618 /* pfnGetPCHSGeometry */
5619 iscsiGetPCHSGeometry,
5620 /* pfnSetPCHSGeometry */
5621 iscsiSetPCHSGeometry,
5622 /* pfnGetLCHSGeometry */
5623 iscsiGetLCHSGeometry,
5624 /* pfnSetLCHSGeometry */
5625 iscsiSetLCHSGeometry,
5626 /* pfnGetImageFlags */
5627 iscsiGetImageFlags,
5628 /* pfnGetOpenFlags */
5629 iscsiGetOpenFlags,
5630 /* pfnSetOpenFlags */
5631 iscsiSetOpenFlags,
5632 /* pfnGetComment */
5633 iscsiGetComment,
5634 /* pfnSetComment */
5635 iscsiSetComment,
5636 /* pfnGetUuid */
5637 iscsiGetUuid,
5638 /* pfnSetUuid */
5639 iscsiSetUuid,
5640 /* pfnGetModificationUuid */
5641 iscsiGetModificationUuid,
5642 /* pfnSetModificationUuid */
5643 iscsiSetModificationUuid,
5644 /* pfnGetParentUuid */
5645 iscsiGetParentUuid,
5646 /* pfnSetParentUuid */
5647 iscsiSetParentUuid,
5648 /* pfnGetParentModificationUuid */
5649 iscsiGetParentModificationUuid,
5650 /* pfnSetParentModificationUuid */
5651 iscsiSetParentModificationUuid,
5652 /* pfnDump */
5653 iscsiDump,
5654 /* pfnGetTimestamp */
5655 NULL,
5656 /* pfnGetParentTimestamp */
5657 NULL,
5658 /* pfnSetParentTimestamp */
5659 NULL,
5660 /* pfnGetParentFilename */
5661 NULL,
5662 /* pfnSetParentFilename */
5663 NULL,
5664 /* pfnComposeLocation */
5665 iscsiComposeLocation,
5666 /* pfnComposeName */
5667 iscsiComposeName,
5668 /* pfnCompact */
5669 NULL,
5670 /* pfnResize */
5671 NULL,
5672 /* pfnRepair */
5673 NULL,
5674 /* pfnTraverseMetadata */
5675 NULL
5676};
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette