VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/ISCSIHDDCore.cpp@ 31608

Last change on this file since 31608 was 31608, checked in by vboxsync, 15 years ago

iSCSI: Proper handling of lost connections. Reset the state and resend all active tasks after the connection was successfully established again. Handle commands which succeed but have sense data properly

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette