VirtualBox

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

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

ISCSI: IPv6 address parsing.

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