VirtualBox

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

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

Storage/iSCSI: Fix possible endless reconnect loop if logging in to the target succeeds but any other requests cause a disconnect. Limit the amount of logins between successful I/O requests to 3, more login attempts are denied and cause the VM to suspend with the apporpriate error about an unrespnsive iSCSI target

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