VirtualBox

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

Last change on this file since 74910 was 74107, checked in by vboxsync, 6 years ago

Storage/iSCSI: Fix login to COMSTAR iSCSI targets. The NSG field in the Login response is only valid if the transit bit is set according to RFC3720 chapter 10.12.3 (public bug #17507)

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