VirtualBox

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

Last change on this file since 37423 was 37176, checked in by vboxsync, 14 years ago

iSCSI: Pause the VM if a request times out.

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