VirtualBox

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

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

Storage: Doxygen fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 200.0 KB
Line 
1/* $Id: ISCSI.cpp 64272 2016-10-14 08:25:05Z 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 * Returns a human readable version of the given initiator login error detail.
1212 *
1213 * @returns String with the error detail.
1214 * @param u8Detail The detail indicator from the response.
1215 */
1216static const char *iscsiGetLoginErrorDetail(uint8_t u8Detail)
1217{
1218 const char *pszDetail = NULL;
1219
1220 switch (u8Detail)
1221 {
1222 case 0x00:
1223 pszDetail = "Miscelleanous iSCSI intiaitor error";
1224 break;
1225 case 0x01:
1226 pszDetail = "Authentication failure";
1227 break;
1228 case 0x02:
1229 pszDetail = "Authorization failure";
1230 break;
1231 case 0x03:
1232 pszDetail = "Not found";
1233 break;
1234 case 0x04:
1235 pszDetail = "Target removed";
1236 break;
1237 case 0x05:
1238 pszDetail = "Unsupported version";
1239 break;
1240 case 0x06:
1241 pszDetail = "Too many connections";
1242 break;
1243 case 0x07:
1244 pszDetail = "Missing parameter";
1245 break;
1246 case 0x08:
1247 pszDetail = "Can't include in session";
1248 break;
1249 case 0x09:
1250 pszDetail = "Session type not supported";
1251 break;
1252 case 0x0a:
1253 pszDetail = "Session does not exist";
1254 break;
1255 case 0x0b:
1256 pszDetail = "Invalid request type during login";
1257 break;
1258 default:
1259 pszDetail = "Unknown status detail";
1260 }
1261
1262 return pszDetail;
1263}
1264
1265/**
1266 * Attempts one login attempt to the given target.
1267 *
1268 * @returns VBox status code.
1269 * @retval VINF_TRY_AGAIN when getting redirected and having to start over.
1270 * @retval VERR_TRY_AGAIN in case the connection was lost while receiving a reply
1271 * from the target and the login attempt can be repeated.
1272 * @param pImage The iSCSI connection state to be used.
1273 */
1274static int iscsiLogin(PISCSIIMAGE pImage)
1275{
1276 int rc = VINF_SUCCESS;
1277 uint32_t itt;
1278 uint32_t csg, nsg, substate;
1279 uint64_t isid_tsih;
1280 uint8_t bBuf[4096]; /* Should be large enough even for large authentication values. */
1281 size_t cbBuf;
1282 bool transit;
1283 uint8_t pbChallenge[1024]; /* RFC3720 specifies this as maximum. */
1284 size_t cbChallenge = 0; /* shut up gcc */
1285 uint8_t bChapIdx = 0; /* (MSC is used uninitialized) */
1286 uint8_t aResponse[RTMD5HASHSIZE];
1287 uint32_t cnISCSIReq = 0;
1288 ISCSIREQ aISCSIReq[4];
1289 uint32_t aReqBHS[12];
1290 uint32_t cnISCSIRes = 0;
1291 ISCSIRES aISCSIRes[2];
1292 uint32_t aResBHS[12];
1293 char *pszNext;
1294 bool fParameterNeg = true;
1295 pImage->cbRecvDataLength = ISCSI_DATA_LENGTH_MAX;
1296 pImage->cbSendDataLength = RT_MIN(ISCSI_DATA_LENGTH_MAX, pImage->cbWriteSplit);
1297 char szMaxDataLength[16];
1298 RTStrPrintf(szMaxDataLength, sizeof(szMaxDataLength), "%u", ISCSI_DATA_LENGTH_MAX);
1299 ISCSIPARAMETER aParameterNeg[] =
1300 {
1301 { "HeaderDigest", "None", 0 },
1302 { "DataDigest", "None", 0 },
1303 { "MaxConnections", "1", 0 },
1304 { "InitialR2T", "No", 0 },
1305 { "ImmediateData", "Yes", 0 },
1306 { "MaxRecvDataSegmentLength", szMaxDataLength, 0 },
1307 { "MaxBurstLength", szMaxDataLength, 0 },
1308 { "FirstBurstLength", szMaxDataLength, 0 },
1309 { "DefaultTime2Wait", "0", 0 },
1310 { "DefaultTime2Retain", "60", 0 },
1311 { "DataPDUInOrder", "Yes", 0 },
1312 { "DataSequenceInOrder", "Yes", 0 },
1313 { "ErrorRecoveryLevel", "0", 0 },
1314 { "MaxOutstandingR2T", "1", 0 }
1315 };
1316
1317 if (!iscsiIsClientConnected(pImage))
1318 {
1319 rc = iscsiTransportOpen(pImage);
1320 if (RT_FAILURE(rc))
1321 return rc;
1322 }
1323
1324 pImage->state = ISCSISTATE_IN_LOGIN;
1325 pImage->ITT = 1;
1326 pImage->FirstRecvPDU = true;
1327 pImage->CmdSN = 1;
1328 pImage->ExpCmdSN = 0;
1329 pImage->MaxCmdSN = 1;
1330 pImage->ExpStatSN = 0;
1331
1332 /*
1333 * Send login request to target.
1334 */
1335 itt = iscsiNewITT(pImage);
1336 csg = 0;
1337 nsg = 0;
1338 substate = 0;
1339 isid_tsih = pImage->ISID << 16; /* TSIH field currently always 0 */
1340
1341 do
1342 {
1343 transit = false;
1344 cbBuf = 0;
1345 /* Handle all cases with a single switch statement. */
1346 switch (csg << 8 | substate)
1347 {
1348 case 0x0000: /* security negotiation, step 0: propose authentication. */
1349 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "SessionType", "Normal", 0);
1350 if (RT_FAILURE(rc))
1351 break;
1352 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "InitiatorName", pImage->pszInitiatorName, 0);
1353 if (RT_FAILURE(rc))
1354 break;
1355 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "TargetName", pImage->pszTargetName, 0);
1356 if (RT_FAILURE(rc))
1357 break;
1358 if (pImage->pszInitiatorUsername == NULL)
1359 {
1360 /* No authentication. Immediately switch to next phase. */
1361 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "AuthMethod", "None", 0);
1362 if (RT_FAILURE(rc))
1363 break;
1364 nsg = 1;
1365 transit = true;
1366 }
1367 else
1368 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "AuthMethod", "CHAP,None", 0);
1369 break;
1370 case 0x0001: /* security negotiation, step 1: propose CHAP_MD5 variant. */
1371 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_A", "5", 0);
1372 break;
1373 case 0x0002: /* security negotiation, step 2: send authentication info. */
1374 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_N", pImage->pszInitiatorUsername, 0);
1375 if (RT_FAILURE(rc))
1376 break;
1377 chap_md5_compute_response(aResponse, bChapIdx, pbChallenge, cbChallenge,
1378 pImage->pbInitiatorSecret, pImage->cbInitiatorSecret);
1379 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_R", (const char *)aResponse, RTMD5HASHSIZE);
1380 if (RT_FAILURE(rc))
1381 break;
1382 nsg = 1;
1383 transit = true;
1384 break;
1385 case 0x0100: /* login operational negotiation, step 0: set parameters. */
1386 if (fParameterNeg)
1387 {
1388 for (unsigned i = 0; i < RT_ELEMENTS(aParameterNeg); i++)
1389 {
1390 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf,
1391 aParameterNeg[i].pszParamName,
1392 aParameterNeg[i].pszParamValue,
1393 aParameterNeg[i].cbParamValue);
1394 if (RT_FAILURE(rc))
1395 break;
1396 }
1397 fParameterNeg = false;
1398 }
1399
1400 nsg = 3;
1401 transit = true;
1402 break;
1403 case 0x0300: /* full feature phase. */
1404 default:
1405 /* Should never come here. */
1406 AssertMsgFailed(("send: Undefined login state %d substate %d\n", csg, substate));
1407 break;
1408 }
1409
1410 if (RT_FAILURE(rc))
1411 break;
1412
1413 aReqBHS[0] = RT_H2N_U32( ISCSI_IMMEDIATE_DELIVERY_BIT
1414 | (csg << ISCSI_CSG_SHIFT)
1415 | (transit ? (nsg << ISCSI_NSG_SHIFT | ISCSI_TRANSIT_BIT) : 0)
1416 | ISCSI_MY_VERSION /* Minimum version. */
1417 | (ISCSI_MY_VERSION << 8) /* Maximum version. */
1418 | ISCSIOP_LOGIN_REQ); /* C=0 */
1419 aReqBHS[1] = RT_H2N_U32((uint32_t)cbBuf); /* TotalAHSLength=0 */
1420 aReqBHS[2] = RT_H2N_U32(isid_tsih >> 32);
1421 aReqBHS[3] = RT_H2N_U32(isid_tsih & 0xffffffff);
1422 aReqBHS[4] = itt;
1423 aReqBHS[5] = RT_H2N_U32(1 << 16); /* CID=1,reserved */
1424 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
1425 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
1426 aReqBHS[8] = 0; /* reserved */
1427 aReqBHS[9] = 0; /* reserved */
1428 aReqBHS[10] = 0; /* reserved */
1429 aReqBHS[11] = 0; /* reserved */
1430
1431 cnISCSIReq = 0;
1432 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
1433 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
1434 cnISCSIReq++;
1435
1436 aISCSIReq[cnISCSIReq].pcvSeg = bBuf;
1437 aISCSIReq[cnISCSIReq].cbSeg = cbBuf;
1438 cnISCSIReq++;
1439
1440 rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_NO_REATTACH);
1441 if (RT_SUCCESS(rc))
1442 {
1443 ISCSIOPCODE cmd;
1444 ISCSILOGINSTATUSCLASS loginStatusClass;
1445
1446 cnISCSIRes = 0;
1447 aISCSIRes[cnISCSIRes].pvSeg = aResBHS;
1448 aISCSIRes[cnISCSIRes].cbSeg = sizeof(aResBHS);
1449 cnISCSIRes++;
1450 aISCSIRes[cnISCSIRes].pvSeg = bBuf;
1451 aISCSIRes[cnISCSIRes].cbSeg = sizeof(bBuf);
1452 cnISCSIRes++;
1453
1454 rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes, ISCSIPDU_NO_REATTACH);
1455 if (RT_FAILURE(rc))
1456 {
1457 /*
1458 * We lost connection to the target while receiving the answer,
1459 * start from the beginning.
1460 */
1461 if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
1462 rc = VERR_TRY_AGAIN;
1463 break;
1464 }
1465
1466 /** @todo collect partial login responses with Continue bit set. */
1467 Assert(aISCSIRes[0].pvSeg == aResBHS);
1468 Assert(aISCSIRes[0].cbSeg >= ISCSI_BHS_SIZE);
1469 Assert((RT_N2H_U32(aResBHS[0]) & ISCSI_CONTINUE_BIT) == 0);
1470
1471 cmd = (ISCSIOPCODE)(RT_N2H_U32(aResBHS[0]) & ISCSIOP_MASK);
1472 if (cmd == ISCSIOP_LOGIN_RES)
1473 {
1474 if ((RT_N2H_U32(aResBHS[0]) & 0xff) != ISCSI_MY_VERSION)
1475 {
1476 iscsiTransportClose(pImage);
1477 rc = VERR_PARSE_ERROR;
1478 break; /* Give up immediately, as a RFC violation in version fields is very serious. */
1479 }
1480
1481 loginStatusClass = (ISCSILOGINSTATUSCLASS)(RT_N2H_U32(aResBHS[9]) >> 24);
1482 switch (loginStatusClass)
1483 {
1484 case ISCSI_LOGIN_STATUS_CLASS_SUCCESS:
1485 uint32_t targetCSG;
1486 uint32_t targetNSG;
1487 bool targetTransit;
1488
1489 if (pImage->FirstRecvPDU)
1490 {
1491 pImage->FirstRecvPDU = false;
1492 pImage->ExpStatSN = RT_N2H_U32(aResBHS[6]) + 1;
1493 }
1494
1495 targetCSG = (RT_N2H_U32(aResBHS[0]) & ISCSI_CSG_MASK) >> ISCSI_CSG_SHIFT;
1496 targetNSG = (RT_N2H_U32(aResBHS[0]) & ISCSI_NSG_MASK) >> ISCSI_NSG_SHIFT;
1497 targetTransit = !!(RT_N2H_U32(aResBHS[0]) & ISCSI_TRANSIT_BIT);
1498
1499 /* Handle all cases with a single switch statement. */
1500 switch (csg << 8 | substate)
1501 {
1502 case 0x0000: /* security negotiation, step 0: receive final authentication. */
1503 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1504 if (RT_FAILURE(rc))
1505 break;
1506
1507 const char *pcszAuthMethod;
1508
1509 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "AuthMethod", &pcszAuthMethod);
1510 if (RT_FAILURE(rc))
1511 {
1512 rc = VERR_PARSE_ERROR;
1513 break;
1514 }
1515 if (strcmp(pcszAuthMethod, "None") == 0)
1516 {
1517 /* Authentication offered, but none required. Skip to operational parameters. */
1518 csg = 1;
1519 nsg = 1;
1520 transit = true;
1521 substate = 0;
1522 break;
1523 }
1524 else if (strcmp(pcszAuthMethod, "CHAP") == 0 && targetNSG == 0 && !targetTransit)
1525 {
1526 /* CHAP authentication required, continue with next substate. */
1527 substate++;
1528 break;
1529 }
1530
1531 /* Unknown auth method or login response PDU headers incorrect. */
1532 rc = VERR_PARSE_ERROR;
1533 break;
1534 case 0x0001: /* security negotiation, step 1: receive final CHAP variant and challenge. */
1535 {
1536 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1537 if (RT_FAILURE(rc))
1538 break;
1539
1540 const char *pcszChapAuthMethod;
1541 const char *pcszChapIdxTarget;
1542 const char *pcszChapChallengeStr;
1543
1544 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_A", &pcszChapAuthMethod);
1545 if (RT_FAILURE(rc))
1546 {
1547 rc = VERR_PARSE_ERROR;
1548 break;
1549 }
1550 if (strcmp(pcszChapAuthMethod, "5") != 0)
1551 {
1552 rc = VERR_PARSE_ERROR;
1553 break;
1554 }
1555 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_I", &pcszChapIdxTarget);
1556 if (RT_FAILURE(rc))
1557 {
1558 rc = VERR_PARSE_ERROR;
1559 break;
1560 }
1561 rc = RTStrToUInt8Ex(pcszChapIdxTarget, &pszNext, 0, &bChapIdx);
1562/** @todo r=bird: Unsafe use of pszNext on failure. The code should probably
1563 * use RTStrToUInt8Full and check for rc != VINF_SUCCESS. */
1564 if (rc > VINF_SUCCESS || *pszNext != '\0')
1565 {
1566 rc = VERR_PARSE_ERROR;
1567 break;
1568 }
1569 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_C", &pcszChapChallengeStr);
1570 if (RT_FAILURE(rc))
1571 {
1572 rc = VERR_PARSE_ERROR;
1573 break;
1574 }
1575 cbChallenge = sizeof(pbChallenge);
1576 rc = iscsiStrToBinary(pcszChapChallengeStr, pbChallenge, &cbChallenge);
1577 if (RT_FAILURE(rc))
1578 break;
1579 substate++;
1580 transit = true;
1581 break;
1582 }
1583 case 0x0002: /* security negotiation, step 2: check authentication success. */
1584 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1585 if (RT_FAILURE(rc))
1586 break;
1587
1588 if (targetCSG == 0 && targetNSG == 1 && targetTransit)
1589 {
1590 /* Target wants to continue in login operational state, authentication success. */
1591 csg = 1;
1592 nsg = 3;
1593 substate = 0;
1594 break;
1595 }
1596 rc = VERR_PARSE_ERROR;
1597 break;
1598 case 0x0100: /* login operational negotiation, step 0: check results. */
1599 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1600 if (RT_FAILURE(rc))
1601 break;
1602
1603 if (targetCSG == 1 && targetNSG == 3 && targetTransit)
1604 {
1605 /* Target wants to continue in full feature phase, login finished. */
1606 csg = 3;
1607 nsg = 3;
1608 substate = 0;
1609 break;
1610 }
1611 else if (targetCSG == 1 && targetNSG == 1 && !targetTransit)
1612 {
1613 /* Target wants to negotiate certain parameters and
1614 * stay in login operational negotiation. */
1615 csg = 1;
1616 nsg = 3;
1617 substate = 0;
1618 }
1619 rc = VERR_PARSE_ERROR;
1620 break;
1621 case 0x0300: /* full feature phase. */
1622 default:
1623 AssertMsgFailed(("recv: Undefined login state %d substate %d\n", csg, substate));
1624 rc = VERR_PARSE_ERROR;
1625 break;
1626 }
1627 break;
1628 case ISCSI_LOGIN_STATUS_CLASS_REDIRECTION:
1629 const char *pcszTargetRedir;
1630
1631 /* Target has moved to some other location, as indicated in the TargetAddress key. */
1632 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "TargetAddress", &pcszTargetRedir);
1633 if (RT_FAILURE(rc))
1634 {
1635 rc = VERR_PARSE_ERROR;
1636 break;
1637 }
1638 if (pImage->pszTargetAddress)
1639 RTMemFree(pImage->pszTargetAddress);
1640 {
1641 size_t cb = strlen(pcszTargetRedir) + 1;
1642 pImage->pszTargetAddress = (char *)RTMemAlloc(cb);
1643 if (!pImage->pszTargetAddress)
1644 {
1645 rc = VERR_NO_MEMORY;
1646 break;
1647 }
1648 memcpy(pImage->pszTargetAddress, pcszTargetRedir, cb);
1649 }
1650 rc = VINF_TRY_AGAIN;
1651 break;
1652 case ISCSI_LOGIN_STATUS_CLASS_INITIATOR_ERROR:
1653 {
1654 LogRel(("iSCSI: login to target failed with: %s\n",
1655 iscsiGetLoginErrorDetail((RT_N2H_U32(aResBHS[9]) >> 16) & 0xff)));
1656 iscsiTransportClose(pImage);
1657 rc = VERR_IO_GEN_FAILURE;
1658 break;
1659 }
1660 case ISCSI_LOGIN_STATUS_CLASS_TARGET_ERROR:
1661 iscsiTransportClose(pImage);
1662 rc = VINF_EOF;
1663 break;
1664 default:
1665 rc = VERR_PARSE_ERROR;
1666 }
1667
1668 if (RT_FAILURE(rc) || rc == VINF_TRY_AGAIN)
1669 break;
1670
1671 if (csg == 3)
1672 {
1673 /*
1674 * Finished login, continuing with Full Feature Phase.
1675 */
1676 rc = VINF_SUCCESS;
1677 break;
1678 }
1679 }
1680 else
1681 AssertMsgFailed(("%s: ignoring unexpected PDU with first word = %#08x\n", __FUNCTION__, RT_N2H_U32(aResBHS[0])));
1682 }
1683 else
1684 break;
1685 } while (true);
1686
1687 if ( RT_FAILURE(rc)
1688 && rc != VERR_TRY_AGAIN)
1689 {
1690 /*
1691 * Dump the last request and response of we are supposed to do so and there is a request
1692 * or response.
1693 */
1694 if (cnISCSIReq)
1695 iscsiDumpPacket(pImage, aISCSIReq, cnISCSIReq, VINF_SUCCESS, true /* fRequest */);
1696
1697 if (cnISCSIRes)
1698 iscsiDumpPacket(pImage, (PISCSIREQ)aISCSIRes, cnISCSIRes, rc, false /* fRequest */);
1699
1700 /*
1701 * Close connection to target.
1702 */
1703 iscsiTransportClose(pImage);
1704 pImage->state = ISCSISTATE_FREE;
1705 }
1706 else if (RT_FAILURE(rc))
1707 pImage->state = ISCSISTATE_NORMAL;
1708
1709 return rc;
1710}
1711
1712/**
1713 * Attach to an iSCSI target. Performs all operations necessary to enter
1714 * Full Feature Phase.
1715 *
1716 * @returns VBox status code.
1717 * @param pvUser The iSCSI connection state to be used as opaque user data.
1718 */
1719static DECLCALLBACK(int) iscsiAttach(void *pvUser)
1720{
1721 int rc = VINF_SUCCESS;
1722 unsigned cRetries = 5;
1723 PISCSIIMAGE pImage = (PISCSIIMAGE)pvUser;
1724
1725 LogFlowFunc(("entering\n"));
1726
1727 Assert(pImage->state == ISCSISTATE_FREE);
1728
1729 /*
1730 * If there were too many logins without any successful I/O just fail
1731 * and assume the target is not working properly.
1732 */
1733 if (ASMAtomicReadU32(&pImage->cLoginsSinceIo) == 3)
1734 return VERR_BROKEN_PIPE;
1735
1736 RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
1737
1738 /* Make 100% sure the connection isn't reused for a new login. */
1739 iscsiTransportClose(pImage);
1740
1741 /* Try to log in a few number of times. */
1742 while (cRetries > 0)
1743 {
1744 rc = iscsiLogin(pImage);
1745 if (rc == VINF_SUCCESS) /* Login succeeded, continue with full feature phase. */
1746 break;
1747 else if (rc == VERR_TRY_AGAIN) /* Lost connection during receive. */
1748 cRetries--;
1749 else if (RT_FAILURE(rc))
1750 break;
1751 else /* For redirects try again. */
1752 AssertMsg(rc == VINF_TRY_AGAIN, ("Unexpected status code %Rrc\n", rc));
1753 }
1754
1755 if (RT_SUCCESS(rc))
1756 ASMAtomicIncU32(&pImage->cLoginsSinceIo);
1757
1758 RTSemMutexRelease(pImage->Mutex);
1759
1760 LogFlowFunc(("returning %Rrc\n", rc));
1761 LogRel(("iSCSI: login to target %s %s (%Rrc)\n", pImage->pszTargetName, RT_SUCCESS(rc) ? "successful" : "failed", rc));
1762 return rc;
1763}
1764
1765
1766/**
1767 * Detach from an iSCSI target.
1768 *
1769 * @returns VBox status code.
1770 * @param pvUser The iSCSI connection state to be used as opaque user data.
1771 */
1772static DECLCALLBACK(int) iscsiDetach(void *pvUser)
1773{
1774 int rc;
1775 uint32_t itt;
1776 uint32_t cnISCSIReq = 0;
1777 ISCSIREQ aISCSIReq[4];
1778 uint32_t aReqBHS[12];
1779 PISCSIIMAGE pImage = (PISCSIIMAGE)pvUser;
1780
1781 LogFlowFunc(("entering\n"));
1782
1783 RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
1784
1785 if (pImage->state != ISCSISTATE_FREE && pImage->state != ISCSISTATE_IN_LOGOUT)
1786 {
1787 pImage->state = ISCSISTATE_IN_LOGOUT;
1788
1789 /*
1790 * Send logout request to target.
1791 */
1792 itt = iscsiNewITT(pImage);
1793 aReqBHS[0] = RT_H2N_U32(ISCSI_FINAL_BIT | ISCSIOP_LOGOUT_REQ); /* I=0,F=1,Reason=close session */
1794 aReqBHS[1] = RT_H2N_U32(0); /* TotalAHSLength=0,DataSementLength=0 */
1795 aReqBHS[2] = 0; /* reserved */
1796 aReqBHS[3] = 0; /* reserved */
1797 aReqBHS[4] = itt;
1798 aReqBHS[5] = 0; /* reserved */
1799 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
1800 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
1801 aReqBHS[8] = 0; /* reserved */
1802 aReqBHS[9] = 0; /* reserved */
1803 aReqBHS[10] = 0; /* reserved */
1804 aReqBHS[11] = 0; /* reserved */
1805 pImage->CmdSN++;
1806
1807 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
1808 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
1809 cnISCSIReq++;
1810
1811 rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_NO_REATTACH);
1812 if (RT_SUCCESS(rc))
1813 {
1814 /*
1815 * Read logout response from target.
1816 */
1817 ISCSIRES aISCSIRes;
1818 uint32_t aResBHS[12];
1819
1820 aISCSIRes.pvSeg = aResBHS;
1821 aISCSIRes.cbSeg = sizeof(aResBHS);
1822 rc = iscsiRecvPDU(pImage, itt, &aISCSIRes, 1, ISCSIPDU_NO_REATTACH);
1823 if (RT_SUCCESS(rc))
1824 {
1825 if (RT_N2H_U32(aResBHS[0]) != (ISCSI_FINAL_BIT | ISCSIOP_LOGOUT_RES))
1826 AssertMsgFailed(("iSCSI Logout response invalid\n"));
1827 }
1828 else
1829 AssertMsgFailed(("iSCSI Logout response error, rc=%Rrc\n", rc));
1830 }
1831 else
1832 AssertMsgFailed(("Could not send iSCSI Logout request, rc=%Rrc\n", rc));
1833 }
1834
1835 if (pImage->state != ISCSISTATE_FREE)
1836 {
1837 /*
1838 * Close connection to target.
1839 */
1840 rc = iscsiTransportClose(pImage);
1841 if (RT_FAILURE(rc))
1842 AssertMsgFailed(("Could not close connection to target, rc=%Rrc\n", rc));
1843 }
1844
1845 pImage->state = ISCSISTATE_FREE;
1846
1847 RTSemMutexRelease(pImage->Mutex);
1848
1849 LogFlowFunc(("leaving\n"));
1850 LogRel(("iSCSI: logout to target %s\n", pImage->pszTargetName));
1851 return VINF_SUCCESS;
1852}
1853
1854
1855/**
1856 * Perform a command on an iSCSI target. Target must be already in
1857 * Full Feature Phase.
1858 *
1859 * @returns VBox status code.
1860 * @param pImage The iSCSI connection state to be used.
1861 * @param pRequest Command descriptor. Contains all information about
1862 * the command, its transfer directions and pointers
1863 * to the buffer(s) used for transferring data and
1864 * status information.
1865 */
1866static int iscsiCommand(PISCSIIMAGE pImage, PSCSIREQ pRequest)
1867{
1868 int rc;
1869 uint32_t itt;
1870 uint32_t cbData;
1871 uint32_t cnISCSIReq = 0;
1872 ISCSIREQ aISCSIReq[4];
1873 uint32_t aReqBHS[12];
1874
1875 uint32_t *pDst = NULL;
1876 size_t cbBufLength;
1877 uint32_t aStatus[256]; /**< Plenty of buffer for status information. */
1878 uint32_t ExpDataSN = 0;
1879 bool final = false;
1880
1881
1882 LogFlowFunc(("entering, CmdSN=%d\n", pImage->CmdSN));
1883
1884 Assert(pRequest->enmXfer != SCSIXFER_TO_FROM_TARGET); /**< @todo not yet supported, would require AHS. */
1885 Assert(pRequest->cbI2TData <= 0xffffff); /* larger transfers would require R2T support. */
1886 Assert(pRequest->cbCDB <= 16); /* would cause buffer overrun below. */
1887
1888 /* If not in normal state, then the transport connection was dropped. Try
1889 * to reestablish by logging in, the target might be responsive again. */
1890 if (pImage->state == ISCSISTATE_FREE)
1891 rc = iscsiAttach(pImage);
1892
1893 /* If still not in normal state, then the underlying transport connection
1894 * cannot be established. Get out before bad things happen (and make
1895 * sure the caller suspends the VM again). */
1896 if (pImage->state == ISCSISTATE_NORMAL)
1897 {
1898 /*
1899 * Send SCSI command to target with all I2T data included.
1900 */
1901 cbData = 0;
1902 if (pRequest->enmXfer == SCSIXFER_FROM_TARGET)
1903 cbData = (uint32_t)pRequest->cbT2IData;
1904 else
1905 cbData = (uint32_t)pRequest->cbI2TData;
1906
1907 RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
1908
1909 itt = iscsiNewITT(pImage);
1910 memset(aReqBHS, 0, sizeof(aReqBHS));
1911 aReqBHS[0] = RT_H2N_U32( ISCSI_FINAL_BIT | ISCSI_TASK_ATTR_SIMPLE | ISCSIOP_SCSI_CMD
1912 | (pRequest->enmXfer << 21)); /* I=0,F=1,Attr=Simple */
1913 aReqBHS[1] = RT_H2N_U32(0x00000000 | ((uint32_t)pRequest->cbI2TData & 0xffffff)); /* TotalAHSLength=0 */
1914 aReqBHS[2] = RT_H2N_U32(pImage->LUN >> 32);
1915 aReqBHS[3] = RT_H2N_U32(pImage->LUN & 0xffffffff);
1916 aReqBHS[4] = itt;
1917 aReqBHS[5] = RT_H2N_U32(cbData);
1918 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
1919 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
1920 memcpy(aReqBHS + 8, pRequest->abCDB, pRequest->cbCDB);
1921 pImage->CmdSN++;
1922
1923 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
1924 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
1925 cnISCSIReq++;
1926
1927 if ( pRequest->enmXfer == SCSIXFER_TO_TARGET
1928 || pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET)
1929 {
1930 Assert(pRequest->cI2TSegs == 1);
1931 aISCSIReq[cnISCSIReq].pcvSeg = pRequest->paI2TSegs[0].pvSeg;
1932 aISCSIReq[cnISCSIReq].cbSeg = pRequest->paI2TSegs[0].cbSeg; /* Padding done by transport. */
1933 cnISCSIReq++;
1934 }
1935
1936 rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_DEFAULT);
1937 if (RT_SUCCESS(rc))
1938 {
1939 /* Place SCSI request in queue. */
1940 pImage->paCurrReq = aISCSIReq;
1941 pImage->cnCurrReq = cnISCSIReq;
1942
1943 /*
1944 * Read SCSI response/data in PDUs from target.
1945 */
1946 if ( pRequest->enmXfer == SCSIXFER_FROM_TARGET
1947 || pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET)
1948 {
1949 Assert(pRequest->cT2ISegs == 1);
1950 pDst = (uint32_t *)pRequest->paT2ISegs[0].pvSeg;
1951 cbBufLength = pRequest->paT2ISegs[0].cbSeg;
1952 }
1953 else
1954 cbBufLength = 0;
1955
1956 do
1957 {
1958 uint32_t cnISCSIRes = 0;
1959 ISCSIRES aISCSIRes[4];
1960 uint32_t aResBHS[12];
1961
1962 aISCSIRes[cnISCSIRes].pvSeg = aResBHS;
1963 aISCSIRes[cnISCSIRes].cbSeg = sizeof(aResBHS);
1964 cnISCSIRes++;
1965 if (cbBufLength != 0 &&
1966 ( pRequest->enmXfer == SCSIXFER_FROM_TARGET
1967 || pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET))
1968 {
1969 aISCSIRes[cnISCSIRes].pvSeg = pDst;
1970 aISCSIRes[cnISCSIRes].cbSeg = cbBufLength;
1971 cnISCSIRes++;
1972 }
1973 /* Always reserve space for the status - it's impossible to tell
1974 * beforehand whether this will be the final PDU or not. */
1975 aISCSIRes[cnISCSIRes].pvSeg = aStatus;
1976 aISCSIRes[cnISCSIRes].cbSeg = sizeof(aStatus);
1977 cnISCSIRes++;
1978
1979 rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes, ISCSIPDU_DEFAULT);
1980 if (RT_FAILURE(rc))
1981 break;
1982
1983 final = !!(RT_N2H_U32(aResBHS[0]) & ISCSI_FINAL_BIT);
1984 ISCSIOPCODE cmd = (ISCSIOPCODE)(RT_N2H_U32(aResBHS[0]) & ISCSIOP_MASK);
1985 if (cmd == ISCSIOP_SCSI_RES)
1986 {
1987 /* This is the final PDU which delivers the status (and may be omitted if
1988 * the last Data-In PDU included successful completion status). Note
1989 * that ExpStatSN has been bumped already in iscsiRecvPDU. */
1990 if (!final || ((RT_N2H_U32(aResBHS[0]) & 0x0000ff00) != 0) || (RT_N2H_U32(aResBHS[6]) != pImage->ExpStatSN - 1))
1991 {
1992 /* SCSI Response in the wrong place or with a (target) failure. */
1993 rc = VERR_PARSE_ERROR;
1994 break;
1995 }
1996 /* The following is a bit tricky, as in error situations we may
1997 * get the status only instead of the result data plus optional
1998 * status. Thus the status may have ended up partially in the
1999 * data area. */
2000 pRequest->status = RT_N2H_U32(aResBHS[0]) & 0x000000ff;
2001 cbData = RT_N2H_U32(aResBHS[1]) & 0x00ffffff;
2002 if (cbData >= 2)
2003 {
2004 uint32_t cbStat = RT_N2H_U32(((uint32_t *)aISCSIRes[1].pvSeg)[0]) >> 16;
2005 if (cbStat + 2 > cbData)
2006 {
2007 rc = VERR_BUFFER_OVERFLOW;
2008 break;
2009 }
2010 /* Truncate sense data if it doesn't fit into the buffer. */
2011 pRequest->cbSense = RT_MIN(cbStat, pRequest->cbSense);
2012 memcpy(pRequest->abSense,
2013 ((const char *)aISCSIRes[1].pvSeg) + 2,
2014 RT_MIN(aISCSIRes[1].cbSeg - 2, pRequest->cbSense));
2015 if ( cnISCSIRes > 2 && aISCSIRes[2].cbSeg
2016 && (ssize_t)pRequest->cbSense - aISCSIRes[1].cbSeg + 2 > 0)
2017 {
2018 memcpy((char *)pRequest->abSense + aISCSIRes[1].cbSeg - 2,
2019 aISCSIRes[2].pvSeg,
2020 pRequest->cbSense - aISCSIRes[1].cbSeg + 2);
2021 }
2022 }
2023 else if (cbData == 1)
2024 {
2025 rc = VERR_PARSE_ERROR;
2026 break;
2027 }
2028 else
2029 pRequest->cbSense = 0;
2030 break;
2031 }
2032 else if (cmd == ISCSIOP_SCSI_DATA_IN)
2033 {
2034 /* A Data-In PDU carries some data that needs to be added to the received
2035 * data in response to the command. There may be both partial and complete
2036 * Data-In PDUs, so collect data until the status is included or the status
2037 * is sent in a separate SCSI Result frame (see above). */
2038 if (final && aISCSIRes[2].cbSeg != 0)
2039 {
2040 /* The received PDU is partially stored in the buffer for status.
2041 * Must not happen under normal circumstances and is a target error. */
2042 rc = VERR_BUFFER_OVERFLOW;
2043 break;
2044 }
2045 uint32_t len = RT_N2H_U32(aResBHS[1]) & 0x00ffffff;
2046 pDst = (uint32_t *)((char *)pDst + len);
2047 cbBufLength -= len;
2048 ExpDataSN++;
2049 if (final && (RT_N2H_U32(aResBHS[0]) & ISCSI_STATUS_BIT) != 0)
2050 {
2051 pRequest->status = RT_N2H_U32(aResBHS[0]) & 0x000000ff;
2052 pRequest->cbSense = 0;
2053 break;
2054 }
2055 }
2056 else
2057 {
2058 rc = VERR_PARSE_ERROR;
2059 break;
2060 }
2061 } while (true);
2062
2063 /* Remove SCSI request from queue. */
2064 pImage->paCurrReq = NULL;
2065 pImage->cnCurrReq = 0;
2066 }
2067
2068 if (rc == VERR_TIMEOUT)
2069 {
2070 /* Drop connection in case the target plays dead. Much better than
2071 * delaying the next requests until the timed out command actually
2072 * finishes. Also keep in mind that command shouldn't take longer than
2073 * about 30-40 seconds, or the guest will lose its patience. */
2074 iscsiTransportClose(pImage);
2075 pImage->state = ISCSISTATE_FREE;
2076 rc = VERR_BROKEN_PIPE;
2077 }
2078 RTSemMutexRelease(pImage->Mutex);
2079 }
2080 else
2081 rc = VERR_NET_CONNECTION_REFUSED;
2082
2083 if (RT_SUCCESS(rc))
2084 ASMAtomicWriteU32(&pImage->cLoginsSinceIo, 0);
2085 LogFlowFunc(("returns %Rrc\n", rc));
2086 return rc;
2087}
2088
2089
2090/**
2091 * Generate a new Initiator Task Tag.
2092 *
2093 * @returns Initiator Task Tag.
2094 * @param pImage The iSCSI connection state to be used.
2095 */
2096static uint32_t iscsiNewITT(PISCSIIMAGE pImage)
2097{
2098 uint32_t next_itt;
2099
2100 next_itt = pImage->ITT++;
2101 if (pImage->ITT == ISCSI_TASK_TAG_RSVD)
2102 pImage->ITT = 0;
2103 return RT_H2N_U32(next_itt);
2104}
2105
2106
2107/**
2108 * Send an iSCSI request. The request can consist of several segments, which
2109 * are padded to 4 byte boundaries and concatenated.
2110 *
2111 * @returns VBOX status
2112 * @param pImage The iSCSI connection state to be used.
2113 * @param paReq Pointer to array of iSCSI request sections.
2114 * @param cnReq Number of valid iSCSI request sections in the array.
2115 * @param uFlags Flags controlling the exact send semantics.
2116 */
2117static int iscsiSendPDU(PISCSIIMAGE pImage, PISCSIREQ paReq, uint32_t cnReq,
2118 uint32_t uFlags)
2119{
2120 int rc = VINF_SUCCESS;
2121 /** @todo return VERR_VD_ISCSI_INVALID_STATE in the appropriate situations,
2122 * needs cleaning up of timeout/disconnect handling a bit, as otherwise
2123 * too many incorrect errors are signalled. */
2124 Assert(cnReq >= 1);
2125 Assert(paReq[0].cbSeg >= ISCSI_BHS_SIZE);
2126
2127 for (uint32_t i = 0; i < pImage->cISCSIRetries; i++)
2128 {
2129 rc = iscsiTransportWrite(pImage, paReq, cnReq);
2130 if (RT_SUCCESS(rc))
2131 break;
2132 if ( (uFlags & ISCSIPDU_NO_REATTACH)
2133 || (rc != VERR_BROKEN_PIPE && rc != VERR_NET_CONNECTION_REFUSED))
2134 break;
2135 /* No point in reestablishing the connection for a logout */
2136 if (pImage->state == ISCSISTATE_IN_LOGOUT)
2137 break;
2138 RTThreadSleep(500);
2139 if (pImage->state != ISCSISTATE_IN_LOGIN)
2140 {
2141 /* Attempt to re-login when a connection fails, but only when not
2142 * currently logging in. */
2143 rc = iscsiAttach(pImage);
2144 if (RT_FAILURE(rc))
2145 break;
2146 }
2147 }
2148 return rc;
2149}
2150
2151
2152/**
2153 * Wait for an iSCSI response with a matching Initiator Target Tag. The response is
2154 * split into several segments, as requested by the caller-provided buffer specification.
2155 * Remember that the response can be split into several PDUs by the sender, so make
2156 * sure that all parts are collected and processed appropriately by the caller.
2157 *
2158 * @returns VBOX status
2159 * @param pImage The iSCSI connection state to be used.
2160 * @param itt The initiator task tag.
2161 * @param paRes Pointer to array of iSCSI response sections.
2162 * @param cnRes Number of valid iSCSI response sections in the array.
2163 * @param fRecvFlags PDU receive flags.
2164 */
2165static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes,
2166 uint32_t fRecvFlags)
2167{
2168 int rc = VINF_SUCCESS;
2169 ISCSIRES aResBuf;
2170
2171 for (uint32_t i = 0; i < pImage->cISCSIRetries; i++)
2172 {
2173 aResBuf.pvSeg = pImage->pvRecvPDUBuf;
2174 aResBuf.cbSeg = pImage->cbRecvPDUBuf;
2175 rc = iscsiTransportRead(pImage, &aResBuf, 1);
2176 if (RT_FAILURE(rc))
2177 {
2178 if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
2179 {
2180 /* No point in reestablishing the connection for a logout */
2181 if (pImage->state == ISCSISTATE_IN_LOGOUT)
2182 break;
2183 /* Connection broken while waiting for a response - wait a while and
2184 * try to restart by re-sending the original request (if any).
2185 * This also handles the connection reestablishment (login etc.). */
2186 RTThreadSleep(500);
2187 if ( pImage->state != ISCSISTATE_IN_LOGIN
2188 && !(fRecvFlags & ISCSIPDU_NO_REATTACH))
2189 {
2190 /* Attempt to re-login when a connection fails, but only when not
2191 * currently logging in. */
2192 rc = iscsiAttach(pImage);
2193 if (RT_FAILURE(rc))
2194 break;
2195
2196 if (pImage->paCurrReq != NULL)
2197 {
2198 rc = iscsiSendPDU(pImage, pImage->paCurrReq, pImage->cnCurrReq, ISCSIPDU_DEFAULT);
2199 if (RT_FAILURE(rc))
2200 break;
2201 }
2202 }
2203 }
2204 else
2205 {
2206 /* Signal other errors (VERR_BUFFER_OVERFLOW etc.) to the caller. */
2207 break;
2208 }
2209 }
2210 else
2211 {
2212 ISCSIOPCODE cmd;
2213 const uint32_t *pcvResSeg = (const uint32_t *)aResBuf.pvSeg;
2214
2215 /* Check whether the received PDU is valid, and update the internal state of
2216 * the iSCSI connection/session. */
2217 rc = iscsiValidatePDU(&aResBuf, 1);
2218 if (RT_FAILURE(rc))
2219 {
2220 iscsiDumpPacket(pImage, (PISCSIREQ)&aResBuf, 1, rc, false /* fRequest */);
2221 continue;
2222 }
2223 cmd = (ISCSIOPCODE)(RT_N2H_U32(pcvResSeg[0]) & ISCSIOP_MASK);
2224 switch (cmd)
2225 {
2226 case ISCSIOP_SCSI_RES:
2227 case ISCSIOP_SCSI_TASKMGMT_RES:
2228 case ISCSIOP_SCSI_DATA_IN:
2229 case ISCSIOP_R2T:
2230 case ISCSIOP_ASYN_MSG:
2231 case ISCSIOP_TEXT_RES:
2232 case ISCSIOP_LOGIN_RES:
2233 case ISCSIOP_LOGOUT_RES:
2234 case ISCSIOP_REJECT:
2235 case ISCSIOP_NOP_IN:
2236 if (serial_number_less(pImage->MaxCmdSN, RT_N2H_U32(pcvResSeg[8])))
2237 pImage->MaxCmdSN = RT_N2H_U32(pcvResSeg[8]);
2238 if (serial_number_less(pImage->ExpCmdSN, RT_N2H_U32(pcvResSeg[7])))
2239 pImage->ExpCmdSN = RT_N2H_U32(pcvResSeg[7]);
2240 break;
2241 default:
2242 rc = VERR_PARSE_ERROR;
2243 iscsiDumpPacket(pImage, (PISCSIREQ)&aResBuf, 1, rc, false /* fRequest */);
2244 }
2245 if (RT_FAILURE(rc))
2246 continue;
2247 if ( !pImage->FirstRecvPDU
2248 && (cmd != ISCSIOP_SCSI_DATA_IN || (RT_N2H_U32(pcvResSeg[0]) & ISCSI_STATUS_BIT))
2249 && ( cmd != ISCSIOP_LOGIN_RES
2250 || (ISCSILOGINSTATUSCLASS)((RT_N2H_U32(pcvResSeg[9]) >> 24) == ISCSI_LOGIN_STATUS_CLASS_SUCCESS)))
2251 {
2252 if (pImage->ExpStatSN == RT_N2H_U32(pcvResSeg[6]))
2253 {
2254 /* StatSN counter is not advanced on R2T and on a target SN update NOP-In. */
2255 if ( (cmd != ISCSIOP_R2T)
2256 && ((cmd != ISCSIOP_NOP_IN) || (RT_N2H_U32(pcvResSeg[4]) != ISCSI_TASK_TAG_RSVD)))
2257 pImage->ExpStatSN++;
2258 }
2259 else
2260 {
2261 rc = VERR_PARSE_ERROR;
2262 iscsiDumpPacket(pImage, (PISCSIREQ)&aResBuf, 1, rc, false /* fRequest */);
2263 continue;
2264 }
2265 }
2266 /* Finally check whether the received PDU matches what the caller wants. */
2267 if ( itt == pcvResSeg[4]
2268 && itt != ISCSI_TASK_TAG_RSVD)
2269 {
2270 /* Copy received PDU (one segment) to caller-provided buffers. */
2271 uint32_t j;
2272 size_t cbSeg;
2273 const uint8_t *pSrc;
2274
2275 pSrc = (const uint8_t *)aResBuf.pvSeg;
2276 cbSeg = aResBuf.cbSeg;
2277 for (j = 0; j < cnRes; j++)
2278 {
2279 if (cbSeg > paRes[j].cbSeg)
2280 {
2281 memcpy(paRes[j].pvSeg, pSrc, paRes[j].cbSeg);
2282 pSrc += paRes[j].cbSeg;
2283 cbSeg -= paRes[j].cbSeg;
2284 }
2285 else
2286 {
2287 memcpy(paRes[j].pvSeg, pSrc, cbSeg);
2288 paRes[j].cbSeg = cbSeg;
2289 cbSeg = 0;
2290 break;
2291 }
2292 }
2293 if (cbSeg != 0)
2294 {
2295 rc = VERR_BUFFER_OVERFLOW;
2296 break;
2297 }
2298 for (j++; j < cnRes; j++)
2299 paRes[j].cbSeg = 0;
2300 break;
2301 }
2302 else if ( cmd == ISCSIOP_NOP_IN
2303 && RT_N2H_U32(pcvResSeg[5]) != ISCSI_TASK_TAG_RSVD)
2304 {
2305 uint32_t cnISCSIReq;
2306 ISCSIREQ aISCSIReq[4];
2307 uint32_t aReqBHS[12];
2308
2309 aReqBHS[0] = RT_H2N_U32(ISCSI_IMMEDIATE_DELIVERY_BIT | ISCSI_FINAL_BIT | ISCSIOP_NOP_OUT);
2310 aReqBHS[1] = RT_H2N_U32(0); /* TotalAHSLength=0,DataSementLength=0 */
2311 aReqBHS[2] = pcvResSeg[2]; /* copy LUN from NOP-In */
2312 aReqBHS[3] = pcvResSeg[3]; /* copy LUN from NOP-In */
2313 aReqBHS[4] = RT_H2N_U32(ISCSI_TASK_TAG_RSVD); /* ITT, reply */
2314 aReqBHS[5] = pcvResSeg[5]; /* copy TTT from NOP-In */
2315 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
2316 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
2317 aReqBHS[8] = 0; /* reserved */
2318 aReqBHS[9] = 0; /* reserved */
2319 aReqBHS[10] = 0; /* reserved */
2320 aReqBHS[11] = 0; /* reserved */
2321
2322 cnISCSIReq = 0;
2323 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
2324 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
2325 cnISCSIReq++;
2326
2327 iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_NO_REATTACH);
2328 /* Break if the caller wanted to process the NOP-in only. */
2329 if (itt == ISCSI_TASK_TAG_RSVD)
2330 break;
2331 }
2332 }
2333 }
2334
2335 LogFlowFunc(("returns rc=%Rrc\n", rc));
2336 return rc;
2337}
2338
2339
2340/**
2341 * Reset the PDU buffer
2342 *
2343 * @param pImage The iSCSI connection state to be used.
2344 */
2345static void iscsiRecvPDUReset(PISCSIIMAGE pImage)
2346{
2347 pImage->cbRecvPDUResidual = ISCSI_BHS_SIZE;
2348 pImage->fRecvPDUBHS = true;
2349 pImage->pbRecvPDUBufCur = (uint8_t *)pImage->pvRecvPDUBuf;
2350}
2351
2352static void iscsiPDUTxAdd(PISCSIIMAGE pImage, PISCSIPDUTX pIScsiPDUTx, bool fFront)
2353{
2354 if (!fFront)
2355 {
2356 /* Insert PDU at the tail of the list. */
2357 if (!pImage->pIScsiPDUTxHead)
2358 pImage->pIScsiPDUTxHead = pIScsiPDUTx;
2359 else
2360 pImage->pIScsiPDUTxTail->pNext = pIScsiPDUTx;
2361 pImage->pIScsiPDUTxTail = pIScsiPDUTx;
2362 }
2363 else
2364 {
2365 /* Insert PDU at the beginning of the list. */
2366 pIScsiPDUTx->pNext = pImage->pIScsiPDUTxHead;
2367 pImage->pIScsiPDUTxHead = pIScsiPDUTx;
2368 if (!pImage->pIScsiPDUTxTail)
2369 pImage->pIScsiPDUTxTail = pIScsiPDUTx;
2370 }
2371}
2372
2373/**
2374 * Receives a PDU in a non blocking way.
2375 *
2376 * @returns VBOX status code.
2377 * @param pImage The iSCSI connection state to be used.
2378 */
2379static int iscsiRecvPDUAsync(PISCSIIMAGE pImage)
2380{
2381 size_t cbActuallyRead = 0;
2382 int rc = VINF_SUCCESS;
2383
2384 LogFlowFunc(("pImage=%#p\n", pImage));
2385
2386 /* Check if we are in the middle of a PDU receive. */
2387 if (pImage->cbRecvPDUResidual == 0)
2388 {
2389 /*
2390 * We are receiving a new PDU, don't read more than the BHS initially
2391 * until we know the real size of the PDU.
2392 */
2393 iscsiRecvPDUReset(pImage);
2394 LogFlow(("Receiving new PDU\n"));
2395 }
2396
2397 rc = pImage->pIfNet->pfnReadNB(pImage->Socket, pImage->pbRecvPDUBufCur,
2398 pImage->cbRecvPDUResidual, &cbActuallyRead);
2399 if (RT_SUCCESS(rc) && cbActuallyRead == 0)
2400 rc = VERR_BROKEN_PIPE;
2401
2402 if (RT_SUCCESS(rc))
2403 {
2404 LogFlow(("Received %zu bytes\n", cbActuallyRead));
2405 pImage->cbRecvPDUResidual -= cbActuallyRead;
2406 pImage->pbRecvPDUBufCur += cbActuallyRead;
2407
2408 /* Check if we received everything we wanted. */
2409 if ( !pImage->cbRecvPDUResidual
2410 && pImage->fRecvPDUBHS)
2411 {
2412 size_t cbAHSLength, cbDataLength;
2413
2414 /* If we were reading the BHS first get the actual PDU size now. */
2415 uint32_t word1 = RT_N2H_U32(((uint32_t *)(pImage->pvRecvPDUBuf))[1]);
2416 cbAHSLength = (word1 & 0xff000000) >> 24;
2417 cbAHSLength = ((cbAHSLength - 1) | 3) + 1; /* Add padding. */
2418 cbDataLength = word1 & 0x00ffffff;
2419 cbDataLength = ((cbDataLength - 1) | 3) + 1; /* Add padding. */
2420 pImage->cbRecvPDUResidual = cbAHSLength + cbDataLength;
2421 pImage->fRecvPDUBHS = false; /* Start receiving the rest of the PDU. */
2422 }
2423
2424 if (!pImage->cbRecvPDUResidual)
2425 {
2426 /* We received the complete PDU with or without any payload now. */
2427 LogFlow(("Received complete PDU\n"));
2428 ISCSIRES aResBuf;
2429 aResBuf.pvSeg = pImage->pvRecvPDUBuf;
2430 aResBuf.cbSeg = pImage->cbRecvPDUBuf;
2431 rc = iscsiRecvPDUProcess(pImage, &aResBuf, 1);
2432 }
2433 }
2434 else
2435 LogFlowFunc(("Reading from the socket returned with rc=%Rrc\n", rc));
2436
2437 return rc;
2438}
2439
2440static int iscsiSendPDUAsync(PISCSIIMAGE pImage)
2441{
2442 size_t cbSent = 0;
2443 int rc = VINF_SUCCESS;
2444
2445 LogFlowFunc(("pImage=%#p\n", pImage));
2446
2447 do
2448 {
2449 /*
2450 * If there is no PDU active, get the first one from the list.
2451 * Check that we are allowed to transfer the PDU by comparing the
2452 * command sequence number and the maximum sequence number allowed by the target.
2453 */
2454 if (!pImage->pIScsiPDUTxCur)
2455 {
2456 if ( !pImage->pIScsiPDUTxHead
2457 || serial_number_greater(pImage->pIScsiPDUTxHead->CmdSN, pImage->MaxCmdSN))
2458 break;
2459
2460 pImage->pIScsiPDUTxCur = pImage->pIScsiPDUTxHead;
2461 pImage->pIScsiPDUTxHead = pImage->pIScsiPDUTxCur->pNext;
2462 if (!pImage->pIScsiPDUTxHead)
2463 pImage->pIScsiPDUTxTail = NULL;
2464 }
2465
2466 /* Send as much as we can. */
2467 rc = pImage->pIfNet->pfnSgWriteNB(pImage->Socket, &pImage->pIScsiPDUTxCur->SgBuf, &cbSent);
2468 LogFlow(("SgWriteNB returned rc=%Rrc cbSent=%zu\n", rc, cbSent));
2469 if (RT_SUCCESS(rc))
2470 {
2471 LogFlow(("Sent %zu bytes for PDU %#p\n", cbSent, pImage->pIScsiPDUTxCur));
2472 pImage->pIScsiPDUTxCur->cbSgLeft -= cbSent;
2473 RTSgBufAdvance(&pImage->pIScsiPDUTxCur->SgBuf, cbSent);
2474 if (!pImage->pIScsiPDUTxCur->cbSgLeft)
2475 {
2476 /* PDU completed, free it and place the command on the waiting for response list. */
2477 if (pImage->pIScsiPDUTxCur->pIScsiCmd)
2478 {
2479 LogFlow(("Sent complete PDU, placing on waiting list\n"));
2480 iscsiCmdInsert(pImage, pImage->pIScsiPDUTxCur->pIScsiCmd);
2481 }
2482 RTMemFree(pImage->pIScsiPDUTxCur);
2483 pImage->pIScsiPDUTxCur = NULL;
2484 }
2485 }
2486 } while ( RT_SUCCESS(rc)
2487 && !pImage->pIScsiPDUTxCur);
2488
2489 if (rc == VERR_TRY_AGAIN)
2490 rc = VINF_SUCCESS;
2491
2492 /* Add the write poll flag if we still have something to send, clear it otherwise. */
2493 if (pImage->pIScsiPDUTxCur)
2494 pImage->fPollEvents |= VD_INTERFACETCPNET_EVT_WRITE;
2495 else
2496 pImage->fPollEvents &= ~VD_INTERFACETCPNET_EVT_WRITE;
2497
2498 LogFlowFunc(("rc=%Rrc pIScsiPDUTxCur=%#p\n", rc, pImage->pIScsiPDUTxCur));
2499 return rc;
2500}
2501
2502/**
2503 * Process a received PDU.
2504 *
2505 * @return VBOX status code.
2506 * @param pImage The iSCSI connection state to be used.
2507 * @param paRes Pointer to the array of iSCSI response sections.
2508 * @param cnRes Number of valid iSCSI response sections in the array.
2509 */
2510static int iscsiRecvPDUProcess(PISCSIIMAGE pImage, PISCSIRES paRes, uint32_t cnRes)
2511{
2512 int rc = VINF_SUCCESS;
2513
2514 LogFlowFunc(("pImage=%#p paRes=%#p cnRes=%u\n", pImage, paRes, cnRes));
2515
2516 /* Validate the PDU first. */
2517 rc = iscsiValidatePDU(paRes, cnRes);
2518 if (RT_SUCCESS(rc))
2519 {
2520 ISCSIOPCODE cmd;
2521 const uint32_t *pcvResSeg = (const uint32_t *)paRes[0].pvSeg;
2522
2523 Assert(paRes[0].cbSeg > 9 * sizeof(uint32_t));
2524
2525 do
2526 {
2527 cmd = (ISCSIOPCODE)(RT_N2H_U32(pcvResSeg[0]) & ISCSIOP_MASK);
2528 switch (cmd)
2529 {
2530 case ISCSIOP_SCSI_RES:
2531 case ISCSIOP_SCSI_TASKMGMT_RES:
2532 case ISCSIOP_SCSI_DATA_IN:
2533 case ISCSIOP_R2T:
2534 case ISCSIOP_ASYN_MSG:
2535 case ISCSIOP_TEXT_RES:
2536 case ISCSIOP_LOGIN_RES:
2537 case ISCSIOP_LOGOUT_RES:
2538 case ISCSIOP_REJECT:
2539 case ISCSIOP_NOP_IN:
2540 if (serial_number_less(pImage->MaxCmdSN, RT_N2H_U32(pcvResSeg[8])))
2541 pImage->MaxCmdSN = RT_N2H_U32(pcvResSeg[8]);
2542 if (serial_number_less(pImage->ExpCmdSN, RT_N2H_U32(pcvResSeg[7])))
2543 pImage->ExpCmdSN = RT_N2H_U32(pcvResSeg[7]);
2544 break;
2545 default:
2546 rc = VERR_PARSE_ERROR;
2547 iscsiDumpPacket(pImage, (PISCSIREQ)paRes, cnRes, rc, false /* fRequest */);
2548 }
2549
2550 if (RT_FAILURE(rc))
2551 break;
2552
2553 if ( !pImage->FirstRecvPDU
2554 && (cmd != ISCSIOP_SCSI_DATA_IN || (RT_N2H_U32(pcvResSeg[0]) & ISCSI_STATUS_BIT)))
2555 {
2556 if (pImage->ExpStatSN == RT_N2H_U32(pcvResSeg[6]))
2557 {
2558 /* StatSN counter is not advanced on R2T and on a target SN update NOP-In. */
2559 if ( (cmd != ISCSIOP_R2T)
2560 && ((cmd != ISCSIOP_NOP_IN) || (RT_N2H_U32(pcvResSeg[4]) != ISCSI_TASK_TAG_RSVD)))
2561 pImage->ExpStatSN++;
2562 }
2563 else
2564 {
2565 rc = VERR_PARSE_ERROR;
2566 iscsiDumpPacket(pImage, (PISCSIREQ)paRes, cnRes, rc, false /* fRequest */);
2567 break;
2568 }
2569 }
2570
2571 if (pcvResSeg[4] != ISCSI_TASK_TAG_RSVD)
2572 {
2573 /*
2574 * This is a response from the target for a request from the initiator.
2575 * Get the request and update its state.
2576 */
2577 rc = iscsiRecvPDUUpdateRequest(pImage, paRes, cnRes);
2578 /* Try to send more PDUs now that we updated the MaxCmdSN field */
2579 if ( RT_SUCCESS(rc)
2580 && !pImage->pIScsiPDUTxCur)
2581 rc = iscsiSendPDUAsync(pImage);
2582 }
2583 else
2584 {
2585 /* This is a target initiated request (we handle only NOP-In request at the moment). */
2586 if ( cmd == ISCSIOP_NOP_IN
2587 && RT_N2H_U32(pcvResSeg[5]) != ISCSI_TASK_TAG_RSVD)
2588 {
2589 PISCSIPDUTX pIScsiPDUTx;
2590 uint32_t cnISCSIReq;
2591 uint32_t *paReqBHS;
2592
2593 LogFlowFunc(("Sending NOP-Out\n"));
2594
2595 /* Allocate a new PDU initialize it and put onto the waiting list. */
2596 pIScsiPDUTx = (PISCSIPDUTX)RTMemAllocZ(sizeof(ISCSIPDUTX));
2597 if (!pIScsiPDUTx)
2598 {
2599 rc = VERR_NO_MEMORY;
2600 break;
2601 }
2602 paReqBHS = &pIScsiPDUTx->aBHS[0];
2603 paReqBHS[0] = RT_H2N_U32(ISCSI_IMMEDIATE_DELIVERY_BIT | ISCSI_FINAL_BIT | ISCSIOP_NOP_OUT);
2604 paReqBHS[1] = RT_H2N_U32(0); /* TotalAHSLength=0,DataSementLength=0 */
2605 paReqBHS[2] = pcvResSeg[2]; /* copy LUN from NOP-In */
2606 paReqBHS[3] = pcvResSeg[3]; /* copy LUN from NOP-In */
2607 paReqBHS[4] = RT_H2N_U32(ISCSI_TASK_TAG_RSVD); /* ITT, reply */
2608 paReqBHS[5] = pcvResSeg[5]; /* copy TTT from NOP-In */
2609 paReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
2610 paReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
2611 paReqBHS[8] = 0; /* reserved */
2612 paReqBHS[9] = 0; /* reserved */
2613 paReqBHS[10] = 0; /* reserved */
2614 paReqBHS[11] = 0; /* reserved */
2615
2616 cnISCSIReq = 0;
2617 pIScsiPDUTx->aISCSIReq[cnISCSIReq].pvSeg = paReqBHS;
2618 pIScsiPDUTx->aISCSIReq[cnISCSIReq].cbSeg = sizeof(pIScsiPDUTx->aBHS);
2619 cnISCSIReq++;
2620 pIScsiPDUTx->cbSgLeft = sizeof(pIScsiPDUTx->aBHS);
2621 RTSgBufInit(&pIScsiPDUTx->SgBuf, pIScsiPDUTx->aISCSIReq, cnISCSIReq);
2622
2623 /*
2624 * Link the PDU to the list.
2625 * Insert at the front of the list to send the response as soon as possible
2626 * to avoid frequent reconnects for a slow connection when there are many PDUs
2627 * waiting.
2628 */
2629 iscsiPDUTxAdd(pImage, pIScsiPDUTx, true /* fFront */);
2630
2631 /* Start transfer of a PDU if there is no one active at the moment. */
2632 if (!pImage->pIScsiPDUTxCur)
2633 rc = iscsiSendPDUAsync(pImage);
2634 }
2635 }
2636 } while (0);
2637 }
2638 else
2639 iscsiDumpPacket(pImage, (PISCSIREQ)paRes, cnRes, rc, false /* fRequest */);
2640
2641 return rc;
2642}
2643
2644/**
2645 * Check the static (not dependent on the connection/session state) validity of an iSCSI response PDU.
2646 *
2647 * @returns VBOX status
2648 * @param paRes Pointer to array of iSCSI response sections.
2649 * @param cnRes Number of valid iSCSI response sections in the array.
2650 */
2651static int iscsiValidatePDU(PISCSIRES paRes, uint32_t cnRes)
2652{
2653 RT_NOREF1(cnRes);
2654 const uint32_t *pcrgResBHS;
2655 uint32_t hw0;
2656 Assert(cnRes >= 1);
2657 Assert(paRes[0].cbSeg >= ISCSI_BHS_SIZE);
2658
2659 LogFlowFunc(("paRes=%#p cnRes=%u\n", paRes, cnRes));
2660
2661 pcrgResBHS = (const uint32_t *)(paRes[0].pvSeg);
2662 hw0 = RT_N2H_U32(pcrgResBHS[0]);
2663 switch (hw0 & ISCSIOP_MASK)
2664 {
2665 case ISCSIOP_NOP_IN:
2666 /* NOP-In responses must not be split into several PDUs nor it may contain
2667 * ping data for target-initiated pings nor may both task tags be valid task tags. */
2668 if ( (hw0 & ISCSI_FINAL_BIT) == 0
2669 || ( RT_N2H_U32(pcrgResBHS[4]) == ISCSI_TASK_TAG_RSVD
2670 && RT_N2H_U32(pcrgResBHS[1]) != 0)
2671 || ( RT_N2H_U32(pcrgResBHS[4]) != ISCSI_TASK_TAG_RSVD
2672 && RT_N2H_U32(pcrgResBHS[5]) != ISCSI_TASK_TAG_RSVD))
2673 return VERR_PARSE_ERROR;
2674 break;
2675 case ISCSIOP_SCSI_RES:
2676 /* SCSI responses must not be split into several PDUs nor must the residual
2677 * bits be contradicting each other nor may the residual bits be set for PDUs
2678 * containing anything else but a completed command response. Underflow
2679 * is no reason for declaring a PDU as invalid, as the target may choose
2680 * to return less data than we assume to get. */
2681 if ( (hw0 & ISCSI_FINAL_BIT) == 0
2682 || ((hw0 & ISCSI_BI_READ_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_BI_READ_RESIDUAL_UNFL_BIT))
2683 || ((hw0 & ISCSI_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_RESIDUAL_UNFL_BIT))
2684 || ( ((hw0 & ISCSI_SCSI_RESPONSE_MASK) == 0)
2685 && ((hw0 & ISCSI_SCSI_STATUS_MASK) == SCSI_STATUS_OK)
2686 && (hw0 & ( ISCSI_BI_READ_RESIDUAL_OVFL_BIT | ISCSI_BI_READ_RESIDUAL_UNFL_BIT
2687 | ISCSI_RESIDUAL_OVFL_BIT))))
2688 return VERR_PARSE_ERROR;
2689 else
2690 LogFlowFunc(("good SCSI response, first word %#08x\n", RT_N2H_U32(pcrgResBHS[0])));
2691 break;
2692 case ISCSIOP_LOGIN_RES:
2693 /* Login responses must not contain contradicting transit and continue bits. */
2694 if ((hw0 & ISCSI_CONTINUE_BIT) && ((hw0 & ISCSI_TRANSIT_BIT) != 0))
2695 return VERR_PARSE_ERROR;
2696 break;
2697 case ISCSIOP_TEXT_RES:
2698 /* Text responses must not contain contradicting final and continue bits nor
2699 * may the final bit be set for PDUs containing a target transfer tag other than
2700 * the reserved transfer tag (and vice versa). */
2701 if ( (((hw0 & ISCSI_CONTINUE_BIT) && (hw0 & ISCSI_FINAL_BIT) != 0))
2702 || (((hw0 & ISCSI_FINAL_BIT) && (RT_N2H_U32(pcrgResBHS[5]) != ISCSI_TASK_TAG_RSVD)))
2703 || (((hw0 & ISCSI_FINAL_BIT) == 0) && (RT_N2H_U32(pcrgResBHS[5]) == ISCSI_TASK_TAG_RSVD)))
2704 return VERR_PARSE_ERROR;
2705 break;
2706 case ISCSIOP_SCSI_DATA_IN:
2707 /* SCSI Data-in responses must not contain contradicting residual bits when
2708 * status bit is set. */
2709 if ((hw0 & ISCSI_STATUS_BIT) && (hw0 & ISCSI_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_RESIDUAL_UNFL_BIT))
2710 return VERR_PARSE_ERROR;
2711 break;
2712 case ISCSIOP_LOGOUT_RES:
2713 /* Logout responses must not have the final bit unset and may not contain any
2714 * data or additional header segments. */
2715 if ( ((hw0 & ISCSI_FINAL_BIT) == 0)
2716 || (RT_N2H_U32(pcrgResBHS[1]) != 0))
2717 return VERR_PARSE_ERROR;
2718 break;
2719 case ISCSIOP_ASYN_MSG:
2720 /* Asynchronous Messages must not have the final bit unset and may not contain
2721 * an initiator task tag. */
2722 if ( ((hw0 & ISCSI_FINAL_BIT) == 0)
2723 || (RT_N2H_U32(pcrgResBHS[4]) != ISCSI_TASK_TAG_RSVD))
2724 return VERR_PARSE_ERROR;
2725 break;
2726 case ISCSIOP_SCSI_TASKMGMT_RES:
2727 case ISCSIOP_R2T:
2728 case ISCSIOP_REJECT:
2729 default:
2730 /* Do some logging, ignore PDU. */
2731 LogFlowFunc(("ignore unhandled PDU, first word %#08x\n", RT_N2H_U32(pcrgResBHS[0])));
2732 return VERR_PARSE_ERROR;
2733 }
2734 /* A target must not send PDUs with MaxCmdSN less than ExpCmdSN-1. */
2735
2736 if (serial_number_less(RT_N2H_U32(pcrgResBHS[8]), RT_N2H_U32(pcrgResBHS[7])-1))
2737 return VERR_PARSE_ERROR;
2738
2739 return VINF_SUCCESS;
2740}
2741
2742
2743/**
2744 * Prepares a PDU to transfer for the given command and adds it to the list.
2745 */
2746static int iscsiPDUTxPrepare(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd)
2747{
2748 int rc = VINF_SUCCESS;
2749 uint32_t *paReqBHS;
2750 size_t cbData = 0;
2751 size_t cbSegs = 0;
2752 PSCSIREQ pScsiReq;
2753 PISCSIPDUTX pIScsiPDU = NULL;
2754
2755 LogFlowFunc(("pImage=%#p pIScsiCmd=%#p\n", pImage, pIScsiCmd));
2756
2757 Assert(pIScsiCmd->enmCmdType == ISCSICMDTYPE_REQ);
2758
2759 pIScsiCmd->Itt = iscsiNewITT(pImage);
2760 pScsiReq = pIScsiCmd->CmdType.ScsiReq.pScsiReq;
2761
2762 if (pScsiReq->cT2ISegs)
2763 RTSgBufInit(&pScsiReq->SgBufT2I, pScsiReq->paT2ISegs, pScsiReq->cT2ISegs);
2764
2765 /*
2766 * Allocate twice as much entries as required for padding (worst case).
2767 * The additional segment is for the BHS.
2768 */
2769 size_t cI2TSegs = 2*(pScsiReq->cI2TSegs + 1);
2770 pIScsiPDU = (PISCSIPDUTX)RTMemAllocZ(RT_OFFSETOF(ISCSIPDUTX, aISCSIReq[cI2TSegs]));
2771 if (!pIScsiPDU)
2772 return VERR_NO_MEMORY;
2773
2774 pIScsiPDU->pIScsiCmd = pIScsiCmd;
2775
2776 if (pScsiReq->enmXfer == SCSIXFER_FROM_TARGET)
2777 cbData = (uint32_t)pScsiReq->cbT2IData;
2778 else
2779 cbData = (uint32_t)pScsiReq->cbI2TData;
2780
2781 paReqBHS = pIScsiPDU->aBHS;
2782
2783 /* Setup the BHS. */
2784 paReqBHS[0] = RT_H2N_U32( ISCSI_FINAL_BIT | ISCSI_TASK_ATTR_SIMPLE | ISCSIOP_SCSI_CMD
2785 | (pScsiReq->enmXfer << 21)); /* I=0,F=1,Attr=Simple */
2786 paReqBHS[1] = RT_H2N_U32(0x00000000 | ((uint32_t)pScsiReq->cbI2TData & 0xffffff)); /* TotalAHSLength=0 */
2787 paReqBHS[2] = RT_H2N_U32(pImage->LUN >> 32);
2788 paReqBHS[3] = RT_H2N_U32(pImage->LUN & 0xffffffff);
2789 paReqBHS[4] = pIScsiCmd->Itt;
2790 paReqBHS[5] = RT_H2N_U32((uint32_t)cbData); Assert((uint32_t)cbData == cbData);
2791 paReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
2792 paReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
2793 memcpy(paReqBHS + 8, pScsiReq->abCDB, pScsiReq->cbCDB);
2794
2795 pIScsiPDU->CmdSN = pImage->CmdSN;
2796 pImage->CmdSN++;
2797
2798 /* Setup the S/G buffers. */
2799 uint32_t cnISCSIReq = 0;
2800 pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg = sizeof(pIScsiPDU->aBHS);
2801 pIScsiPDU->aISCSIReq[cnISCSIReq].pvSeg = pIScsiPDU->aBHS;
2802 cnISCSIReq++;
2803 cbSegs = sizeof(pIScsiPDU->aBHS);
2804 /* Padding is not necessary for the BHS. */
2805
2806 if (pScsiReq->cbI2TData)
2807 {
2808 for (unsigned cSeg = 0; cSeg < pScsiReq->cI2TSegs; cSeg++)
2809 {
2810 Assert(cnISCSIReq < cI2TSegs);
2811 pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg = pScsiReq->paI2TSegs[cSeg].cbSeg;
2812 pIScsiPDU->aISCSIReq[cnISCSIReq].pvSeg = pScsiReq->paI2TSegs[cSeg].pvSeg;
2813 cbSegs += pScsiReq->paI2TSegs[cSeg].cbSeg;
2814 cnISCSIReq++;
2815
2816 /* Add padding if necessary. */
2817 if (pScsiReq->paI2TSegs[cSeg].cbSeg & 3)
2818 {
2819 Assert(cnISCSIReq < cI2TSegs);
2820 pIScsiPDU->aISCSIReq[cnISCSIReq].pvSeg = &pImage->aPadding[0];
2821 pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg = 4 - (pScsiReq->paI2TSegs[cSeg].cbSeg & 3);
2822 cbSegs += pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg;
2823 cnISCSIReq++;
2824 }
2825 }
2826 }
2827
2828 pIScsiPDU->cISCSIReq = cnISCSIReq;
2829 pIScsiPDU->cbSgLeft = cbSegs;
2830 RTSgBufInit(&pIScsiPDU->SgBuf, pIScsiPDU->aISCSIReq, cnISCSIReq);
2831
2832 /* Link the PDU to the list. */
2833 iscsiPDUTxAdd(pImage, pIScsiPDU, false /* fFront */);
2834
2835 /* Start transfer of a PDU if there is no one active at the moment. */
2836 if (!pImage->pIScsiPDUTxCur)
2837 rc = iscsiSendPDUAsync(pImage);
2838
2839 return rc;
2840}
2841
2842
2843/**
2844 * Updates the state of a request from the PDU we received.
2845 *
2846 * @return VBox status code.
2847 * @param pImage iSCSI connection state to use.
2848 * @param paRes Pointer to array of iSCSI response sections.
2849 * @param cnRes Number of valid iSCSI response sections in the array.
2850 */
2851static int iscsiRecvPDUUpdateRequest(PISCSIIMAGE pImage, PISCSIRES paRes, uint32_t cnRes)
2852{
2853 int rc = VINF_SUCCESS;
2854 PISCSICMD pIScsiCmd;
2855 uint32_t *paResBHS;
2856
2857 LogFlowFunc(("pImage=%#p paRes=%#p cnRes=%u\n", pImage, paRes, cnRes));
2858
2859 Assert(cnRes == 1);
2860 Assert(paRes[0].cbSeg >= ISCSI_BHS_SIZE);
2861
2862 paResBHS = (uint32_t *)paRes[0].pvSeg;
2863
2864 pIScsiCmd = iscsiCmdGetFromItt(pImage, paResBHS[4]);
2865
2866 if (pIScsiCmd)
2867 {
2868 bool final = false;
2869 PSCSIREQ pScsiReq;
2870
2871 LogFlow(("Found SCSI command %#p for Itt=%#u\n", pIScsiCmd, paResBHS[4]));
2872
2873 Assert(pIScsiCmd->enmCmdType == ISCSICMDTYPE_REQ);
2874 pScsiReq = pIScsiCmd->CmdType.ScsiReq.pScsiReq;
2875
2876 final = !!(RT_N2H_U32(paResBHS[0]) & ISCSI_FINAL_BIT);
2877 ISCSIOPCODE cmd = (ISCSIOPCODE)(RT_N2H_U32(paResBHS[0]) & ISCSIOP_MASK);
2878 if (cmd == ISCSIOP_SCSI_RES)
2879 {
2880 /* This is the final PDU which delivers the status (and may be omitted if
2881 * the last Data-In PDU included successful completion status). Note
2882 * that ExpStatSN has been bumped already in iscsiRecvPDU. */
2883 if (!final || ((RT_N2H_U32(paResBHS[0]) & 0x0000ff00) != 0) || (RT_N2H_U32(paResBHS[6]) != pImage->ExpStatSN - 1))
2884 {
2885 /* SCSI Response in the wrong place or with a (target) failure. */
2886 LogFlow(("Wrong ExpStatSN value in PDU\n"));
2887 rc = VERR_PARSE_ERROR;
2888 }
2889 else
2890 {
2891 pScsiReq->status = RT_N2H_U32(paResBHS[0]) & 0x000000ff;
2892 size_t cbData = RT_N2H_U32(paResBHS[1]) & 0x00ffffff;
2893 void *pvSense = (uint8_t *)paRes[0].pvSeg + ISCSI_BHS_SIZE;
2894
2895 if (cbData >= 2)
2896 {
2897 uint32_t cbStat = RT_N2H_U32(((uint32_t *)pvSense)[0]) >> 16;
2898 if (cbStat + 2 > cbData)
2899 {
2900 rc = VERR_BUFFER_OVERFLOW;
2901 }
2902 else
2903 {
2904 /* Truncate sense data if it doesn't fit into the buffer. */
2905 pScsiReq->cbSense = RT_MIN(cbStat, pScsiReq->cbSense);
2906 memcpy(pScsiReq->abSense, (uint8_t *)pvSense + 2,
2907 RT_MIN(paRes[0].cbSeg - ISCSI_BHS_SIZE - 2, pScsiReq->cbSense));
2908 }
2909 }
2910 else if (cbData == 1)
2911 rc = VERR_PARSE_ERROR;
2912 else
2913 pScsiReq->cbSense = 0;
2914 }
2915 iscsiCmdComplete(pImage, pIScsiCmd, rc);
2916 }
2917 else if (cmd == ISCSIOP_SCSI_DATA_IN)
2918 {
2919 /* A Data-In PDU carries some data that needs to be added to the received
2920 * data in response to the command. There may be both partial and complete
2921 * Data-In PDUs, so collect data until the status is included or the status
2922 * is sent in a separate SCSI Result frame (see above). */
2923 size_t cbData = RT_N2H_U32(paResBHS[1]) & 0x00ffffff;
2924 void *pvData = (uint8_t *)paRes[0].pvSeg + ISCSI_BHS_SIZE;
2925
2926 if (final && cbData > pScsiReq->cbT2IData)
2927 {
2928 /* The received PDU is bigger than what we requested.
2929 * Must not happen under normal circumstances and is a target error. */
2930 rc = VERR_BUFFER_OVERFLOW;
2931 }
2932 else
2933 {
2934 /* Copy data from the received PDU into the T2I segments. */
2935 size_t cbCopied = RTSgBufCopyFromBuf(&pScsiReq->SgBufT2I, pvData, cbData);
2936 Assert(cbCopied == cbData); NOREF(cbCopied);
2937
2938 if (final && (RT_N2H_U32(paResBHS[0]) & ISCSI_STATUS_BIT) != 0)
2939 {
2940 pScsiReq->status = RT_N2H_U32(paResBHS[0]) & 0x000000ff;
2941 pScsiReq->cbSense = 0;
2942 iscsiCmdComplete(pImage, pIScsiCmd, VINF_SUCCESS);
2943 }
2944 }
2945 }
2946 else
2947 rc = VERR_PARSE_ERROR;
2948 }
2949
2950 /* Log any errors here but ignore the PDU. */
2951 if (RT_FAILURE(rc))
2952 {
2953 LogRel(("iSCSI: Received malformed PDU from target %s (rc=%Rrc), ignoring\n", pImage->pszTargetName, rc));
2954 iscsiDumpPacket(pImage, (PISCSIREQ)paRes, cnRes, rc, false /* fRequest */);
2955 rc = VINF_SUCCESS;
2956 }
2957
2958 return rc;
2959}
2960
2961/**
2962 * Appends a key-value pair to the buffer. Normal ASCII strings (cbValue == 0) and large binary values
2963 * of a given length (cbValue > 0) are directly supported. Other value types must be converted to ASCII
2964 * by the caller. Strings must be in UTF-8 encoding.
2965 *
2966 * @returns VBOX status
2967 * @param pbBuf Pointer to the key-value buffer.
2968 * @param cbBuf Length of the key-value buffer.
2969 * @param pcbBufCurr Currently used portion of the key-value buffer.
2970 * @param pcszKey Pointer to a string containing the key.
2971 * @param pcszValue Pointer to either a string containing the value or to a large binary value.
2972 * @param cbValue Length of the binary value if applicable.
2973 */
2974static int iscsiTextAddKeyValue(uint8_t *pbBuf, size_t cbBuf, size_t *pcbBufCurr, const char *pcszKey,
2975 const char *pcszValue, size_t cbValue)
2976{
2977 size_t cbBufTmp = *pcbBufCurr;
2978 size_t cbKey = strlen(pcszKey);
2979 size_t cbValueEnc;
2980 uint8_t *pbCurr;
2981
2982 if (cbValue == 0)
2983 cbValueEnc = strlen(pcszValue);
2984 else
2985 cbValueEnc = cbValue * 2 + 2; /* 2 hex bytes per byte, 2 bytes prefix */
2986
2987 if (cbBuf < cbBufTmp + cbKey + 1 + cbValueEnc + 1)
2988 {
2989 /* Buffer would overflow, signal error. */
2990 return VERR_BUFFER_OVERFLOW;
2991 }
2992
2993 /*
2994 * Append a key=value pair (zero terminated string) to the end of the buffer.
2995 */
2996 pbCurr = pbBuf + cbBufTmp;
2997 memcpy(pbCurr, pcszKey, cbKey);
2998 pbCurr += cbKey;
2999 *pbCurr++ = '=';
3000 if (cbValue == 0)
3001 {
3002 memcpy(pbCurr, pcszValue, cbValueEnc);
3003 pbCurr += cbValueEnc;
3004 }
3005 else
3006 {
3007 *pbCurr++ = '0';
3008 *pbCurr++ = 'x';
3009 for (uint32_t i = 0; i < cbValue; i++)
3010 {
3011 uint8_t b;
3012 b = pcszValue[i];
3013 *pbCurr++ = NUM_2_HEX(b >> 4);
3014 *pbCurr++ = NUM_2_HEX(b & 0xf);
3015 }
3016 }
3017 *pbCurr = '\0';
3018 *pcbBufCurr = cbBufTmp + cbKey + 1 + cbValueEnc + 1;
3019
3020 return VINF_SUCCESS;
3021}
3022
3023
3024/**
3025 * Retrieve the value for a given key from the key=value buffer.
3026 *
3027 * @returns VBox status code.
3028 * @param pbBuf Buffer containing key=value pairs.
3029 * @param cbBuf Length of buffer with key=value pairs.
3030 * @param pcszKey Pointer to key for which to retrieve the value.
3031 * @param ppcszValue Pointer to value string pointer.
3032 */
3033static int iscsiTextGetKeyValue(const uint8_t *pbBuf, size_t cbBuf, const char *pcszKey, const char **ppcszValue)
3034{
3035 size_t cbKey = strlen(pcszKey);
3036
3037 while (cbBuf != 0)
3038 {
3039 size_t cbKeyValNull = strlen((const char *)pbBuf) + 1;
3040
3041 if (strncmp(pcszKey, (const char *)pbBuf, cbKey) == 0 && pbBuf[cbKey] == '=')
3042 {
3043 *ppcszValue = (const char *)(pbBuf + cbKey + 1);
3044 return VINF_SUCCESS;
3045 }
3046 pbBuf += cbKeyValNull;
3047 cbBuf -= cbKeyValNull;
3048 }
3049 return VERR_INVALID_NAME;
3050}
3051
3052
3053/**
3054 * Convert a long-binary value from a value string to the binary representation.
3055 *
3056 * @returns VBOX status
3057 * @param pcszValue Pointer to a string containing the textual value representation.
3058 * @param pbValue Pointer to the value buffer for the binary value.
3059 * @param pcbValue In: length of value buffer, out: actual length of binary value.
3060 */
3061static int iscsiStrToBinary(const char *pcszValue, uint8_t *pbValue, size_t *pcbValue)
3062{
3063 size_t cbValue = *pcbValue;
3064 char c1, c2, c3, c4;
3065 Assert(cbValue >= 1);
3066
3067 if (strlen(pcszValue) < 3)
3068 return VERR_PARSE_ERROR;
3069 if (*pcszValue++ != '0')
3070 return VERR_PARSE_ERROR;
3071 switch (*pcszValue++)
3072 {
3073 case 'x':
3074 case 'X':
3075 if (strlen(pcszValue) & 1)
3076 {
3077 c1 = *pcszValue++;
3078 *pbValue++ = HEX_2_NUM(c1);
3079 cbValue--;
3080 }
3081 while (*pcszValue != '\0')
3082 {
3083 if (cbValue == 0)
3084 return VERR_BUFFER_OVERFLOW;
3085 c1 = *pcszValue++;
3086 if ((c1 < '0' || c1 > '9') && (c1 < 'a' || c1 > 'f') && (c1 < 'A' || c1 > 'F'))
3087 return VERR_PARSE_ERROR;
3088 c2 = *pcszValue++;
3089 if ((c2 < '0' || c2 > '9') && (c2 < 'a' || c2 > 'f') && (c2 < 'A' || c2 > 'F'))
3090 return VERR_PARSE_ERROR;
3091 *pbValue++ = (HEX_2_NUM(c1) << 4) | HEX_2_NUM(c2);
3092 cbValue--;
3093 }
3094 *pcbValue -= cbValue;
3095 break;
3096 case 'b':
3097 case 'B':
3098 if ((strlen(pcszValue) & 3) != 0)
3099 return VERR_PARSE_ERROR;
3100 while (*pcszValue != '\0')
3101 {
3102 uint32_t temp;
3103 if (cbValue == 0)
3104 return VERR_BUFFER_OVERFLOW;
3105 c1 = *pcszValue++;
3106 if ((c1 < 'A' || c1 > 'Z') && (c1 < 'a' || c1 >'z') && (c1 < '0' || c1 > '9') && (c1 != '+') && (c1 != '/'))
3107 return VERR_PARSE_ERROR;
3108 c2 = *pcszValue++;
3109 if ((c2 < 'A' || c2 > 'Z') && (c2 < 'a' || c2 >'z') && (c2 < '0' || c2 > '9') && (c2 != '+') && (c2 != '/'))
3110 return VERR_PARSE_ERROR;
3111 c3 = *pcszValue++;
3112 if ((c3 < 'A' || c3 > 'Z') && (c3 < 'a' || c3 >'z') && (c3 < '0' || c3 > '9') && (c3 != '+') && (c3 != '/') && (c3 != '='))
3113 return VERR_PARSE_ERROR;
3114 c4 = *pcszValue++;
3115 if ( (c3 == '=' && c4 != '=')
3116 || ((c4 < 'A' || c4 > 'Z') && (c4 < 'a' || c4 >'z') && (c4 < '0' || c4 > '9') && (c4 != '+') && (c4 != '/') && (c4 != '=')))
3117 return VERR_PARSE_ERROR;
3118 temp = (B64_2_NUM(c1) << 18) | (B64_2_NUM(c2) << 12);
3119 if (c3 == '=') {
3120 if (*pcszValue != '\0')
3121 return VERR_PARSE_ERROR;
3122 *pbValue++ = temp >> 16;
3123 cbValue--;
3124 } else {
3125 temp |= B64_2_NUM(c3) << 6;
3126 if (c4 == '=') {
3127 if (*pcszValue != '\0')
3128 return VERR_PARSE_ERROR;
3129 if (cbValue < 2)
3130 return VERR_BUFFER_OVERFLOW;
3131 *pbValue++ = temp >> 16;
3132 *pbValue++ = (temp >> 8) & 0xff;
3133 cbValue -= 2;
3134 }
3135 else
3136 {
3137 temp |= B64_2_NUM(c4);
3138 if (cbValue < 3)
3139 return VERR_BUFFER_OVERFLOW;
3140 *pbValue++ = temp >> 16;
3141 *pbValue++ = (temp >> 8) & 0xff;
3142 *pbValue++ = temp & 0xff;
3143 cbValue -= 3;
3144 }
3145 }
3146 }
3147 *pcbValue -= cbValue;
3148 break;
3149 default:
3150 return VERR_PARSE_ERROR;
3151 }
3152 return VINF_SUCCESS;
3153}
3154
3155
3156/**
3157 * Retrieve the relevant parameter values and update the initiator state.
3158 *
3159 * @returns VBox status code.
3160 * @param pImage Current iSCSI initiator state.
3161 * @param pbBuf Buffer containing key=value pairs.
3162 * @param cbBuf Length of buffer with key=value pairs.
3163 */
3164static int iscsiUpdateParameters(PISCSIIMAGE pImage, const uint8_t *pbBuf, size_t cbBuf)
3165{
3166 int rc;
3167 const char *pcszMaxRecvDataSegmentLength = NULL;
3168 const char *pcszMaxBurstLength = NULL;
3169 const char *pcszFirstBurstLength = NULL;
3170 rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "MaxRecvDataSegmentLength", &pcszMaxRecvDataSegmentLength);
3171 if (rc == VERR_INVALID_NAME)
3172 rc = VINF_SUCCESS;
3173 if (RT_FAILURE(rc))
3174 return VERR_PARSE_ERROR;
3175 rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "MaxBurstLength", &pcszMaxBurstLength);
3176 if (rc == VERR_INVALID_NAME)
3177 rc = VINF_SUCCESS;
3178 if (RT_FAILURE(rc))
3179 return VERR_PARSE_ERROR;
3180 rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "FirstBurstLength", &pcszFirstBurstLength);
3181 if (rc == VERR_INVALID_NAME)
3182 rc = VINF_SUCCESS;
3183 if (RT_FAILURE(rc))
3184 return VERR_PARSE_ERROR;
3185 if (pcszMaxRecvDataSegmentLength)
3186 {
3187 uint32_t cb = pImage->cbSendDataLength;
3188 rc = RTStrToUInt32Full(pcszMaxRecvDataSegmentLength, 0, &cb);
3189 AssertRC(rc);
3190 pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
3191 }
3192 if (pcszMaxBurstLength)
3193 {
3194 uint32_t cb = pImage->cbSendDataLength;
3195 rc = RTStrToUInt32Full(pcszMaxBurstLength, 0, &cb);
3196 AssertRC(rc);
3197 pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
3198 }
3199 if (pcszFirstBurstLength)
3200 {
3201 uint32_t cb = pImage->cbSendDataLength;
3202 rc = RTStrToUInt32Full(pcszFirstBurstLength, 0, &cb);
3203 AssertRC(rc);
3204 pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
3205 }
3206 return VINF_SUCCESS;
3207}
3208
3209
3210static bool serial_number_less(uint32_t s1, uint32_t s2)
3211{
3212 return (s1 < s2 && s2 - s1 < 0x80000000) || (s1 > s2 && s1 - s2 > 0x80000000);
3213}
3214
3215static bool serial_number_greater(uint32_t s1, uint32_t s2)
3216{
3217 return (s1 < s2 && s2 - s1 > 0x80000000) || (s1 > s2 && s1 - s2 < 0x80000000);
3218}
3219
3220
3221#ifdef IMPLEMENT_TARGET_AUTH
3222static void chap_md5_generate_challenge(uint8_t *pbChallenge, size_t *pcbChallenge)
3223{
3224 uint8_t cbChallenge;
3225
3226 cbChallenge = RTrand_U8(CHAP_MD5_CHALLENGE_MIN, CHAP_MD5_CHALLENGE_MAX);
3227 RTrand_bytes(pbChallenge, cbChallenge);
3228 *pcbChallenge = cbChallenge;
3229}
3230#endif
3231
3232
3233static void chap_md5_compute_response(uint8_t *pbResponse, uint8_t id, const uint8_t *pbChallenge, size_t cbChallenge,
3234 const uint8_t *pbSecret, size_t cbSecret)
3235{
3236 RTMD5CONTEXT ctx;
3237 uint8_t bId;
3238
3239 bId = id;
3240 RTMd5Init(&ctx);
3241 RTMd5Update(&ctx, &bId, 1);
3242 RTMd5Update(&ctx, pbSecret, cbSecret);
3243 RTMd5Update(&ctx, pbChallenge, cbChallenge);
3244 RTMd5Final(pbResponse, &ctx);
3245}
3246
3247/**
3248 * Internal. - Wrapper around the extended select callback of the net interface.
3249 */
3250DECLINLINE(int) iscsiIoThreadWait(PISCSIIMAGE pImage, RTMSINTERVAL cMillies, uint32_t fEvents, uint32_t *pfEvents)
3251{
3252 return pImage->pIfNet->pfnSelectOneEx(pImage->Socket, fEvents, pfEvents, cMillies);
3253}
3254
3255/**
3256 * Internal. - Pokes a thread waiting for I/O.
3257 */
3258DECLINLINE(int) iscsiIoThreadPoke(PISCSIIMAGE pImage)
3259{
3260 return pImage->pIfNet->pfnPoke(pImage->Socket);
3261}
3262
3263/**
3264 * Internal. - Get the next request from the queue.
3265 */
3266DECLINLINE(PISCSICMD) iscsiCmdGet(PISCSIIMAGE pImage)
3267{
3268 int rc;
3269 PISCSICMD pIScsiCmd = NULL;
3270
3271 rc = RTSemMutexRequest(pImage->MutexReqQueue, RT_INDEFINITE_WAIT);
3272 AssertRC(rc);
3273
3274 pIScsiCmd = pImage->pScsiReqQueue;
3275 if (pIScsiCmd)
3276 {
3277 pImage->pScsiReqQueue = pIScsiCmd->pNext;
3278 pIScsiCmd->pNext = NULL;
3279 }
3280
3281 rc = RTSemMutexRelease(pImage->MutexReqQueue);
3282 AssertRC(rc);
3283
3284 return pIScsiCmd;
3285}
3286
3287
3288/**
3289 * Internal. - Adds the given command to the queue.
3290 */
3291DECLINLINE(int) iscsiCmdPut(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd)
3292{
3293 int rc = RTSemMutexRequest(pImage->MutexReqQueue, RT_INDEFINITE_WAIT);
3294 AssertRC(rc);
3295
3296 pIScsiCmd->pNext = pImage->pScsiReqQueue;
3297 pImage->pScsiReqQueue = pIScsiCmd;
3298
3299 rc = RTSemMutexRelease(pImage->MutexReqQueue);
3300 AssertRC(rc);
3301
3302 iscsiIoThreadPoke(pImage);
3303
3304 return rc;
3305}
3306
3307/**
3308 * Internal. - Completes the request with the appropriate action.
3309 * Synchronous requests are completed with waking up the thread
3310 * and asynchronous ones by continuing the associated I/O context.
3311 */
3312static void iscsiCmdComplete(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd, int rcCmd)
3313{
3314 LogFlowFunc(("pImage=%#p pIScsiCmd=%#p rcCmd=%Rrc\n", pImage, pIScsiCmd, rcCmd));
3315
3316 /* Remove from the table first. */
3317 iscsiCmdRemove(pImage, pIScsiCmd->Itt);
3318
3319 /* Call completion callback. */
3320 pIScsiCmd->pfnComplete(pImage, rcCmd, pIScsiCmd->pvUser);
3321
3322 /* Free command structure. */
3323#ifdef DEBUG
3324 memset(pIScsiCmd, 0xff, sizeof(ISCSICMD));
3325#endif
3326 RTMemFree(pIScsiCmd);
3327}
3328
3329/**
3330 * Clears all RX/TX PDU states and returns the command for the current
3331 * pending TX PDU if existing.
3332 *
3333 * @returns Pointer to the iSCSI command for the current PDU transmitted or NULL
3334 * if none is waiting.
3335 * @param pImage iSCSI connection state.
3336 */
3337static PISCSICMD iscsiPDURxTxClear(PISCSIIMAGE pImage)
3338{
3339 PISCSICMD pIScsiCmdHead = NULL;
3340 PISCSIPDUTX pIScsiPDUTx = NULL;
3341
3342 /* Reset PDU we are receiving. */
3343 iscsiRecvPDUReset(pImage);
3344
3345 /*
3346 * Abort all PDUs we are about to transmit,
3347 * the command need a new Itt if the relogin is successful.
3348 */
3349 while (pImage->pIScsiPDUTxHead)
3350 {
3351 pIScsiPDUTx = pImage->pIScsiPDUTxHead;
3352 pImage->pIScsiPDUTxHead = pIScsiPDUTx->pNext;
3353
3354 PISCSICMD pIScsiCmd = pIScsiPDUTx->pIScsiCmd;
3355 if (pIScsiCmd)
3356 {
3357 /* Place on command list. */
3358 pIScsiCmd->pNext = pIScsiCmdHead;
3359 pIScsiCmdHead = pIScsiCmd;
3360 }
3361 RTMemFree(pIScsiPDUTx);
3362 }
3363
3364 /* Clear the tail pointer (safety precaution). */
3365 pImage->pIScsiPDUTxTail = NULL;
3366
3367 /* Clear the current PDU too. */
3368 if (pImage->pIScsiPDUTxCur)
3369 {
3370 pIScsiPDUTx = pImage->pIScsiPDUTxCur;
3371
3372 pImage->pIScsiPDUTxCur = NULL;
3373 PISCSICMD pIScsiCmd = pIScsiPDUTx->pIScsiCmd;
3374 if (pIScsiCmd)
3375 {
3376 pIScsiCmd->pNext = pIScsiCmdHead;
3377 pIScsiCmdHead = pIScsiCmd;
3378 }
3379 RTMemFree(pIScsiPDUTx);
3380 }
3381
3382 return pIScsiCmdHead;
3383}
3384
3385/**
3386 * Rests the iSCSI connection state and returns a list of iSCSI commands pending
3387 * when this was called.
3388 *
3389 * @returns Pointer to the head of the pending iSCSI command list.
3390 * @param pImage iSCSI connection state.
3391 */
3392static PISCSICMD iscsiReset(PISCSIIMAGE pImage)
3393{
3394 PISCSICMD pIScsiCmdHead = NULL;
3395 PISCSICMD pIScsiCmdCur = NULL;
3396
3397 /* Clear all in flight PDUs. */
3398 pIScsiCmdHead = iscsiPDURxTxClear(pImage);
3399
3400 /*
3401 * Get all commands which are waiting for a response
3402 * They need to be resend too after a successful reconnect.
3403 */
3404 PISCSICMD pIScsiCmd = iscsiCmdRemoveAll(pImage);
3405 if (pIScsiCmd)
3406 {
3407 pIScsiCmdCur = pIScsiCmd;
3408 while (pIScsiCmdCur->pNext)
3409 pIScsiCmdCur = pIScsiCmdCur->pNext;
3410
3411 /*
3412 * Place them in front of the list because they are the oldest requests
3413 * and need to be processed first to minimize the risk to time out.
3414 */
3415 pIScsiCmdCur->pNext = pIScsiCmdHead;
3416 pIScsiCmdHead = pIScsiCmd;
3417 }
3418
3419 return pIScsiCmdHead;
3420}
3421
3422/**
3423 * Reattaches the to the target after an error aborting
3424 * pending commands and resending them.
3425 *
3426 * @param pImage iSCSI connection state.
3427 */
3428static void iscsiReattach(PISCSIIMAGE pImage)
3429{
3430 /* Close connection. */
3431 iscsiTransportClose(pImage);
3432 pImage->state = ISCSISTATE_FREE;
3433
3434 /* Reset the state and get the currently pending commands. */
3435 PISCSICMD pIScsiCmdHead = iscsiReset(pImage);
3436
3437 /* Try to attach. */
3438 int rc = iscsiAttach(pImage);
3439 if (RT_SUCCESS(rc))
3440 {
3441 /* Phew, we have a connection again.
3442 * Prepare new PDUs for the aborted commands.
3443 */
3444 while (pIScsiCmdHead)
3445 {
3446 PISCSICMD pIScsiCmd = pIScsiCmdHead;
3447 pIScsiCmdHead = pIScsiCmdHead->pNext;
3448
3449 pIScsiCmd->pNext = NULL;
3450
3451 rc = iscsiPDUTxPrepare(pImage, pIScsiCmd);
3452 if (RT_FAILURE(rc))
3453 break;
3454 }
3455
3456 if (RT_FAILURE(rc))
3457 {
3458 /* Another error, just give up and report an error. */
3459 PISCSICMD pIScsiCmd = iscsiReset(pImage);
3460
3461 /* Concatenate both lists together so we can abort all requests below. */
3462 if (pIScsiCmd)
3463 {
3464 PISCSICMD pIScsiCmdCur = pIScsiCmd;
3465 while (pIScsiCmdCur->pNext)
3466 pIScsiCmdCur = pIScsiCmdCur->pNext;
3467
3468 pIScsiCmdCur->pNext = pIScsiCmdHead;
3469 pIScsiCmdHead = pIScsiCmd;
3470 }
3471 }
3472 }
3473
3474 if (RT_FAILURE(rc))
3475 {
3476 /*
3477 * Still no luck, complete commands with error so the caller
3478 * has a chance to inform the user and maybe resend the command.
3479 */
3480 while (pIScsiCmdHead)
3481 {
3482 PISCSICMD pIScsiCmd = pIScsiCmdHead;
3483 pIScsiCmdHead = pIScsiCmdHead->pNext;
3484
3485 iscsiCmdComplete(pImage, pIScsiCmd, VERR_BROKEN_PIPE);
3486 }
3487 }
3488}
3489
3490/**
3491 * Internal. Main iSCSI I/O worker.
3492 */
3493static DECLCALLBACK(int) iscsiIoThreadWorker(RTTHREAD hThreadSelf, void *pvUser)
3494{
3495 RT_NOREF1(hThreadSelf);
3496 PISCSIIMAGE pImage = (PISCSIIMAGE)pvUser;
3497
3498 /* Initialize the initial event mask. */
3499 pImage->fPollEvents = VD_INTERFACETCPNET_EVT_READ | VD_INTERFACETCPNET_EVT_ERROR;
3500
3501 while (pImage->fRunning)
3502 {
3503 uint32_t fEvents;
3504 int rc;
3505
3506 fEvents = 0;
3507
3508 /* Wait for work or for data from the target. */
3509 RTMSINTERVAL msWait;
3510
3511 if (pImage->cCmdsWaiting)
3512 {
3513 pImage->fPollEvents &= ~VD_INTERFACETCPNET_HINT_INTERRUPT;
3514 msWait = pImage->uReadTimeout;
3515 }
3516 else
3517 {
3518 pImage->fPollEvents |= VD_INTERFACETCPNET_HINT_INTERRUPT;
3519 msWait = RT_INDEFINITE_WAIT;
3520 }
3521
3522 LogFlow(("Waiting for events fPollEvents=%#x\n", pImage->fPollEvents));
3523 rc = iscsiIoThreadWait(pImage, msWait, pImage->fPollEvents, &fEvents);
3524 if (rc == VERR_INTERRUPTED)
3525 {
3526 /* Check the queue. */
3527 PISCSICMD pIScsiCmd = iscsiCmdGet(pImage);
3528
3529 while (pIScsiCmd)
3530 {
3531 switch (pIScsiCmd->enmCmdType)
3532 {
3533 case ISCSICMDTYPE_REQ:
3534 {
3535 if ( !iscsiIsClientConnected(pImage)
3536 && pImage->fTryReconnect)
3537 {
3538 pImage->fTryReconnect = false;
3539 iscsiReattach(pImage);
3540 }
3541
3542 /* If there is no connection complete the command with an error. */
3543 if (RT_LIKELY(iscsiIsClientConnected(pImage)))
3544 {
3545 rc = iscsiPDUTxPrepare(pImage, pIScsiCmd);
3546 if (RT_FAILURE(rc))
3547 iscsiReattach(pImage);
3548 }
3549 else
3550 iscsiCmdComplete(pImage, pIScsiCmd, VERR_NET_CONNECTION_REFUSED);
3551 break;
3552 }
3553 case ISCSICMDTYPE_EXEC:
3554 {
3555 rc = pIScsiCmd->CmdType.Exec.pfnExec(pIScsiCmd->CmdType.Exec.pvUser);
3556 iscsiCmdComplete(pImage, pIScsiCmd, rc);
3557 break;
3558 }
3559 default:
3560 AssertMsgFailed(("Invalid command type %d\n", pIScsiCmd->enmCmdType));
3561 }
3562
3563 pIScsiCmd = iscsiCmdGet(pImage);
3564 }
3565 }
3566 else if (rc == VERR_TIMEOUT && pImage->cCmdsWaiting)
3567 {
3568 /*
3569 * We are waiting for a response from the target but
3570 * it didn't answered yet.
3571 * We assume the connection is broken and try to reconnect.
3572 */
3573 LogFlow(("Timed out while waiting for an answer from the target, reconnecting\n"));
3574 iscsiReattach(pImage);
3575 }
3576 else if (RT_SUCCESS(rc) || rc == VERR_TIMEOUT)
3577 {
3578 Assert(pImage->state == ISCSISTATE_NORMAL);
3579 LogFlow(("Got socket events %#x\n", fEvents));
3580
3581 if (fEvents & VD_INTERFACETCPNET_EVT_READ)
3582 {
3583 /* Continue or start a new PDU receive task */
3584 LogFlow(("There is data on the socket\n"));
3585 rc = iscsiRecvPDUAsync(pImage);
3586 if (rc == VERR_BROKEN_PIPE)
3587 iscsiReattach(pImage);
3588 else if (RT_FAILURE(rc))
3589 iscsiLogRel(pImage, "iSCSI: Handling incoming request failed %Rrc\n", rc);
3590 }
3591
3592 if (fEvents & VD_INTERFACETCPNET_EVT_WRITE)
3593 {
3594 LogFlow(("The socket is writable\n"));
3595 rc = iscsiSendPDUAsync(pImage);
3596 if (RT_FAILURE(rc))
3597 {
3598 /*
3599 * Something unexpected happened, log the error and try to reset everything
3600 * by reattaching to the target.
3601 */
3602 iscsiLogRel(pImage, "iSCSI: Sending PDU failed %Rrc\n", rc);
3603 iscsiReattach(pImage);
3604 }
3605 }
3606
3607 if (fEvents & VD_INTERFACETCPNET_EVT_ERROR)
3608 {
3609 LogFlow(("An error ocurred\n"));
3610 iscsiReattach(pImage);
3611 }
3612 }
3613 else
3614 iscsiLogRel(pImage, "iSCSI: Waiting for I/O failed rc=%Rrc\n", rc);
3615 }
3616
3617 return VINF_SUCCESS;
3618}
3619
3620/**
3621 * Internal. - Enqueues a request asynchronously.
3622 */
3623static int iscsiCommandAsync(PISCSIIMAGE pImage, PSCSIREQ pScsiReq,
3624 PFNISCSICMDCOMPLETED pfnComplete, void *pvUser)
3625{
3626 int rc;
3627
3628 if (pImage->fExtendedSelectSupported)
3629 {
3630 PISCSICMD pIScsiCmd = (PISCSICMD)RTMemAllocZ(sizeof(ISCSICMD));
3631 if (!pIScsiCmd)
3632 return VERR_NO_MEMORY;
3633
3634 /* Init the command structure. */
3635 pIScsiCmd->pNext = NULL;
3636 pIScsiCmd->enmCmdType = ISCSICMDTYPE_REQ;
3637 pIScsiCmd->pfnComplete = pfnComplete;
3638 pIScsiCmd->pvUser = pvUser;
3639 pIScsiCmd->CmdType.ScsiReq.pScsiReq = pScsiReq;
3640
3641 rc = iscsiCmdPut(pImage, pIScsiCmd);
3642 if (RT_FAILURE(rc))
3643 RTMemFree(pIScsiCmd);
3644 }
3645 else
3646 rc = VERR_NOT_SUPPORTED;
3647
3648 return rc;
3649}
3650
3651static DECLCALLBACK(void) iscsiCommandCompleteSync(PISCSIIMAGE pImage, int rcReq, void *pvUser)
3652{
3653 RT_NOREF1(pImage);
3654 PISCSICMDSYNC pIScsiCmdSync = (PISCSICMDSYNC)pvUser;
3655
3656 pIScsiCmdSync->rcCmd = rcReq;
3657 int rc = RTSemEventSignal(pIScsiCmdSync->EventSem);
3658 AssertRC(rc);
3659}
3660
3661/**
3662 * Internal. - Enqueues a request in a synchronous fashion
3663 * i.e. returns when the request completed.
3664 */
3665static int iscsiCommandSync(PISCSIIMAGE pImage, PSCSIREQ pScsiReq, bool fRetry, int rcSense)
3666{
3667 int rc;
3668
3669 if (pImage->fExtendedSelectSupported)
3670 {
3671 ISCSICMDSYNC IScsiCmdSync;
3672
3673 /* Create event semaphore. */
3674 rc = RTSemEventCreate(&IScsiCmdSync.EventSem);
3675 if (RT_FAILURE(rc))
3676 return rc;
3677
3678 if (fRetry)
3679 {
3680 for (unsigned i = 0; i < 10; i++)
3681 {
3682 rc = iscsiCommandAsync(pImage, pScsiReq, iscsiCommandCompleteSync, &IScsiCmdSync);
3683 if (RT_FAILURE(rc))
3684 break;
3685
3686 rc = RTSemEventWait(IScsiCmdSync.EventSem, RT_INDEFINITE_WAIT);
3687 AssertRC(rc);
3688 rc = IScsiCmdSync.rcCmd;
3689
3690 if ( (RT_SUCCESS(rc) && !pScsiReq->cbSense)
3691 || RT_FAILURE(rc))
3692 break;
3693 rc = rcSense;
3694 }
3695 }
3696 else
3697 {
3698 rc = iscsiCommandAsync(pImage, pScsiReq, iscsiCommandCompleteSync, &IScsiCmdSync);
3699 if (RT_SUCCESS(rc))
3700 {
3701 rc = RTSemEventWait(IScsiCmdSync.EventSem, RT_INDEFINITE_WAIT);
3702 AssertRC(rc);
3703 rc = IScsiCmdSync.rcCmd;
3704
3705 if (RT_FAILURE(rc) || pScsiReq->cbSense > 0)
3706 rc = rcSense;
3707 }
3708 }
3709
3710 RTSemEventDestroy(IScsiCmdSync.EventSem);
3711 }
3712 else
3713 {
3714 if (fRetry)
3715 {
3716 rc = VINF_SUCCESS; /* (MSC incorrectly thinks it can be uninitialized) */
3717 for (unsigned i = 0; i < 10; i++)
3718 {
3719 rc = iscsiCommand(pImage, pScsiReq);
3720 if ( (RT_SUCCESS(rc) && !pScsiReq->cbSense)
3721 || RT_FAILURE(rc))
3722 break;
3723 rc = rcSense;
3724 }
3725 }
3726 else
3727 {
3728 rc = iscsiCommand(pImage, pScsiReq);
3729 if (RT_FAILURE(rc) || pScsiReq->cbSense > 0)
3730 rc = rcSense;
3731 }
3732 }
3733
3734 return rc;
3735}
3736
3737
3738/**
3739 * Internal. - Executes a given function in a synchronous fashion
3740 * on the I/O thread if available.
3741 */
3742static int iscsiExecSync(PISCSIIMAGE pImage, PFNISCSIEXEC pfnExec, void *pvUser)
3743{
3744 int rc;
3745
3746 if (pImage->fExtendedSelectSupported)
3747 {
3748 ISCSICMDSYNC IScsiCmdSync;
3749 PISCSICMD pIScsiCmd = (PISCSICMD)RTMemAllocZ(sizeof(ISCSICMD));
3750 if (!pIScsiCmd)
3751 return VERR_NO_MEMORY;
3752
3753 /* Create event semaphore. */
3754 rc = RTSemEventCreate(&IScsiCmdSync.EventSem);
3755 if (RT_FAILURE(rc))
3756 {
3757 RTMemFree(pIScsiCmd);
3758 return rc;
3759 }
3760
3761 /* Init the command structure. */
3762 pIScsiCmd->pNext = NULL;
3763 pIScsiCmd->enmCmdType = ISCSICMDTYPE_EXEC;
3764 pIScsiCmd->pfnComplete = iscsiCommandCompleteSync;
3765 pIScsiCmd->pvUser = &IScsiCmdSync;
3766 pIScsiCmd->CmdType.Exec.pfnExec = pfnExec;
3767 pIScsiCmd->CmdType.Exec.pvUser = pvUser;
3768
3769 rc = iscsiCmdPut(pImage, pIScsiCmd);
3770 if (RT_FAILURE(rc))
3771 RTMemFree(pIScsiCmd);
3772 else
3773 {
3774 rc = RTSemEventWait(IScsiCmdSync.EventSem, RT_INDEFINITE_WAIT);
3775 AssertRC(rc);
3776 rc = IScsiCmdSync.rcCmd;
3777 }
3778
3779 RTSemEventDestroy(IScsiCmdSync.EventSem);
3780 }
3781 else
3782 {
3783 /* No I/O thread, execute in the current thread. */
3784 rc = pfnExec(pvUser);
3785 }
3786
3787 return rc;
3788}
3789
3790
3791static DECLCALLBACK(void) iscsiCommandAsyncComplete(PISCSIIMAGE pImage, int rcReq, void *pvUser)
3792{
3793 bool fComplete = true;
3794 size_t cbTransfered = 0;
3795 PSCSIREQ pScsiReq = (PSCSIREQ)pvUser;
3796
3797 if (RT_SUCCESS(rcReq))
3798 ASMAtomicWriteU32(&pImage->cLoginsSinceIo, 0);
3799
3800 if ( RT_SUCCESS(rcReq)
3801 && pScsiReq->cbSense > 0)
3802 {
3803 /* Try again if possible. */
3804 if (pScsiReq->cSenseRetries > 0)
3805 {
3806 pScsiReq->cSenseRetries--;
3807 pScsiReq->cbSense = sizeof(pScsiReq->abSense);
3808 int rc = iscsiCommandAsync(pImage, pScsiReq, iscsiCommandAsyncComplete, pScsiReq);
3809 if (RT_SUCCESS(rc))
3810 fComplete = false;
3811 else
3812 rcReq = pScsiReq->rcSense;
3813 }
3814 else
3815 rcReq = pScsiReq->rcSense;
3816 }
3817
3818 if (fComplete)
3819 {
3820 if (pScsiReq->enmXfer == SCSIXFER_FROM_TARGET)
3821 cbTransfered = pScsiReq->cbT2IData;
3822 else if (pScsiReq->enmXfer == SCSIXFER_TO_TARGET)
3823 cbTransfered = pScsiReq->cbI2TData;
3824 else
3825 AssertMsg(pScsiReq->enmXfer == SCSIXFER_NONE, ("To/From transfers are not supported yet\n"));
3826
3827 /* Continue I/O context. */
3828 pImage->pIfIo->pfnIoCtxCompleted(pImage->pIfIo->Core.pvUser,
3829 pScsiReq->pIoCtx, rcReq,
3830 cbTransfered);
3831
3832 RTMemFree(pScsiReq);
3833 }
3834}
3835
3836
3837/**
3838 * Internal. Free all allocated space for representing an image, and optionally
3839 * delete the image from disk.
3840 */
3841static int iscsiFreeImage(PISCSIIMAGE pImage, bool fDelete)
3842{
3843 int rc = VINF_SUCCESS;
3844 Assert(!fDelete); NOREF(fDelete); /* This MUST be false, the flag isn't supported. */
3845
3846 /* Freeing a never allocated image (e.g. because the open failed) is
3847 * not signalled as an error. After all nothing bad happens. */
3848 if (pImage)
3849 {
3850 if (pImage->Mutex != NIL_RTSEMMUTEX)
3851 {
3852 /* Detaching only makes sense when the mutex is there. Otherwise the
3853 * failure happened long before we could attach to the target. */
3854 iscsiExecSync(pImage, iscsiDetach, pImage);
3855 RTSemMutexDestroy(pImage->Mutex);
3856 pImage->Mutex = NIL_RTSEMMUTEX;
3857 }
3858 if (pImage->hThreadIo != NIL_RTTHREAD)
3859 {
3860 ASMAtomicXchgBool(&pImage->fRunning, false);
3861 rc = iscsiIoThreadPoke(pImage);
3862 AssertRC(rc);
3863
3864 /* Wait for the thread to terminate. */
3865 rc = RTThreadWait(pImage->hThreadIo, RT_INDEFINITE_WAIT, NULL);
3866 AssertRC(rc);
3867 }
3868 /* Destroy the socket. */
3869 if (pImage->Socket != NIL_VDSOCKET)
3870 {
3871 pImage->pIfNet->pfnSocketDestroy(pImage->Socket);
3872 }
3873 if (pImage->MutexReqQueue != NIL_RTSEMMUTEX)
3874 {
3875 RTSemMutexDestroy(pImage->MutexReqQueue);
3876 pImage->MutexReqQueue = NIL_RTSEMMUTEX;
3877 }
3878 if (pImage->pszTargetName)
3879 {
3880 RTMemFree(pImage->pszTargetName);
3881 pImage->pszTargetName = NULL;
3882 }
3883 if (pImage->pszTargetAddress)
3884 {
3885 RTMemFree(pImage->pszTargetAddress);
3886 pImage->pszTargetAddress = NULL;
3887 }
3888 if (pImage->pszInitiatorName)
3889 {
3890 if (pImage->fAutomaticInitiatorName)
3891 RTStrFree(pImage->pszInitiatorName);
3892 else
3893 RTMemFree(pImage->pszInitiatorName);
3894 pImage->pszInitiatorName = NULL;
3895 }
3896 if (pImage->pszInitiatorUsername)
3897 {
3898 RTMemFree(pImage->pszInitiatorUsername);
3899 pImage->pszInitiatorUsername = NULL;
3900 }
3901 if (pImage->pbInitiatorSecret)
3902 {
3903 RTMemFree(pImage->pbInitiatorSecret);
3904 pImage->pbInitiatorSecret = NULL;
3905 }
3906 if (pImage->pszTargetUsername)
3907 {
3908 RTMemFree(pImage->pszTargetUsername);
3909 pImage->pszTargetUsername = NULL;
3910 }
3911 if (pImage->pbTargetSecret)
3912 {
3913 RTMemFree(pImage->pbTargetSecret);
3914 pImage->pbTargetSecret = NULL;
3915 }
3916 if (pImage->pvRecvPDUBuf)
3917 {
3918 RTMemFree(pImage->pvRecvPDUBuf);
3919 pImage->pvRecvPDUBuf = NULL;
3920 }
3921 if (pImage->pszHostname)
3922 {
3923 RTMemFree(pImage->pszHostname);
3924 pImage->pszHostname = NULL;
3925 }
3926
3927 pImage->cbRecvPDUResidual = 0;
3928 }
3929
3930 LogFlowFunc(("returns %Rrc\n", rc));
3931 return rc;
3932}
3933
3934/**
3935 * Inits the basic iSCSI image state, allocating vital resources.
3936 *
3937 * @returns VBox status code.
3938 * @param pImage The iSCSI image instance.
3939 */
3940static int iscsiOpenImageInit(PISCSIIMAGE pImage)
3941{
3942 int rc = VINF_SUCCESS;
3943
3944 /* Get error signalling interface. */
3945 pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk);
3946
3947 /* Get TCP network stack interface. */
3948 pImage->pIfNet = VDIfTcpNetGet(pImage->pVDIfsImage);
3949 if (pImage->pIfNet)
3950 {
3951 /* Get configuration interface. */
3952 pImage->pIfConfig = VDIfConfigGet(pImage->pVDIfsImage);
3953 if (pImage->pIfConfig)
3954 {
3955 /* Get I/O interface. */
3956 pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage);
3957 if (pImage->pIfIo)
3958 {
3959 /* This ISID will be adjusted later to make it unique on this host. */
3960 pImage->pszHostname = NULL;
3961 pImage->uPort = 0;
3962 pImage->Socket = NIL_VDSOCKET;
3963 pImage->ISID = 0x800000000000ULL | 0x001234560000ULL;
3964 pImage->cISCSIRetries = 10;
3965 pImage->state = ISCSISTATE_FREE;
3966 pImage->cLoginsSinceIo = 0;
3967 pImage->Mutex = NIL_RTSEMMUTEX;
3968 pImage->MutexReqQueue = NIL_RTSEMMUTEX;
3969 pImage->pszInitiatorUsername = NULL;
3970 pImage->pbInitiatorSecret = NULL;
3971 pImage->cbInitiatorSecret = 0;
3972 pImage->pszTargetUsername = NULL;
3973 pImage->pbTargetSecret = NULL;
3974 pImage->cbTargetSecret = 0;
3975
3976 memset(pImage->aCmdsWaiting, 0, sizeof(pImage->aCmdsWaiting));
3977 pImage->cbRecvPDUResidual = 0;
3978
3979 pImage->pvRecvPDUBuf = RTMemAlloc(ISCSI_RECV_PDU_BUFFER_SIZE);
3980 pImage->cbRecvPDUBuf = ISCSI_RECV_PDU_BUFFER_SIZE;
3981 if (!pImage->pvRecvPDUBuf)
3982 rc = VERR_NO_MEMORY;
3983
3984 if (RT_SUCCESS(rc))
3985 rc = RTSemMutexCreate(&pImage->Mutex);
3986 if (RT_SUCCESS(rc))
3987 rc = RTSemMutexCreate(&pImage->MutexReqQueue);
3988 }
3989 else
3990 rc = vdIfError(pImage->pIfError, VERR_VD_UNKNOWN_INTERFACE,
3991 RT_SRC_POS, N_("iSCSI: I/O interface missing"));
3992 }
3993 else
3994 rc = vdIfError(pImage->pIfError, VERR_VD_UNKNOWN_INTERFACE,
3995 RT_SRC_POS, N_("iSCSI: configuration interface missing"));
3996 }
3997 else
3998 rc = vdIfError(pImage->pIfError, VERR_VD_UNKNOWN_INTERFACE,
3999 RT_SRC_POS, N_("iSCSI: TCP network stack interface missing"));
4000
4001 return rc;
4002}
4003
4004/**
4005 * Parses the user supplied config before opening the connection to the target.
4006 *
4007 * @returns VBox status code.
4008 * @param pImage The iSCSI image instance.
4009 */
4010static int iscsiOpenImageParseCfg(PISCSIIMAGE pImage)
4011{
4012 char *pszLUN = NULL, *pszLUNInitial = NULL;
4013 bool fLunEncoded = false;
4014 uint32_t uWriteSplitDef = 0;
4015 uint32_t uTimeoutDef = 0;
4016 uint64_t uCfgTmp = 0;
4017 bool fHostIPDef = false;
4018 bool fDumpMalformedPacketsDef = false;
4019
4020 int rc = RTStrToUInt32Full(s_iscsiConfigDefaultWriteSplit, 0, &uWriteSplitDef);
4021 AssertRC(rc);
4022 rc = RTStrToUInt32Full(s_iscsiConfigDefaultTimeout, 0, &uTimeoutDef);
4023 AssertRC(rc);
4024 rc = RTStrToUInt64Full(s_iscsiConfigDefaultHostIPStack, 0, &uCfgTmp);
4025 AssertRC(rc);
4026 fHostIPDef = RT_BOOL(uCfgTmp);
4027 rc = RTStrToUInt64Full(s_iscsiConfigDefaultDumpMalformedPackets, 0, &uCfgTmp);
4028 AssertRC(rc);
4029 fDumpMalformedPacketsDef = RT_BOOL(uCfgTmp);
4030
4031 /* Validate configuration, detect unknown keys. */
4032 if (!VDCFGAreKeysValid(pImage->pIfConfig,
4033 "TargetName\0"
4034 "InitiatorName\0"
4035 "LUN\0"
4036 "TargetAddress\0"
4037 "InitiatorUsername\0"
4038 "InitiatorSecret\0"
4039 "InitiatorSecretEncrypted\0"
4040 "TargetUsername\0"
4041 "TargetSecret\0"
4042 "WriteSplit\0"
4043 "Timeout\0"
4044 "HostIPStack\0"
4045 "DumpMalformedPackets\0"))
4046 return vdIfError(pImage->pIfError, VERR_VD_UNKNOWN_CFG_VALUES, RT_SRC_POS, N_("iSCSI: configuration error: unknown configuration keys present"));
4047
4048 /* Query the iSCSI upper level configuration. */
4049 rc = VDCFGQueryStringAlloc(pImage->pIfConfig, "TargetName", &pImage->pszTargetName);
4050 if (RT_FAILURE(rc))
4051 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetName as string"));
4052
4053 rc = VDCFGQueryStringAlloc(pImage->pIfConfig, "InitiatorName", &pImage->pszInitiatorName);
4054 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
4055 pImage->fAutomaticInitiatorName = true;
4056 else if (RT_FAILURE(rc))
4057 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorName as string"));
4058
4059 rc = VDCFGQueryStringAllocDef(pImage->pIfConfig, "LUN", &pszLUN, s_iscsiConfigDefaultLUN);
4060 if (RT_FAILURE(rc))
4061 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read LUN as string"));
4062
4063 pszLUNInitial = pszLUN;
4064 if (!strncmp(pszLUN, "enc", 3))
4065 {
4066 fLunEncoded = true;
4067 pszLUN += 3;
4068 }
4069 rc = RTStrToUInt64Full(pszLUN, 0, &pImage->LUN);
4070 if (RT_FAILURE(rc))
4071 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to convert LUN to integer"));
4072
4073 RTMemFree(pszLUNInitial);
4074 if (RT_SUCCESS(rc) && !fLunEncoded)
4075 {
4076 if (pImage->LUN <= 255)
4077 pImage->LUN = pImage->LUN << 48; /* uses peripheral device addressing method */
4078 else if (pImage->LUN <= 16383)
4079 pImage->LUN = (pImage->LUN << 48) | RT_BIT_64(62); /* uses flat space addressing method */
4080 else
4081 rc = vdIfError(pImage->pIfError, VERR_OUT_OF_RANGE, RT_SRC_POS, N_("iSCSI: configuration error: LUN number out of range (0-16383)"));
4082 }
4083
4084 if (RT_FAILURE(rc))
4085 return rc;
4086
4087 rc = VDCFGQueryStringAlloc(pImage->pIfConfig, "TargetAddress", &pImage->pszTargetAddress);
4088 if (RT_FAILURE(rc))
4089 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetAddress as string"));
4090
4091 rc = VDCFGQueryStringAlloc(pImage->pIfConfig, "InitiatorUsername", &pImage->pszInitiatorUsername);
4092 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND && rc != VERR_CFGM_NO_PARENT)
4093 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorUsername as string"));
4094
4095 rc = VDCFGQueryBytesAlloc(pImage->pIfConfig, "InitiatorSecret",
4096 (void **)&pImage->pbInitiatorSecret, &pImage->cbInitiatorSecret);
4097 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND && rc != VERR_CFGM_NO_PARENT)
4098 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorSecret as byte string"));
4099
4100 void *pvInitiatorSecretEncrypted;
4101 size_t cbInitiatorSecretEncrypted;
4102 rc = VDCFGQueryBytesAlloc(pImage->pIfConfig, "InitiatorSecretEncrypted",
4103 &pvInitiatorSecretEncrypted, &cbInitiatorSecretEncrypted);
4104 if (RT_SUCCESS(rc))
4105 {
4106 RTMemFree(pvInitiatorSecretEncrypted);
4107 if (!pImage->pbInitiatorSecret)
4108 {
4109 /* we have an encrypted initiator secret but not an unencrypted one */
4110 return vdIfError(pImage->pIfError, VERR_VD_ISCSI_SECRET_ENCRYPTED, RT_SRC_POS, N_("iSCSI: initiator secret not decrypted"));
4111 }
4112 }
4113
4114 rc = VDCFGQueryStringAlloc(pImage->pIfConfig, "TargetUsername", &pImage->pszTargetUsername);
4115 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND && rc != VERR_CFGM_NO_PARENT)
4116 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetUsername as string"));
4117
4118 rc = VDCFGQueryBytesAlloc(pImage->pIfConfig, "TargetSecret",
4119 (void **)&pImage->pbTargetSecret, &pImage->cbTargetSecret);
4120 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND && rc != VERR_CFGM_NO_PARENT)
4121 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetSecret as byte string"));
4122
4123 rc = VDCFGQueryU32Def(pImage->pIfConfig, "WriteSplit", &pImage->cbWriteSplit, uWriteSplitDef);
4124 if (RT_FAILURE(rc))
4125 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read WriteSplit as U32"));
4126
4127 /* Query the iSCSI lower level configuration. */
4128 rc = VDCFGQueryU32Def(pImage->pIfConfig, "Timeout", &pImage->uReadTimeout, uTimeoutDef);
4129 if (RT_FAILURE(rc))
4130 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read Timeout as U32"));
4131
4132 rc = VDCFGQueryBoolDef(pImage->pIfConfig, "HostIPStack", &pImage->fHostIP, fHostIPDef);
4133 if (RT_FAILURE(rc))
4134 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read HostIPStack as boolean"));
4135
4136 rc = VDCFGQueryBoolDef(pImage->pIfConfig, "DumpMalformedPackets",
4137 &pImage->fDumpMalformedPackets, fDumpMalformedPacketsDef);
4138 if (RT_FAILURE(rc))
4139 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read DumpMalformedPackets as boolean"));
4140
4141 return VINF_SUCCESS;
4142}
4143
4144/**
4145 * Creates the necessary socket structure.
4146 *
4147 * @returns VBox status code.
4148 * @param pImage The iSCSI image instance.
4149 */
4150static int iscsiOpenImageSocketCreate(PISCSIIMAGE pImage)
4151{
4152 /* Create the socket structure. */
4153 int rc = pImage->pIfNet->pfnSocketCreate(VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT,
4154 &pImage->Socket);
4155 if (RT_SUCCESS(rc))
4156 {
4157 pImage->fExtendedSelectSupported = true;
4158 pImage->fRunning = true;
4159 rc = RTThreadCreate(&pImage->hThreadIo, iscsiIoThreadWorker, pImage, 0,
4160 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "iSCSI-Io");
4161 if (RT_FAILURE(rc))
4162 LogFunc(("Creating iSCSI I/O thread failed rc=%Rrc\n", rc));
4163 }
4164 else if (rc == VERR_NOT_SUPPORTED)
4165 {
4166 /* Async I/O is not supported without extended select. */
4167 if ((pImage->uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO))
4168 LogFunc(("Extended select is not supported by the interface but async I/O is requested -> %Rrc\n", rc));
4169 else
4170 {
4171 pImage->fExtendedSelectSupported = false;
4172 rc = pImage->pIfNet->pfnSocketCreate(0, &pImage->Socket);
4173 }
4174 }
4175
4176 if (RT_FAILURE(rc))
4177 LogFunc(("Creating socket failed -> %Rrc\n", rc));
4178
4179 return rc;
4180}
4181
4182/**
4183 * Issues a REPORT LUNS to the target.
4184 *
4185 * @returns VBox status code.
4186 * @param pImage The iSCSI image instance.
4187 */
4188static int iscsiOpenImageReportLuns(PISCSIIMAGE pImage)
4189{
4190 SCSIREQ sr;
4191 RTSGSEG DataSeg;
4192 uint8_t rlundata[16];
4193
4194 /*
4195 * Inquire available LUNs - purely dummy request.
4196 */
4197 RT_ZERO(sr.abCDB);
4198 sr.abCDB[0] = SCSI_REPORT_LUNS;
4199 sr.abCDB[1] = 0; /* reserved */
4200 sr.abCDB[2] = 0; /* reserved */
4201 sr.abCDB[3] = 0; /* reserved */
4202 sr.abCDB[4] = 0; /* reserved */
4203 sr.abCDB[5] = 0; /* reserved */
4204 sr.abCDB[6] = sizeof(rlundata) >> 24;
4205 sr.abCDB[7] = (sizeof(rlundata) >> 16) & 0xff;
4206 sr.abCDB[8] = (sizeof(rlundata) >> 8) & 0xff;
4207 sr.abCDB[9] = sizeof(rlundata) & 0xff;
4208 sr.abCDB[10] = 0; /* reserved */
4209 sr.abCDB[11] = 0; /* control */
4210
4211 DataSeg.pvSeg = rlundata;
4212 DataSeg.cbSeg = sizeof(rlundata);
4213
4214 sr.enmXfer = SCSIXFER_FROM_TARGET;
4215 sr.cbCDB = 12;
4216 sr.cbI2TData = 0;
4217 sr.paI2TSegs = NULL;
4218 sr.cI2TSegs = 0;
4219 sr.cbT2IData = DataSeg.cbSeg;
4220 sr.paT2ISegs = &DataSeg;
4221 sr.cT2ISegs = 1;
4222 sr.cbSense = sizeof(sr.abSense);
4223 int rc = iscsiCommandSync(pImage, &sr, false, VERR_INVALID_STATE);
4224 if (RT_FAILURE(rc))
4225 LogRel(("iSCSI: Could not get LUN info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
4226
4227 return rc;
4228}
4229
4230/**
4231 * Issues the INQUIRY command to the target and checks for the correct device type.
4232 *
4233 * @returns VBox status code.
4234 * @param pImage The iSCSI image instance.
4235 */
4236static int iscsiOpenImageInquiry(PISCSIIMAGE pImage)
4237{
4238 SCSIREQ sr;
4239 RTSGSEG DataSeg;
4240 uint8_t data8[8];
4241
4242 /*
4243 * Inquire device characteristics - no tapes, scanners etc., please.
4244 */
4245 RT_ZERO(sr.abCDB);
4246 sr.abCDB[0] = SCSI_INQUIRY;
4247 sr.abCDB[1] = 0; /* reserved */
4248 sr.abCDB[2] = 0; /* reserved */
4249 sr.abCDB[3] = 0; /* reserved */
4250 sr.abCDB[4] = sizeof(data8);
4251 sr.abCDB[5] = 0; /* control */
4252
4253 DataSeg.pvSeg = data8;
4254 DataSeg.cbSeg = sizeof(data8);
4255
4256 sr.enmXfer = SCSIXFER_FROM_TARGET;
4257 sr.cbCDB = 6;
4258 sr.cbI2TData = 0;
4259 sr.paI2TSegs = NULL;
4260 sr.cI2TSegs = 0;
4261 sr.cbT2IData = DataSeg.cbSeg;
4262 sr.paT2ISegs = &DataSeg;
4263 sr.cT2ISegs = 1;
4264 sr.cbSense = sizeof(sr.abSense);
4265 int rc = iscsiCommandSync(pImage, &sr, true /* fRetry */, VERR_INVALID_STATE);
4266 if (RT_SUCCESS(rc))
4267 {
4268 uint8_t devType = (sr.cbT2IData > 0) ? data8[0] & SCSI_DEVTYPE_MASK : 255;
4269 if (devType == SCSI_DEVTYPE_DISK)
4270 {
4271 uint8_t uCmdQueue = (sr.cbT2IData >= 8) ? data8[7] & SCSI_INQUIRY_CMDQUE_MASK : 0;
4272 if (uCmdQueue > 0)
4273 pImage->fCmdQueuingSupported = true;
4274 else if (pImage->uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO)
4275 rc = VERR_NOT_SUPPORTED;
4276 else
4277 LogRel(("iSCSI: target address %s, target name %s, %s command queuing\n",
4278 pImage->pszTargetAddress, pImage->pszTargetName,
4279 pImage->fCmdQueuingSupported ? "supports" : "doesn't support"));
4280 }
4281 else
4282 {
4283 rc = vdIfError(pImage->pIfError, VERR_VD_ISCSI_INVALID_TYPE,
4284 RT_SRC_POS, N_("iSCSI: target address %s, target name %s, SCSI LUN %lld reports device type=%u"),
4285 pImage->pszTargetAddress, pImage->pszTargetName,
4286 pImage->LUN, devType);
4287 LogRel(("iSCSI: Unsupported SCSI peripheral device type %d for target %s\n", devType & SCSI_DEVTYPE_MASK, pImage->pszTargetName));
4288 }
4289 }
4290 else
4291 LogRel(("iSCSI: Could not get INQUIRY info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
4292
4293 return rc;
4294}
4295
4296/**
4297 * Checks that the target allows write access if the caller requested it.
4298 *
4299 * @returns VBox status code.
4300 * @param pImage The iSCSI image instance.
4301 */
4302static int iscsiOpenImageCheckWriteAccess(PISCSIIMAGE pImage)
4303{
4304 SCSIREQ sr;
4305 RTSGSEG DataSeg;
4306 uint8_t data4[4];
4307
4308 /*
4309 * Query write disable bit in the device specific parameter entry in the
4310 * mode parameter header. Refuse read/write opening of read only disks.
4311 */
4312 RT_ZERO(sr.abCDB);
4313 sr.abCDB[0] = SCSI_MODE_SENSE_6;
4314 sr.abCDB[1] = 0; /* dbd=0/reserved */
4315 sr.abCDB[2] = 0x3f; /* pc=0/page code=0x3f, ask for all pages */
4316 sr.abCDB[3] = 0; /* subpage code=0, return everything in page_0 format */
4317 sr.abCDB[4] = sizeof(data4); /* allocation length=4 */
4318 sr.abCDB[5] = 0; /* control */
4319
4320 DataSeg.pvSeg = data4;
4321 DataSeg.cbSeg = sizeof(data4);
4322
4323 sr.enmXfer = SCSIXFER_FROM_TARGET;
4324 sr.cbCDB = 6;
4325 sr.cbI2TData = 0;
4326 sr.paI2TSegs = NULL;
4327 sr.cI2TSegs = 0;
4328 sr.cbT2IData = DataSeg.cbSeg;
4329 sr.paT2ISegs = &DataSeg;
4330 sr.cT2ISegs = 1;
4331 sr.cbSense = sizeof(sr.abSense);
4332 int rc = iscsiCommandSync(pImage, &sr, true /* fRetry */, VERR_INVALID_STATE);
4333 if (RT_SUCCESS(rc))
4334 {
4335 pImage->fTargetReadOnly = !!(data4[2] & 0x80);
4336 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) && pImage->fTargetReadOnly)
4337 rc = VERR_VD_IMAGE_READ_ONLY;
4338 }
4339 else
4340 LogRel(("iSCSI: Could not get MODE SENSE info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
4341
4342 return rc;
4343}
4344
4345/**
4346 * Queries the media and sector size of the target.
4347 *
4348 * @returns VBox status code.
4349 * @param pImage The iSCSI image instance.
4350 */
4351static int iscsiOpenImageQueryTargetSizes(PISCSIIMAGE pImage)
4352{
4353 SCSIREQ sr;
4354 RTSGSEG DataSeg;
4355 uint8_t data12[12];
4356
4357 /*
4358 * Determine sector size and capacity of the volume immediately.
4359 */
4360 RT_ZERO(data12);
4361 RT_ZERO(sr.abCDB);
4362 sr.abCDB[0] = SCSI_SERVICE_ACTION_IN_16;
4363 sr.abCDB[1] = SCSI_SVC_ACTION_IN_READ_CAPACITY_16; /* subcommand */
4364 sr.abCDB[10+3] = sizeof(data12); /* allocation length (dword) */
4365
4366 DataSeg.pvSeg = data12;
4367 DataSeg.cbSeg = sizeof(data12);
4368
4369 sr.enmXfer = SCSIXFER_FROM_TARGET;
4370 sr.cbCDB = 16;
4371 sr.cbI2TData = 0;
4372 sr.paI2TSegs = NULL;
4373 sr.cI2TSegs = 0;
4374 sr.cbT2IData = DataSeg.cbSeg;
4375 sr.paT2ISegs = &DataSeg;
4376 sr.cT2ISegs = 1;
4377 sr.cbSense = sizeof(sr.abSense);
4378
4379 int rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
4380 if (RT_SUCCESS(rc))
4381 {
4382 bool fEnd = false;
4383 uint8_t cMaxRetries = 10;
4384 do
4385 {
4386 switch (sr.status)
4387 {
4388 case SCSI_STATUS_OK:
4389 {
4390 pImage->cVolume = RT_BE2H_U64(*(uint64_t *)&data12[0]);
4391 pImage->cVolume++;
4392 pImage->cbSector = RT_BE2H_U32(*(uint32_t *)&data12[8]);
4393 pImage->cbSize = pImage->cVolume * pImage->cbSector;
4394 if (pImage->cVolume == 0 || pImage->cbSize < pImage->cVolume)
4395 {
4396 rc = vdIfError(pImage->pIfError, VERR_VD_ISCSI_INVALID_TYPE,
4397 RT_SRC_POS, N_("iSCSI: target address %s, target name %s, SCSI LUN %lld reports media sector count=%llu sector size=%u"),
4398 pImage->pszTargetAddress, pImage->pszTargetName,
4399 pImage->LUN, pImage->cVolume, pImage->cbSector);
4400 }
4401 fEnd = true;
4402 break;
4403 }
4404 case SCSI_STATUS_CHECK_CONDITION:
4405 {
4406 if((sr.abSense[2] & 0x0f) == SCSI_SENSE_UNIT_ATTENTION)
4407 {
4408 if( sr.abSense[12] == SCSI_ASC_POWER_ON_RESET_BUS_DEVICE_RESET_OCCURRED
4409 && sr.abSense[13] == SCSI_ASCQ_POWER_ON_RESET_BUS_DEVICE_RESET_OCCURRED)
4410 {
4411 /** @todo for future: prepare and send command "REQUEST SENSE" which will
4412 * return the status of target and will clear any unit attention
4413 * condition that it reports */
4414 rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
4415 if (RT_FAILURE(rc))
4416 fEnd = true;
4417 cMaxRetries--;
4418 break;
4419 }
4420 }
4421 break;
4422 }
4423 default:
4424 {
4425 rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
4426 if (RT_FAILURE(rc))
4427 fEnd = true;
4428 cMaxRetries--;
4429 break;
4430 }
4431 }
4432 if (!cMaxRetries)
4433 fEnd = true;
4434 } while(!fEnd);
4435 }
4436 else
4437 {
4438 uint8_t data8[8];
4439
4440 RT_ZERO(data8);
4441 sr.abCDB[0] = SCSI_READ_CAPACITY;
4442 sr.abCDB[1] = 0; /* reserved */
4443 sr.abCDB[2] = 0; /* reserved */
4444 sr.abCDB[3] = 0; /* reserved */
4445 sr.abCDB[4] = 0; /* reserved */
4446 sr.abCDB[5] = 0; /* reserved */
4447 sr.abCDB[6] = 0; /* reserved */
4448 sr.abCDB[7] = 0; /* reserved */
4449 sr.abCDB[8] = 0; /* reserved */
4450 sr.abCDB[9] = 0; /* control */
4451
4452 DataSeg.pvSeg = data8;
4453 DataSeg.cbSeg = sizeof(data8);
4454
4455 sr.enmXfer = SCSIXFER_FROM_TARGET;
4456 sr.cbCDB = 10;
4457 sr.cbI2TData = 0;
4458 sr.paI2TSegs = NULL;
4459 sr.cI2TSegs = 0;
4460 sr.cbT2IData = DataSeg.cbSeg;
4461 sr.paT2ISegs = &DataSeg;
4462 sr.cT2ISegs = 1;
4463 sr.cbSense = sizeof(sr.abSense);
4464 rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
4465 if (RT_SUCCESS(rc))
4466 {
4467 bool fEnd = false;
4468 uint8_t cMaxRetries = 10;
4469 do
4470 {
4471 switch (sr.status)
4472 {
4473 case SCSI_STATUS_OK:
4474 {
4475 pImage->cVolume = (data8[0] << 24) | (data8[1] << 16) | (data8[2] << 8) | data8[3];
4476 pImage->cVolume++;
4477 pImage->cbSector = (data8[4] << 24) | (data8[5] << 16) | (data8[6] << 8) | data8[7];
4478 pImage->cbSize = pImage->cVolume * pImage->cbSector;
4479 if (pImage->cVolume == 0)
4480 {
4481 rc = vdIfError(pImage->pIfError, VERR_VD_ISCSI_INVALID_TYPE,
4482 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"),
4483 pImage->pszTargetAddress, pImage->pszTargetName,
4484 pImage->LUN, pImage->cVolume, pImage->cbSector);
4485 }
4486
4487 fEnd = true;
4488 break;
4489 }
4490 case SCSI_STATUS_CHECK_CONDITION:
4491 {
4492 if((sr.abSense[2] & 0x0f) == SCSI_SENSE_UNIT_ATTENTION)
4493 {
4494 if( sr.abSense[12] == SCSI_ASC_POWER_ON_RESET_BUS_DEVICE_RESET_OCCURRED
4495 && sr.abSense[13] == SCSI_ASCQ_POWER_ON_RESET_BUS_DEVICE_RESET_OCCURRED)
4496 {
4497 /** @todo for future: prepare and send command "REQUEST SENSE" which will
4498 * return the status of target and will clear any unit attention
4499 * condition that it reports */
4500 rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
4501 if (RT_FAILURE(rc))
4502 fEnd = true;
4503 cMaxRetries--;
4504 break;
4505
4506 }
4507 }
4508 break;
4509 }
4510 default:
4511 {
4512 rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
4513 if (RT_FAILURE(rc))
4514 fEnd = true;
4515 cMaxRetries--;
4516 break;
4517 }
4518 }
4519 if (!cMaxRetries)
4520 fEnd = true;
4521 } while(!fEnd);
4522 }
4523 else
4524 LogRel(("iSCSI: Could not determine capacity of target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
4525 }
4526
4527 return rc;
4528}
4529
4530/**
4531 * Queries the state of the read/write caches and tries to enable them if disabled.
4532 *
4533 * @returns VBox status code.
4534 * @param pImage The iSCSI image instance.
4535 */
4536static int iscsiOpenImageEnableReadWriteCache(PISCSIIMAGE pImage)
4537{
4538 /*
4539 * Check the read and write cache bits.
4540 * Try to enable the cache if it is disabled.
4541 *
4542 * We already checked that this is a block access device. No need
4543 * to do it again.
4544 */
4545 SCSIREQ sr;
4546 RTSGSEG DataSeg;
4547 uint8_t aCachingModePage[32];
4548
4549 memset(aCachingModePage, '\0', sizeof(aCachingModePage));
4550 sr.abCDB[0] = SCSI_MODE_SENSE_6;
4551 sr.abCDB[1] = 0;
4552 sr.abCDB[2] = (0x00 << 6) | (0x08 & 0x3f); /* Current values and caching mode page */
4553 sr.abCDB[3] = 0; /* Sub page code. */
4554 sr.abCDB[4] = sizeof(aCachingModePage) & 0xff;
4555 sr.abCDB[5] = 0;
4556
4557 DataSeg.pvSeg = aCachingModePage;
4558 DataSeg.cbSeg = sizeof(aCachingModePage);
4559
4560 sr.enmXfer = SCSIXFER_FROM_TARGET;
4561 sr.cbCDB = 6;
4562 sr.cbI2TData = 0;
4563 sr.paI2TSegs = NULL;
4564 sr.cI2TSegs = 0;
4565 sr.cbT2IData = DataSeg.cbSeg;
4566 sr.paT2ISegs = &DataSeg;
4567 sr.cT2ISegs = 1;
4568 sr.cbSense = sizeof(sr.abSense);
4569 int rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
4570 if ( RT_SUCCESS(rc)
4571 && (sr.status == SCSI_STATUS_OK)
4572 && (aCachingModePage[0] >= 15)
4573 && (aCachingModePage[4 + aCachingModePage[3]] & 0x3f) == 0x08
4574 && (aCachingModePage[4 + aCachingModePage[3] + 1] > 3))
4575 {
4576 uint32_t Offset = 4 + aCachingModePage[3];
4577 /*
4578 * Check if the read and/or the write cache is disabled.
4579 * The write cache is disabled if bit 2 (WCE) is zero and
4580 * the read cache is disabled if bit 0 (RCD) is set.
4581 */
4582 if (!ASMBitTest(&aCachingModePage[Offset + 2], 2) || ASMBitTest(&aCachingModePage[Offset + 2], 0))
4583 {
4584 /*
4585 * Write Cache Enable (WCE) bit is zero or the Read Cache Disable (RCD) is one
4586 * So one of the caches is disabled. Enable both caches.
4587 * The rest is unchanged.
4588 */
4589 ASMBitSet(&aCachingModePage[Offset + 2], 2);
4590 ASMBitClear(&aCachingModePage[Offset + 2], 0);
4591
4592 sr.abCDB[0] = SCSI_MODE_SELECT_6;
4593 sr.abCDB[1] = 0; /* Don't write the page into NV RAM. */
4594 sr.abCDB[2] = 0;
4595 sr.abCDB[3] = 0;
4596 sr.abCDB[4] = sizeof(aCachingModePage) & 0xff;
4597 sr.abCDB[5] = 0;
4598
4599 DataSeg.pvSeg = aCachingModePage;
4600 DataSeg.cbSeg = sizeof(aCachingModePage);
4601
4602 sr.enmXfer = SCSIXFER_TO_TARGET;
4603 sr.cbCDB = 6;
4604 sr.cbI2TData = DataSeg.cbSeg;
4605 sr.paI2TSegs = &DataSeg;
4606 sr.cI2TSegs = 1;
4607 sr.cbT2IData = 0;
4608 sr.paT2ISegs = NULL;
4609 sr.cT2ISegs = 0;
4610 sr.cbSense = sizeof(sr.abSense);
4611 sr.status = 0;
4612 rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
4613 if ( RT_SUCCESS(rc)
4614 && (sr.status == SCSI_STATUS_OK))
4615 LogRel(("iSCSI: Enabled read and write cache of target %s\n", pImage->pszTargetName));
4616 else
4617 {
4618 /* Log failures but continue. */
4619 LogRel(("iSCSI: Could not enable read and write cache of target %s, rc=%Rrc status=%#x\n",
4620 pImage->pszTargetName, rc, sr.status));
4621 LogRel(("iSCSI: Sense:\n%.*Rhxd\n", sr.cbSense, sr.abSense));
4622 rc = VINF_SUCCESS;
4623 }
4624 }
4625 }
4626 else
4627 {
4628 /* Log errors but continue. */
4629 LogRel(("iSCSI: Could not check write cache of target %s, rc=%Rrc, got mode page %#x\n", pImage->pszTargetName, rc, aCachingModePage[0] & 0x3f));
4630 LogRel(("iSCSI: Sense:\n%.*Rhxd\n", sr.cbSense, sr.abSense));
4631 rc = VINF_SUCCESS;
4632 }
4633
4634 return rc;
4635}
4636
4637/**
4638 * Internal: Open an image, constructing all necessary data structures.
4639 */
4640static int iscsiOpenImage(PISCSIIMAGE pImage, unsigned uOpenFlags)
4641{
4642 pImage->uOpenFlags = uOpenFlags;
4643
4644 int rc = iscsiOpenImageInit(pImage);
4645 if (RT_SUCCESS(rc))
4646 rc = iscsiOpenImageParseCfg(pImage);
4647
4648 if (RT_SUCCESS(rc))
4649 {
4650 /* Don't actually establish iSCSI transport connection if this is just an
4651 * open to query the image information and the host IP stack isn't used.
4652 * Even trying is rather useless, as in this context the InTnet IP stack
4653 * isn't present. Returning dummies is the best possible result anyway. */
4654 if ((uOpenFlags & VD_OPEN_FLAGS_INFO) && !pImage->fHostIP)
4655 LogFunc(("Not opening the transport connection as IntNet IP stack is not available. Will return dummies\n"));
4656 else
4657 {
4658 rc = iscsiOpenImageSocketCreate(pImage);
4659 if (RT_SUCCESS(rc))
4660 {
4661 /*
4662 * Attach to the iSCSI target. This implicitly establishes the iSCSI
4663 * transport connection.
4664 */
4665 rc = iscsiExecSync(pImage, iscsiAttach, pImage);
4666 if (RT_SUCCESS(rc))
4667 {
4668 LogFlowFunc(("target '%s' opened successfully\n", pImage->pszTargetName));
4669
4670 rc = iscsiOpenImageReportLuns(pImage);
4671 if (RT_SUCCESS(rc))
4672 rc = iscsiOpenImageInquiry(pImage);
4673 if (RT_SUCCESS(rc))
4674 rc = iscsiOpenImageCheckWriteAccess(pImage);
4675 if (RT_SUCCESS(rc))
4676 rc = iscsiOpenImageQueryTargetSizes(pImage);
4677 if (RT_SUCCESS(rc))
4678 rc = iscsiOpenImageEnableReadWriteCache(pImage);
4679 }
4680 else
4681 LogRel(("iSCSI: could not open target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
4682 }
4683 }
4684 }
4685
4686 if (RT_FAILURE(rc))
4687 iscsiFreeImage(pImage, false);
4688 return rc;
4689}
4690
4691
4692/** @copydoc VDIMAGEBACKEND::pfnProbe */
4693static DECLCALLBACK(int) iscsiProbe(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
4694 PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
4695{
4696 RT_NOREF4(pszFilename, pVDIfsDisk, pVDIfsImage, penmType);
4697 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
4698
4699 /* iSCSI images can't be checked for validity this way, as the filename
4700 * just can't supply enough configuration information. */
4701 int rc = VERR_VD_ISCSI_INVALID_HEADER;
4702
4703 LogFlowFunc(("returns %Rrc\n", rc));
4704 return rc;
4705}
4706
4707/** @copydoc VDIMAGEBACKEND::pfnOpen */
4708static DECLCALLBACK(int) iscsiOpen(const char *pszFilename, unsigned uOpenFlags,
4709 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
4710 VDTYPE enmType, void **ppBackendData)
4711{
4712 RT_NOREF1(enmType); /**< @todo r=klaus make use of the type info. */
4713
4714 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p enmType=%u ppBackendData=%#p\n",
4715 pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, enmType, ppBackendData));
4716 int rc;
4717
4718 /* Check open flags. All valid flags are supported. */
4719 AssertReturn(!(uOpenFlags & ~VD_OPEN_FLAGS_MASK), VERR_INVALID_PARAMETER);
4720 AssertReturn((VALID_PTR(pszFilename) && *pszFilename), VERR_INVALID_PARAMETER);
4721
4722 PISCSIIMAGE pImage = (PISCSIIMAGE)RTMemAllocZ(sizeof(ISCSIIMAGE));
4723 if (RT_LIKELY(pImage))
4724 {
4725 pImage->pszFilename = pszFilename;
4726 pImage->pszInitiatorName = NULL;
4727 pImage->pszTargetName = NULL;
4728 pImage->pszTargetAddress = NULL;
4729 pImage->pszInitiatorUsername = NULL;
4730 pImage->pbInitiatorSecret = NULL;
4731 pImage->pszTargetUsername = NULL;
4732 pImage->pbTargetSecret = NULL;
4733 pImage->paCurrReq = NULL;
4734 pImage->pvRecvPDUBuf = NULL;
4735 pImage->pszHostname = NULL;
4736 pImage->pVDIfsDisk = pVDIfsDisk;
4737 pImage->pVDIfsImage = pVDIfsImage;
4738 pImage->cLogRelErrors = 0;
4739
4740 rc = iscsiOpenImage(pImage, uOpenFlags);
4741 if (RT_SUCCESS(rc))
4742 {
4743 LogFlowFunc(("target %s cVolume %d, cbSector %d\n", pImage->pszTargetName, pImage->cVolume, pImage->cbSector));
4744 LogRel(("iSCSI: target address %s, target name %s, SCSI LUN %lld\n", pImage->pszTargetAddress, pImage->pszTargetName, pImage->LUN));
4745 *ppBackendData = pImage;
4746 }
4747 else
4748 RTMemFree(pImage);
4749 }
4750 else
4751 rc = VERR_NO_MEMORY;
4752
4753 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
4754 return rc;
4755}
4756
4757/** @copydoc VDIMAGEBACKEND::pfnCreate */
4758static DECLCALLBACK(int) iscsiCreate(const char *pszFilename, uint64_t cbSize,
4759 unsigned uImageFlags, const char *pszComment,
4760 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
4761 PCRTUUID pUuid, unsigned uOpenFlags,
4762 unsigned uPercentStart, unsigned uPercentSpan,
4763 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
4764 PVDINTERFACE pVDIfsOperation, VDTYPE enmType,
4765 void **ppBackendData)
4766{
4767 RT_NOREF8(pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags);
4768 RT_NOREF7(uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, enmType, ppBackendData);
4769 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",
4770 pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, enmType, ppBackendData));
4771 int rc = VERR_NOT_SUPPORTED;
4772
4773 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
4774 return rc;
4775}
4776
4777/** @copydoc VDIMAGEBACKEND::pfnClose */
4778static DECLCALLBACK(int) iscsiClose(void *pBackendData, bool fDelete)
4779{
4780 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
4781 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4782 int rc;
4783
4784 Assert(!fDelete); /* This flag is unsupported. */
4785
4786 rc = iscsiFreeImage(pImage, fDelete);
4787 RTMemFree(pImage);
4788
4789 LogFlowFunc(("returns %Rrc\n", rc));
4790 return rc;
4791}
4792
4793/** @copydoc VDIMAGEBACKEND::pfnRead */
4794static DECLCALLBACK(int) iscsiRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
4795 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
4796{
4797 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4798 int rc = VINF_SUCCESS;
4799
4800 LogFlowFunc(("pBackendData=%p uOffset=%#llx pIoCtx=%#p cbToRead=%u pcbActuallyRead=%p\n",
4801 pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
4802
4803 if ( uOffset + cbToRead > pImage->cbSize
4804 || cbToRead == 0)
4805 return VERR_INVALID_PARAMETER;
4806
4807 /*
4808 * Clip read size to a value which is supported by the target.
4809 */
4810 cbToRead = RT_MIN(cbToRead, pImage->cbRecvDataLength);
4811
4812 unsigned cT2ISegs = 0;
4813 size_t cbSegs = 0;
4814
4815 /* Get the number of segments. */
4816 cbSegs = pImage->pIfIo->pfnIoCtxSegArrayCreate(pImage->pIfIo->Core.pvUser, pIoCtx,
4817 NULL, &cT2ISegs, cbToRead);
4818 Assert(cbSegs == cbToRead);
4819
4820 PSCSIREQ pReq = (PSCSIREQ)RTMemAllocZ(RT_OFFSETOF(SCSIREQ, aSegs[cT2ISegs]));
4821 if (RT_LIKELY(pReq))
4822 {
4823 uint64_t lba;
4824 uint16_t tls;
4825 uint8_t *pbCDB = &pReq->abCDB[0];
4826 size_t cbCDB;
4827
4828 lba = uOffset / pImage->cbSector;
4829 tls = (uint16_t)(cbToRead / pImage->cbSector);
4830
4831 cbSegs = pImage->pIfIo->pfnIoCtxSegArrayCreate(pImage->pIfIo->Core.pvUser, pIoCtx,
4832 &pReq->aSegs[0],
4833 &cT2ISegs, cbToRead);
4834 Assert(cbSegs == cbToRead);
4835
4836 if (pImage->cVolume < _4G)
4837 {
4838 cbCDB = 10;
4839 pbCDB[0] = SCSI_READ_10;
4840 pbCDB[1] = 0; /* reserved */
4841 pbCDB[2] = (lba >> 24) & 0xff;
4842 pbCDB[3] = (lba >> 16) & 0xff;
4843 pbCDB[4] = (lba >> 8) & 0xff;
4844 pbCDB[5] = lba & 0xff;
4845 pbCDB[6] = 0; /* reserved */
4846 pbCDB[7] = (tls >> 8) & 0xff;
4847 pbCDB[8] = tls & 0xff;
4848 pbCDB[9] = 0; /* control */
4849 }
4850 else
4851 {
4852 cbCDB = 16;
4853 pbCDB[0] = SCSI_READ_16;
4854 pbCDB[1] = 0; /* reserved */
4855 pbCDB[2] = (lba >> 56) & 0xff;
4856 pbCDB[3] = (lba >> 48) & 0xff;
4857 pbCDB[4] = (lba >> 40) & 0xff;
4858 pbCDB[5] = (lba >> 32) & 0xff;
4859 pbCDB[6] = (lba >> 24) & 0xff;
4860 pbCDB[7] = (lba >> 16) & 0xff;
4861 pbCDB[8] = (lba >> 8) & 0xff;
4862 pbCDB[9] = lba & 0xff;
4863 pbCDB[10] = 0; /* tls unused */
4864 pbCDB[11] = 0; /* tls unused */
4865 pbCDB[12] = (tls >> 8) & 0xff;
4866 pbCDB[13] = tls & 0xff;
4867 pbCDB[14] = 0; /* reserved */
4868 pbCDB[15] = 0; /* reserved */
4869 }
4870
4871 pReq->enmXfer = SCSIXFER_FROM_TARGET;
4872 pReq->cbCDB = cbCDB;
4873 pReq->cbI2TData = 0;
4874 pReq->paI2TSegs = NULL;
4875 pReq->cI2TSegs = 0;
4876 pReq->cbT2IData = cbToRead;
4877 pReq->paT2ISegs = &pReq->aSegs[pReq->cI2TSegs];
4878 pReq->cbSense = sizeof(pReq->abSense);
4879 pReq->cT2ISegs = cT2ISegs;
4880 pReq->pIoCtx = pIoCtx;
4881 pReq->cSenseRetries = 10;
4882 pReq->rcSense = VERR_READ_ERROR;
4883
4884 if (vdIfIoIntIoCtxIsSynchronous(pImage->pIfIo, pIoCtx))
4885 {
4886 rc = iscsiCommandSync(pImage, pReq, true, VERR_READ_ERROR);
4887 if (RT_FAILURE(rc))
4888 {
4889 LogFlow(("iscsiCommandSync(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
4890 *pcbActuallyRead = 0;
4891 }
4892 else
4893 *pcbActuallyRead = pReq->cbT2IData;
4894 }
4895 else
4896 {
4897 rc = iscsiCommandAsync(pImage, pReq, iscsiCommandAsyncComplete, pReq);
4898 if (RT_FAILURE(rc))
4899 AssertMsgFailed(("iscsiCommandAsync(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
4900 else
4901 {
4902 *pcbActuallyRead = cbToRead;
4903 return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
4904 }
4905 }
4906
4907 RTMemFree(pReq);
4908 }
4909 else
4910 rc = VERR_NO_MEMORY;
4911
4912 LogFlowFunc(("returns rc=%Rrc\n", rc));
4913 return rc;
4914}
4915
4916/** @copydoc VDIMAGEBACKEND::pfnWrite */
4917static DECLCALLBACK(int) iscsiWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
4918 PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
4919 size_t *pcbPostRead, unsigned fWrite)
4920{
4921 RT_NOREF3(pcbPreRead, pcbPostRead, fWrite);
4922 LogFlowFunc(("pBackendData=%p uOffset=%llu pIoCtx=%#p cbToWrite=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
4923 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite));
4924 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4925 int rc = VINF_SUCCESS;
4926
4927 AssertPtr(pImage);
4928 Assert(uOffset % 512 == 0);
4929 Assert(cbToWrite % 512 == 0);
4930
4931 if (uOffset + cbToWrite > pImage->cbSize)
4932 return VERR_INVALID_PARAMETER;
4933
4934 /*
4935 * Clip read size to a value which is supported by the target.
4936 */
4937 cbToWrite = RT_MIN(cbToWrite, pImage->cbSendDataLength);
4938
4939 unsigned cI2TSegs = 0;
4940 size_t cbSegs = 0;
4941
4942 /* Get the number of segments. */
4943 cbSegs = pImage->pIfIo->pfnIoCtxSegArrayCreate(pImage->pIfIo->Core.pvUser, pIoCtx,
4944 NULL, &cI2TSegs, cbToWrite);
4945 Assert(cbSegs == cbToWrite);
4946
4947 PSCSIREQ pReq = (PSCSIREQ)RTMemAllocZ(RT_OFFSETOF(SCSIREQ, aSegs[cI2TSegs]));
4948 if (RT_LIKELY(pReq))
4949 {
4950 uint64_t lba;
4951 uint16_t tls;
4952 uint8_t *pbCDB = &pReq->abCDB[0];
4953 size_t cbCDB;
4954
4955 lba = uOffset / pImage->cbSector;
4956 tls = (uint16_t)(cbToWrite / pImage->cbSector);
4957
4958 cbSegs = pImage->pIfIo->pfnIoCtxSegArrayCreate(pImage->pIfIo->Core.pvUser, pIoCtx,
4959 &pReq->aSegs[0],
4960 &cI2TSegs, cbToWrite);
4961 Assert(cbSegs == cbToWrite);
4962
4963 if (pImage->cVolume < _4G)
4964 {
4965 cbCDB = 10;
4966 pbCDB[0] = SCSI_WRITE_10;
4967 pbCDB[1] = 0; /* reserved */
4968 pbCDB[2] = (lba >> 24) & 0xff;
4969 pbCDB[3] = (lba >> 16) & 0xff;
4970 pbCDB[4] = (lba >> 8) & 0xff;
4971 pbCDB[5] = lba & 0xff;
4972 pbCDB[6] = 0; /* reserved */
4973 pbCDB[7] = (tls >> 8) & 0xff;
4974 pbCDB[8] = tls & 0xff;
4975 pbCDB[9] = 0; /* control */
4976 }
4977 else
4978 {
4979 cbCDB = 16;
4980 pbCDB[0] = SCSI_WRITE_16;
4981 pbCDB[1] = 0; /* reserved */
4982 pbCDB[2] = (lba >> 56) & 0xff;
4983 pbCDB[3] = (lba >> 48) & 0xff;
4984 pbCDB[4] = (lba >> 40) & 0xff;
4985 pbCDB[5] = (lba >> 32) & 0xff;
4986 pbCDB[6] = (lba >> 24) & 0xff;
4987 pbCDB[7] = (lba >> 16) & 0xff;
4988 pbCDB[8] = (lba >> 8) & 0xff;
4989 pbCDB[9] = lba & 0xff;
4990 pbCDB[10] = 0; /* tls unused */
4991 pbCDB[11] = 0; /* tls unused */
4992 pbCDB[12] = (tls >> 8) & 0xff;
4993 pbCDB[13] = tls & 0xff;
4994 pbCDB[14] = 0; /* reserved */
4995 pbCDB[15] = 0; /* reserved */
4996 }
4997
4998 pReq->enmXfer = SCSIXFER_TO_TARGET;
4999 pReq->cbCDB = cbCDB;
5000 pReq->cbI2TData = cbToWrite;
5001 pReq->paI2TSegs = &pReq->aSegs[0];
5002 pReq->cI2TSegs = cI2TSegs;
5003 pReq->cbT2IData = 0;
5004 pReq->paT2ISegs = NULL;
5005 pReq->cT2ISegs = 0;
5006 pReq->cbSense = sizeof(pReq->abSense);
5007 pReq->pIoCtx = pIoCtx;
5008 pReq->cSenseRetries = 10;
5009 pReq->rcSense = VERR_WRITE_ERROR;
5010
5011 if (vdIfIoIntIoCtxIsSynchronous(pImage->pIfIo, pIoCtx))
5012 {
5013 rc = iscsiCommandSync(pImage, pReq, true, VERR_WRITE_ERROR);
5014 if (RT_FAILURE(rc))
5015 {
5016 LogFlow(("iscsiCommandSync(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
5017 *pcbWriteProcess = 0;
5018 }
5019 else
5020 *pcbWriteProcess = cbToWrite;
5021 }
5022 else
5023 {
5024 rc = iscsiCommandAsync(pImage, pReq, iscsiCommandAsyncComplete, pReq);
5025 if (RT_FAILURE(rc))
5026 AssertMsgFailed(("iscsiCommandAsync(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
5027 else
5028 {
5029 *pcbWriteProcess = cbToWrite;
5030 return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
5031 }
5032 }
5033
5034 RTMemFree(pReq);
5035 }
5036 else
5037 rc = VERR_NO_MEMORY;
5038
5039 LogFlowFunc(("returns rc=%Rrc\n", rc));
5040 return rc;
5041}
5042
5043/** @copydoc VDIMAGEBACKEND::pfnFlush */
5044static DECLCALLBACK(int) iscsiFlush(void *pBackendData, PVDIOCTX pIoCtx)
5045{
5046 LogFlowFunc(("pBackendData=%p pIoCtx=%#p\n", pBackendData, pIoCtx));
5047 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5048 int rc = VINF_SUCCESS;
5049
5050 PSCSIREQ pReq = (PSCSIREQ)RTMemAllocZ(sizeof(SCSIREQ));
5051 if (RT_LIKELY(pReq))
5052 {
5053 uint8_t *pbCDB = &pReq->abCDB[0];
5054
5055 pbCDB[0] = SCSI_SYNCHRONIZE_CACHE;
5056 pbCDB[1] = 0; /* reserved */
5057 pbCDB[2] = 0; /* reserved */
5058 pbCDB[3] = 0; /* reserved */
5059 pbCDB[4] = 0; /* reserved */
5060 pbCDB[5] = 0; /* reserved */
5061 pbCDB[6] = 0; /* reserved */
5062 pbCDB[7] = 0; /* reserved */
5063 pbCDB[8] = 0; /* reserved */
5064 pbCDB[9] = 0; /* control */
5065
5066 pReq->enmXfer = SCSIXFER_NONE;
5067 pReq->cbCDB = 10;
5068 pReq->cbI2TData = 0;
5069 pReq->paI2TSegs = NULL;
5070 pReq->cI2TSegs = 0;
5071 pReq->cbT2IData = 0;
5072 pReq->paT2ISegs = NULL;
5073 pReq->cT2ISegs = 0;
5074 pReq->cbSense = sizeof(pReq->abSense);
5075 pReq->pIoCtx = pIoCtx;
5076 pReq->cSenseRetries = 0;
5077 pReq->rcSense = VINF_SUCCESS;
5078
5079 if (vdIfIoIntIoCtxIsSynchronous(pImage->pIfIo, pIoCtx))
5080 {
5081 rc = iscsiCommandSync(pImage, pReq, false, VINF_SUCCESS);
5082 if (RT_FAILURE(rc))
5083 AssertMsgFailed(("iscsiCommand(%s) -> %Rrc\n", pImage->pszTargetName, rc));
5084 }
5085 else
5086 {
5087 rc = iscsiCommandAsync(pImage, pReq, iscsiCommandAsyncComplete, pReq);
5088 if (RT_FAILURE(rc))
5089 AssertMsgFailed(("iscsiCommand(%s) -> %Rrc\n", pImage->pszTargetName, rc));
5090 else
5091 return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
5092 }
5093
5094 RTMemFree(pReq);
5095 }
5096 else
5097 rc = VERR_NO_MEMORY;
5098
5099 LogFlowFunc(("returns rc=%Rrc\n", rc));
5100 return rc;
5101}
5102
5103/** @copydoc VDIMAGEBACKEND::pfnGetVersion */
5104static DECLCALLBACK(unsigned) iscsiGetVersion(void *pBackendData)
5105{
5106 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
5107 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5108
5109 AssertPtr(pImage);
5110 RT_NOREF1(pImage);
5111
5112 return 0;
5113}
5114
5115/** @copydoc VDIMAGEBACKEND::pfnGetSectorSize */
5116static DECLCALLBACK(uint32_t) iscsiGetSectorSize(void *pBackendData)
5117{
5118 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
5119 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5120
5121 AssertPtrReturn(pImage, 0);
5122
5123 return pImage->cbSector;
5124}
5125
5126/** @copydoc VDIMAGEBACKEND::pfnGetSize */
5127static DECLCALLBACK(uint64_t) iscsiGetSize(void *pBackendData)
5128{
5129 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
5130 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5131
5132 AssertPtrReturn(pImage, 0);
5133
5134 return pImage->cbSize;
5135}
5136
5137/** @copydoc VDIMAGEBACKEND::pfnGetFileSize */
5138static DECLCALLBACK(uint64_t) iscsiGetFileSize(void *pBackendData)
5139{
5140 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
5141 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5142
5143 AssertPtrReturn(pImage, 0);
5144
5145 return pImage->cbSize;
5146}
5147
5148/** @copydoc VDIMAGEBACKEND::pfnGetPCHSGeometry */
5149static DECLCALLBACK(int) iscsiGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
5150{
5151 RT_NOREF1(pPCHSGeometry);
5152 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
5153 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5154
5155 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
5156
5157 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", VERR_VD_GEOMETRY_NOT_SET,
5158 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
5159 return VERR_VD_GEOMETRY_NOT_SET;
5160}
5161
5162/** @copydoc VDIMAGEBACKEND::pfnSetPCHSGeometry */
5163static DECLCALLBACK(int) iscsiSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
5164{
5165 RT_NOREF1(pPCHSGeometry);
5166 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
5167 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5168
5169 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
5170
5171 int rc;
5172 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
5173 rc = VERR_VD_IMAGE_READ_ONLY;
5174 else
5175 rc = VERR_VD_GEOMETRY_NOT_SET;
5176
5177 LogFlowFunc(("returns %Rrc\n", rc));
5178 return rc;
5179}
5180
5181/** @copydoc VDIMAGEBACKEND::pfnGetLCHSGeometry */
5182static DECLCALLBACK(int) iscsiGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
5183{
5184 RT_NOREF1(pLCHSGeometry);
5185 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
5186 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5187
5188 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
5189
5190 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", VERR_VD_GEOMETRY_NOT_SET,
5191 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
5192 return VERR_VD_GEOMETRY_NOT_SET;
5193}
5194
5195/** @copydoc VDIMAGEBACKEND::pfnSetLCHSGeometry */
5196static DECLCALLBACK(int) iscsiSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
5197{
5198 RT_NOREF1(pLCHSGeometry);
5199 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
5200 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5201
5202 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
5203
5204 int rc;
5205 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
5206 rc = VERR_VD_IMAGE_READ_ONLY;
5207 else
5208 rc = VERR_VD_GEOMETRY_NOT_SET;
5209
5210 LogFlowFunc(("returns %Rrc\n", rc));
5211 return rc;
5212}
5213
5214/** @copydoc VDIMAGEBACKEND::pfnGetImageFlags */
5215static DECLCALLBACK(unsigned) iscsiGetImageFlags(void *pBackendData)
5216{
5217 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
5218 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5219
5220 AssertPtrReturn(pImage, 0);
5221
5222 LogFlowFunc(("returns %#x\n", VD_IMAGE_FLAGS_FIXED));
5223 return VD_IMAGE_FLAGS_FIXED;
5224}
5225
5226/** @copydoc VDIMAGEBACKEND::pfnGetOpenFlags */
5227static DECLCALLBACK(unsigned) iscsiGetOpenFlags(void *pBackendData)
5228{
5229 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
5230 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5231
5232 AssertPtrReturn(pImage, 0);
5233
5234 LogFlowFunc(("returns %#x\n", pImage->uOpenFlags));
5235 return pImage->uOpenFlags;
5236}
5237
5238/** @copydoc VDIMAGEBACKEND::pfnSetOpenFlags */
5239static DECLCALLBACK(int) iscsiSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
5240{
5241 LogFlowFunc(("pBackendData=%#p uOpenFlags=%#x\n", pBackendData, uOpenFlags));
5242 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5243 int rc = VINF_SUCCESS;
5244
5245 /* Image must be opened and the new flags must be valid. */
5246 AssertReturn(pImage && !(uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
5247 | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
5248 | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)),
5249 VERR_INVALID_PARAMETER);
5250
5251 /*
5252 * A read/write -> readonly transition is always possible,
5253 * for the reverse direction check that the target didn't present itself
5254 * as readonly during the first attach.
5255 */
5256 if ( !(uOpenFlags & VD_OPEN_FLAGS_READONLY)
5257 && (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
5258 && pImage->fTargetReadOnly)
5259 rc = VERR_VD_IMAGE_READ_ONLY;
5260 else
5261 {
5262 pImage->uOpenFlags = uOpenFlags;
5263 pImage->fTryReconnect = true;
5264 }
5265
5266 LogFlowFunc(("returns %Rrc\n", rc));
5267 return rc;
5268}
5269
5270/** @copydoc VDIMAGEBACKEND::pfnGetComment */
5271static DECLCALLBACK(int) iscsiGetComment(void *pBackendData, char *pszComment,
5272 size_t cbComment)
5273{
5274 RT_NOREF2(pszComment, cbComment);
5275 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
5276 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5277
5278 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
5279
5280 LogFlowFunc(("returns %Rrc comment='%s'\n", VERR_NOT_SUPPORTED, pszComment));
5281 return VERR_NOT_SUPPORTED;
5282}
5283
5284/** @copydoc VDIMAGEBACKEND::pfnSetComment */
5285static DECLCALLBACK(int) iscsiSetComment(void *pBackendData, const char *pszComment)
5286{
5287 RT_NOREF1(pszComment);
5288 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
5289 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5290
5291 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
5292
5293 int rc;
5294 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
5295 rc = VERR_NOT_SUPPORTED;
5296 else
5297 rc = VERR_VD_IMAGE_READ_ONLY;
5298
5299 LogFlowFunc(("returns %Rrc\n", rc));
5300 return rc;
5301}
5302
5303/** @copydoc VDIMAGEBACKEND::pfnGetUuid */
5304static DECLCALLBACK(int) iscsiGetUuid(void *pBackendData, PRTUUID pUuid)
5305{
5306 RT_NOREF1(pUuid);
5307 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
5308 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5309
5310 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
5311
5312 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VERR_NOT_SUPPORTED, pUuid));
5313 return VERR_NOT_SUPPORTED;
5314}
5315
5316/** @copydoc VDIMAGEBACKEND::pfnSetUuid */
5317static DECLCALLBACK(int) iscsiSetUuid(void *pBackendData, PCRTUUID pUuid)
5318{
5319 RT_NOREF1(pUuid);
5320 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
5321 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5322
5323 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
5324
5325 int rc;
5326 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
5327 rc = VERR_NOT_SUPPORTED;
5328 else
5329 rc = VERR_VD_IMAGE_READ_ONLY;
5330
5331 LogFlowFunc(("returns %Rrc\n", rc));
5332 return rc;
5333}
5334
5335/** @copydoc VDIMAGEBACKEND::pfnGetModificationUuid */
5336static DECLCALLBACK(int) iscsiGetModificationUuid(void *pBackendData, PRTUUID pUuid)
5337{
5338 RT_NOREF1(pUuid);
5339 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
5340 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5341
5342 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
5343
5344 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VERR_NOT_SUPPORTED, pUuid));
5345 return VERR_NOT_SUPPORTED;
5346}
5347
5348/** @copydoc VDIMAGEBACKEND::pfnSetModificationUuid */
5349static DECLCALLBACK(int) iscsiSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
5350{
5351 RT_NOREF1(pUuid);
5352 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
5353 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5354
5355 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
5356
5357 int rc;
5358 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
5359 rc = VERR_NOT_SUPPORTED;
5360 else
5361 rc = VERR_VD_IMAGE_READ_ONLY;
5362
5363 LogFlowFunc(("returns %Rrc\n", rc));
5364 return rc;
5365}
5366
5367/** @copydoc VDIMAGEBACKEND::pfnGetParentUuid */
5368static DECLCALLBACK(int) iscsiGetParentUuid(void *pBackendData, PRTUUID pUuid)
5369{
5370 RT_NOREF1(pUuid);
5371 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
5372 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5373
5374 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
5375
5376 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VERR_NOT_SUPPORTED, pUuid));
5377 return VERR_NOT_SUPPORTED;
5378}
5379
5380/** @copydoc VDIMAGEBACKEND::pfnSetParentUuid */
5381static DECLCALLBACK(int) iscsiSetParentUuid(void *pBackendData, PCRTUUID pUuid)
5382{
5383 RT_NOREF1(pUuid);
5384 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
5385 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5386
5387 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
5388
5389 int rc;
5390 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
5391 rc = VERR_NOT_SUPPORTED;
5392 else
5393 rc = VERR_VD_IMAGE_READ_ONLY;
5394
5395 LogFlowFunc(("returns %Rrc\n", rc));
5396 return rc;
5397}
5398
5399/** @copydoc VDIMAGEBACKEND::pfnGetParentModificationUuid */
5400static DECLCALLBACK(int) iscsiGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
5401{
5402 RT_NOREF1(pUuid);
5403 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
5404 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5405
5406 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
5407
5408 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VERR_NOT_SUPPORTED, pUuid));
5409 return VERR_NOT_SUPPORTED;
5410}
5411
5412/** @copydoc VDIMAGEBACKEND::pfnSetParentModificationUuid */
5413static DECLCALLBACK(int) iscsiSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
5414{
5415 RT_NOREF1(pUuid);
5416 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
5417 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5418
5419 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
5420
5421 int rc;
5422 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
5423 rc = VERR_NOT_SUPPORTED;
5424 else
5425 rc = VERR_VD_IMAGE_READ_ONLY;
5426
5427 LogFlowFunc(("returns %Rrc\n", rc));
5428 return rc;
5429}
5430
5431/** @copydoc VDIMAGEBACKEND::pfnDump */
5432static DECLCALLBACK(void) iscsiDump(void *pBackendData)
5433{
5434 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5435
5436 AssertPtrReturnVoid(pImage);
5437 /** @todo put something useful here */
5438 vdIfErrorMessage(pImage->pIfError, "Header: cVolume=%u\n", pImage->cVolume);
5439}
5440
5441/** @copydoc VDIMAGEBACKEND::pfnComposeLocation */
5442static DECLCALLBACK(int) iscsiComposeLocation(PVDINTERFACE pConfig, char **pszLocation)
5443{
5444 char *pszTarget = NULL;
5445 char *pszLUN = NULL;
5446 char *pszAddress = NULL;
5447 int rc = VDCFGQueryStringAlloc(VDIfConfigGet(pConfig), "TargetName", &pszTarget);
5448 if (RT_SUCCESS(rc))
5449 {
5450 rc = VDCFGQueryStringAlloc(VDIfConfigGet(pConfig), "LUN", &pszLUN);
5451 if (RT_SUCCESS(rc))
5452 {
5453 rc = VDCFGQueryStringAlloc(VDIfConfigGet(pConfig), "TargetAddress", &pszAddress);
5454 if (RT_SUCCESS(rc))
5455 {
5456 if (RTStrAPrintf(pszLocation, "iscsi://%s/%s/%s",
5457 pszAddress, pszTarget, pszLUN) < 0)
5458 rc = VERR_NO_MEMORY;
5459 }
5460 }
5461 }
5462 RTMemFree(pszTarget);
5463 RTMemFree(pszLUN);
5464 RTMemFree(pszAddress);
5465 return rc;
5466}
5467
5468/** @copydoc VDIMAGEBACKEND::pfnComposeName */
5469static DECLCALLBACK(int) iscsiComposeName(PVDINTERFACE pConfig, char **pszName)
5470{
5471 char *pszTarget = NULL;
5472 char *pszLUN = NULL;
5473 char *pszAddress = NULL;
5474 int rc = VDCFGQueryStringAlloc(VDIfConfigGet(pConfig), "TargetName", &pszTarget);
5475 if (RT_SUCCESS(rc))
5476 {
5477 rc = VDCFGQueryStringAlloc(VDIfConfigGet(pConfig), "LUN", &pszLUN);
5478 if (RT_SUCCESS(rc))
5479 {
5480 rc = VDCFGQueryStringAlloc(VDIfConfigGet(pConfig), "TargetAddress", &pszAddress);
5481 if (RT_SUCCESS(rc))
5482 {
5483 /** @todo think about a nicer looking location scheme for iSCSI */
5484 if (RTStrAPrintf(pszName, "%s/%s/%s",
5485 pszAddress, pszTarget, pszLUN) < 0)
5486 rc = VERR_NO_MEMORY;
5487 }
5488 }
5489 }
5490 RTMemFree(pszTarget);
5491 RTMemFree(pszLUN);
5492 RTMemFree(pszAddress);
5493
5494 return rc;
5495}
5496
5497
5498const VDIMAGEBACKEND g_ISCSIBackend =
5499{
5500 /* u32Version */
5501 VD_IMGBACKEND_VERSION,
5502 /* pszBackendName */
5503 "iSCSI",
5504 /* uBackendCaps */
5505 VD_CAP_CONFIG | VD_CAP_TCPNET | VD_CAP_ASYNC,
5506 /* papszFileExtensions */
5507 NULL,
5508 /* paConfigInfo */
5509 s_iscsiConfigInfo,
5510 /* prnProbe */
5511 iscsiProbe,
5512 /* pfnOpen */
5513 iscsiOpen,
5514 /* pfnCreate */
5515 iscsiCreate,
5516 /* pfnRename */
5517 NULL,
5518 /* pfnClose */
5519 iscsiClose,
5520 /* pfnRead */
5521 iscsiRead,
5522 /* pfnWrite */
5523 iscsiWrite,
5524 /* pfnFlush */
5525 iscsiFlush,
5526 /* pfnDiscard */
5527 NULL,
5528 /* pfnGetVersion */
5529 iscsiGetVersion,
5530 /* pfnGetSectorSize */
5531 iscsiGetSectorSize,
5532 /* pfnGetSize */
5533 iscsiGetSize,
5534 /* pfnGetFileSize */
5535 iscsiGetFileSize,
5536 /* pfnGetPCHSGeometry */
5537 iscsiGetPCHSGeometry,
5538 /* pfnSetPCHSGeometry */
5539 iscsiSetPCHSGeometry,
5540 /* pfnGetLCHSGeometry */
5541 iscsiGetLCHSGeometry,
5542 /* pfnSetLCHSGeometry */
5543 iscsiSetLCHSGeometry,
5544 /* pfnGetImageFlags */
5545 iscsiGetImageFlags,
5546 /* pfnGetOpenFlags */
5547 iscsiGetOpenFlags,
5548 /* pfnSetOpenFlags */
5549 iscsiSetOpenFlags,
5550 /* pfnGetComment */
5551 iscsiGetComment,
5552 /* pfnSetComment */
5553 iscsiSetComment,
5554 /* pfnGetUuid */
5555 iscsiGetUuid,
5556 /* pfnSetUuid */
5557 iscsiSetUuid,
5558 /* pfnGetModificationUuid */
5559 iscsiGetModificationUuid,
5560 /* pfnSetModificationUuid */
5561 iscsiSetModificationUuid,
5562 /* pfnGetParentUuid */
5563 iscsiGetParentUuid,
5564 /* pfnSetParentUuid */
5565 iscsiSetParentUuid,
5566 /* pfnGetParentModificationUuid */
5567 iscsiGetParentModificationUuid,
5568 /* pfnSetParentModificationUuid */
5569 iscsiSetParentModificationUuid,
5570 /* pfnDump */
5571 iscsiDump,
5572 /* pfnGetTimestamp */
5573 NULL,
5574 /* pfnGetParentTimestamp */
5575 NULL,
5576 /* pfnSetParentTimestamp */
5577 NULL,
5578 /* pfnGetParentFilename */
5579 NULL,
5580 /* pfnSetParentFilename */
5581 NULL,
5582 /* pfnComposeLocation */
5583 iscsiComposeLocation,
5584 /* pfnComposeName */
5585 iscsiComposeName,
5586 /* pfnCompact */
5587 NULL,
5588 /* pfnResize */
5589 NULL,
5590 /* pfnRepair */
5591 NULL,
5592 /* pfnTraverseMetadata */
5593 NULL,
5594 /* u32VersionEnd */
5595 VD_IMGBACKEND_VERSION
5596};
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