VirtualBox

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

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

Storage/VD: Convert all backends to use the region list callbacks, remove the pfnGetSize and pfnGetSectorSize callbacks because they are covered by the region lists

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