VirtualBox

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

Last change on this file since 82828 was 79965, checked in by vboxsync, 5 years ago

Storage: Added a desired format parameter to VDGetFormat() so Main can pass along the device type. Bumped the RAW backend down after the CUE and VISO to prevent (impossible) mixups of tiny files. Extended rawProbe() to look for a valid ISO-9660/UDF descriptor sequence on the image if the caller doesn't say it should be a floppy or hdd, only if that fail go by the extension. Note! the pfnProbe callback should probably get a score return parameter to indicate how confient the backend is about the probe result, as this would prevent the RAW backend from accidentally grabbing images which belongs to a plug-in. bugref:9151

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