VirtualBox

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

Last change on this file since 43448 was 43448, checked in by vboxsync, 12 years ago

Storage/iSCSI: issue 3100. resend command READ CAPACITY in case of a target error during login

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