VirtualBox

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

Last change on this file since 61600 was 61037, checked in by vboxsync, 9 years ago

iSCSI: fix minor memory leaks

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