VirtualBox

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

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

pdmifs.h: yet another batch of refactored interface ID code.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 131.0 KB
Line 
1/* $Id: ISCSIHDDCore.cpp 25981 2010-01-22 18:42:01Z vboxsync $ */
2/** @file
3 * iSCSI initiator driver, VD backend.
4 */
5
6/*
7 * Copyright (C) 2006-2009 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_VD_ISCSI
27#include <VBox/VBoxHDD-Plugin.h>
28#define VBOX_VDICORE_VD /* Signal that the header is included from here. */
29#include "VDICore.h"
30#include <VBox/err.h>
31
32#include <VBox/log.h>
33#include <iprt/alloc.h>
34#include <iprt/assert.h>
35#include <iprt/uuid.h>
36#include <iprt/file.h>
37#include <iprt/string.h>
38#include <iprt/asm.h>
39#include <iprt/thread.h>
40#include <iprt/semaphore.h>
41#include <iprt/md5.h>
42#include <iprt/tcp.h>
43#include <iprt/time.h>
44#include <VBox/scsi.h>
45
46
47/*******************************************************************************
48* Defined Constants And Macros *
49*******************************************************************************/
50
51/** Default port number to use for iSCSI. */
52#define ISCSI_DEFAULT_PORT 3260
53
54
55/** Converts a number in the range of 0 - 15 into the corresponding hex char. */
56#define NUM_2_HEX(b) ('0' + (b) + (((b) > 9) ? 39 : 0))
57/** Converts a hex char into the corresponding number in the range 0-15. */
58#define HEX_2_NUM(c) (((c) <= '9') ? ((c) - '0') : (((c - 'A' + 10) & 0xf)))
59/* Converts a base64 char into the corresponding number in the range 0-63. */
60#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)
61
62
63/** Minumum CHAP_MD5 challenge length in bytes. */
64#define CHAP_MD5_CHALLENGE_MIN 16
65/** Maximum CHAP_MD5 challenge length in bytes. */
66#define CHAP_MD5_CHALLENGE_MAX 24
67
68
69/**
70 * SCSI peripheral device type. */
71typedef enum SCSIDEVTYPE
72{
73 /** direct-access device. */
74 SCSI_DEVTYPE_DISK = 0,
75 /** sequential-access device. */
76 SCSI_DEVTYPE_TAPE,
77 /** printer device. */
78 SCSI_DEVTYPE_PRINTER,
79 /** processor device. */
80 SCSI_DEVTYPE_PROCESSOR,
81 /** write-once device. */
82 SCSI_DEVTYPE_WORM,
83 /** CD/DVD device. */
84 SCSI_DEVTYPE_CDROM,
85 /** scanner device. */
86 SCSI_DEVTYPE_SCANNER,
87 /** optical memory device. */
88 SCSI_DEVTYPE_OPTICAL,
89 /** medium changer. */
90 SCSI_DEVTYPE_CHANGER,
91 /** communications device. */
92 SCSI_DEVTYPE_COMMUNICATION,
93 /** storage array controller device. */
94 SCSI_DEVTYPE_RAIDCTL = 0x0c,
95 /** enclosure services device. */
96 SCSI_DEVTYPE_ENCLOSURE,
97 /** simplified direct-access device. */
98 SCSI_DEVTYPE_SIMPLEDISK,
99 /** optical card reader/writer device. */
100 SCSI_DEVTYPE_OCRW,
101 /** bridge controller device. */
102 SCSI_DEVTYPE_BRIDGE,
103 /** object-based storage device. */
104 SCSI_DEVTYPE_OSD
105} SCSIDEVTYPE;
106
107/** Mask for extracting the SCSI device type out of the first byte of the INQUIRY response. */
108#define SCSI_DEVTYPE_MASK 0x1f
109
110
111/** Maximum PDU payload size we can handle in one piece. Greater or equal than
112 * s_iscsiConfigDefaultWriteSplit. */
113#define ISCSI_DATA_LENGTH_MAX _256K
114
115/** Maximum PDU size we can handle in one piece. */
116#define ISCSI_RECV_PDU_BUFFER_SIZE (ISCSI_DATA_LENGTH_MAX + ISCSI_BHS_SIZE)
117
118
119/** Version of the iSCSI standard which this initiator driver can handle. */
120#define ISCSI_MY_VERSION 0
121
122
123/** Length of ISCSI basic header segment. */
124#define ISCSI_BHS_SIZE 48
125
126
127/** Reserved task tag value. */
128#define ISCSI_TASK_TAG_RSVD 0xffffffff
129
130
131/**
132 * iSCSI opcodes. */
133typedef enum ISCSIOPCODE
134{
135 /** NOP-Out. */
136 ISCSIOP_NOP_OUT = 0x00000000,
137 /** SCSI command. */
138 ISCSIOP_SCSI_CMD = 0x01000000,
139 /** SCSI task management request. */
140 ISCSIOP_SCSI_TASKMGMT_REQ = 0x02000000,
141 /** Login request. */
142 ISCSIOP_LOGIN_REQ = 0x03000000,
143 /** Text request. */
144 ISCSIOP_TEXT_REQ = 0x04000000,
145 /** SCSI Data-Out. */
146 ISCSIOP_SCSI_DATA_OUT = 0x05000000,
147 /** Logout request. */
148 ISCSIOP_LOGOUT_REQ = 0x06000000,
149 /** SNACK request. */
150 ISCSIOP_SNACK_REQ = 0x10000000,
151
152 /** NOP-In. */
153 ISCSIOP_NOP_IN = 0x20000000,
154 /** SCSI response. */
155 ISCSIOP_SCSI_RES = 0x21000000,
156 /** SCSI Task Management response. */
157 ISCSIOP_SCSI_TASKMGMT_RES = 0x22000000,
158 /** Login response. */
159 ISCSIOP_LOGIN_RES = 0x23000000,
160 /** Text response. */
161 ISCSIOP_TEXT_RES = 0x24000000,
162 /** SCSI Data-In. */
163 ISCSIOP_SCSI_DATA_IN = 0x25000000,
164 /** Logout response. */
165 ISCSIOP_LOGOUT_RES = 0x26000000,
166 /** Ready To Transfer (R2T). */
167 ISCSIOP_R2T = 0x31000000,
168 /** Asynchronous message. */
169 ISCSIOP_ASYN_MSG = 0x32000000,
170 /** Reject. */
171 ISCSIOP_REJECT = 0x3f000000
172} ISCSIOPCODE;
173
174/** Mask for extracting the iSCSI opcode out of the first header word. */
175#define ISCSIOP_MASK 0x3f000000
176
177
178/** ISCSI BHS word 0: Request should be processed immediately. */
179#define ISCSI_IMMEDIATE_DELIVERY_BIT 0x40000000
180
181/** ISCSI BHS word 0: This is the final PDU for this request/response. */
182#define ISCSI_FINAL_BIT 0x00800000
183/** ISCSI BHS word 0: Mask for extracting the CSG. */
184#define ISCSI_CSG_MASK 0x000c0000
185/** ISCSI BHS word 0: Shift offset for extracting the CSG. */
186#define ISCSI_CSG_SHIFT 18
187/** ISCSI BHS word 0: Mask for extracting the NSG. */
188#define ISCSI_NSG_MASK 0x00030000
189/** ISCSI BHS word 0: Shift offset for extracting the NSG. */
190#define ISCSI_NSG_SHIFT 16
191
192/** ISCSI BHS word 0: task attribute untagged */
193#define ISCSI_TASK_ATTR_UNTAGGED 0x00000000
194/** ISCSI BHS word 0: task attribute simple */
195#define ISCSI_TASK_ATTR_SIMPLE 0x00010000
196/** ISCSI BHS word 0: task attribute ordered */
197#define ISCSI_TASK_ATTR_ORDERED 0x00020000
198/** ISCSI BHS word 0: task attribute head of queue */
199#define ISCSI_TASK_ATTR_HOQ 0x00030000
200/** ISCSI BHS word 0: task attribute ACA */
201#define ISCSI_TASK_ATTR_ACA 0x00040000
202
203/** ISCSI BHS word 0: transit to next login phase. */
204#define ISCSI_TRANSIT_BIT 0x00800000
205/** ISCSI BHS word 0: continue with login negotiation. */
206#define ISCSI_CONTINUE_BIT 0x00400000
207
208/** ISCSI BHS word 0: residual underflow. */
209#define ISCSI_RESIDUAL_UNFL_BIT 0x00020000
210/** ISCSI BHS word 0: residual overflow. */
211#define ISCSI_RESIDUAL_OVFL_BIT 0x00040000
212/** ISCSI BHS word 0: Bidirectional read residual underflow. */
213#define ISCSI_BI_READ_RESIDUAL_UNFL_BIT 0x00080000
214/** ISCSI BHS word 0: Bidirectional read residual overflow. */
215#define ISCSI_BI_READ_RESIDUAL_OVFL_BIT 0x00100000
216
217/** ISCSI BHS word 0: SCSI response mask. */
218#define ISCSI_SCSI_RESPONSE_MASK 0x0000ff00
219/** ISCSI BHS word 0: SCSI status mask. */
220#define ISCSI_SCSI_STATUS_MASK 0x000000ff
221
222/** ISCSI BHS word 0: response includes status. */
223#define ISCSI_STATUS_BIT 0x00010000
224
225
226/**
227 * iSCSI login status class. */
228typedef enum ISCSILOGINSTATUSCLASS
229{
230 /** Success. */
231 ISCSI_LOGIN_STATUS_CLASS_SUCCESS = 0,
232 /** Redirection. */
233 ISCSI_LOGIN_STATUS_CLASS_REDIRECTION,
234 /** Initiator error. */
235 ISCSI_LOGIN_STATUS_CLASS_INITIATOR_ERROR,
236 /** Target error. */
237 ISCSI_LOGIN_STATUS_CLASS_TARGET_ERROR
238} ISCSILOGINSTATUSCLASS;
239
240
241/**
242 * iSCSI connection state. */
243typedef enum ISCSISTATE
244{
245 /** Not having a connection/session at all. */
246 ISCSISTATE_FREE,
247 /** Currently trying to login. */
248 ISCSISTATE_IN_LOGIN,
249 /** Normal operation, corresponds roughly to the Full Feature Phase. */
250 ISCSISTATE_NORMAL,
251 /** Currently trying to logout. */
252 ISCSISTATE_IN_LOGOUT
253} ISCSISTATE;
254
255
256/*******************************************************************************
257* Structures and Typedefs *
258*******************************************************************************/
259/**
260 * iSCSI Request PDU buffer (gather).
261 */
262typedef struct ISCSIREQ
263{
264 /** Length of PDU segment in bytes. */
265 size_t cbSeg;
266 /** Pointer to PDU segment. */
267 const void *pcvSeg;
268} ISCSIREQ;
269/** Pointer to an iSCSI Request PDU buffer. */
270typedef ISCSIREQ *PISCSIREQ;
271/** Pointer to a const iSCSI Request PDU buffer. */
272typedef ISCSIREQ const *PCISCSIREQ;
273
274
275/**
276 * Block driver instance data.
277 */
278typedef struct ISCSIIMAGE
279{
280 /** Pointer to the filename (location). Not really used. */
281 const char *pszFilename;
282 /** Pointer to the initiator name. */
283 char *pszInitiatorName;
284 /** Pointer to the target name. */
285 char *pszTargetName;
286 /** Pointer to the target address. */
287 char *pszTargetAddress;
288 /** Pointer to the user name for authenticating the Initiator. */
289 char *pszInitiatorUsername;
290 /** Pointer to the secret for authenticating the Initiator. */
291 uint8_t *pbInitiatorSecret;
292 /** Length of the secret for authenticating the Initiator. */
293 size_t cbInitiatorSecret;
294 /** Pointer to the user name for authenticating the Target. */
295 char *pszTargetUsername;
296 /** Pointer to the secret for authenticating the Initiator. */
297 uint8_t *pbTargetSecret;
298 /** Length of the secret for authenticating the Initiator. */
299 size_t cbTargetSecret;
300 /** Limit for iSCSI writes, essentially limiting the amount of data
301 * written in a single write. This is negotiated with the target, so
302 * the actual size might be smaller. */
303 uint32_t cbWriteSplit;
304 /** Initiator session identifier. */
305 uint64_t ISID;
306 /** SCSI Logical Unit Number. */
307 uint64_t LUN;
308 /** Pointer to the per-disk VD interface list. */
309 PVDINTERFACE pVDIfsDisk;
310 /** Error interface. */
311 PVDINTERFACE pInterfaceError;
312 /** Error interface callback table. */
313 PVDINTERFACEERROR pInterfaceErrorCallbacks;
314 /** TCP network stack interface. */
315 PVDINTERFACE pInterfaceNet;
316 /** TCP network stack interface callback table. */
317 PVDINTERFACETCPNET pInterfaceNetCallbacks;
318 /** Pointer to the per-image VD interface list. */
319 PVDINTERFACE pVDIfsImage;
320 /** Config interface. */
321 PVDINTERFACE pInterfaceConfig;
322 /** Config interface callback table. */
323 PVDINTERFACECONFIG pInterfaceConfigCallbacks;
324 /** Image open flags. */
325 unsigned uOpenFlags;
326 /** Number of re-login retries when a connection fails. */
327 uint32_t cISCSIRetries;
328 /** Sector size on volume. */
329 uint32_t cbSector;
330 /** Size of volume in sectors. */
331 uint64_t cVolume;
332 /** Total volume size in bytes. Easiert that multiplying the above values all the time. */
333 uint64_t cbSize;
334
335 /** Negotiated maximum data length when sending to target. */
336 uint32_t cbSendDataLength;
337 /** Negotiated maximum data length when receiving from target. */
338 uint32_t cbRecvDataLength;
339
340 /** Current state of the connection/session. */
341 ISCSISTATE state;
342 /** Flag whether the first Login Response PDU has been seen. */
343 bool FirstRecvPDU;
344 /** Initiator Task Tag of the last iSCSI request PDU. */
345 uint32_t ITT;
346 /** Sequence number of the last command. */
347 uint32_t CmdSN;
348 /** Sequence number of the next command expected by the target. */
349 uint32_t ExpCmdSN;
350 /** Maximum sequence number accepted by the target (determines size of window). */
351 uint32_t MaxCmdSN;
352 /** Expected sequence number of next status. */
353 uint32_t ExpStatSN;
354 /** Currently active request. */
355 PISCSIREQ paCurrReq;
356 /** Segment number of currently active request. */
357 uint32_t cnCurrReq;
358 /** Pointer to receive PDU buffer. (Freed by RT) */
359 void *pvRecvPDUBuf;
360 /** Length of receive PDU buffer. */
361 size_t cbRecvPDUBuf;
362 /** Mutex protecting against concurrent use from several threads. */
363 RTSEMMUTEX Mutex;
364
365 /** Pointer to the target hostname. */
366 char *pszHostname;
367 /** Pointer to the target hostname. */
368 uint32_t uPort;
369 /** Socket handle of the TCP connection. */
370 RTSOCKET Socket;
371 /** Timeout for read operations on the TCP connection (in milliseconds). */
372 uint32_t uReadTimeout;
373 /** Flag whether to use the host IP stack or DevINIP. */
374 bool fHostIP;
375} ISCSIIMAGE, *PISCSIIMAGE;
376
377
378/**
379 * SCSI transfer directions.
380 */
381typedef enum SCSIXFER
382{
383 SCSIXFER_NONE = 0,
384 SCSIXFER_TO_TARGET,
385 SCSIXFER_FROM_TARGET,
386 SCSIXFER_TO_FROM_TARGET
387} SCSIXFER, *PSCSIXFER;
388
389
390/**
391 * SCSI request structure.
392 */
393typedef struct SCSIREQ
394{
395 /** Transfer direction. */
396 SCSIXFER enmXfer;
397 /** Length of command block. */
398 size_t cbCmd;
399 /** Length of Initiator2Target data buffer. */
400 size_t cbI2TData;
401 /** Length of Target2Initiator data buffer. */
402 size_t cbT2IData;
403 /** Length of sense buffer. */
404 size_t cbSense;
405 /** Completion status of the command. */
406 uint8_t status;
407 /** Pointer to command block. */
408 void *pvCmd;
409 /** Pointer to Initiator2Target data buffer. */
410 const void *pcvI2TData;
411 /** Pointer to Target2Initiator data buffer. */
412 void *pvT2IData;
413 /** Pointer to sense buffer. */
414 void *pvSense;
415} SCSIREQ, *PSCSIREQ;
416
417
418/**
419 * iSCSI login negotiation parameter
420 */
421typedef struct ISCSIPARAMETER
422{
423 /** Name of the parameter. */
424 const char *pszParamName;
425 /** Value of the parameter. */
426 const char *pszParamValue;
427 /** Length of the binary parameter. 0=zero-terminated string. */
428 size_t cbParamValue;
429} ISCSIPARAMETER;
430
431
432/**
433 * iSCSI Response PDU buffer (scatter).
434 */
435typedef struct ISCSIRES
436{
437 /** Length of PDU segment. */
438 size_t cbSeg;
439 /** Pointer to PDU segment. */
440 void *pvSeg;
441} ISCSIRES;
442/** Pointer to an iSCSI Response PDU buffer. */
443typedef ISCSIRES *PISCSIRES;
444/** Pointer to a const iSCSI Response PDU buffer. */
445typedef ISCSIRES const *PCISCSIRES;
446
447
448/*******************************************************************************
449* Static Variables *
450*******************************************************************************/
451
452/** Counter for getting unique instance IDs. */
453static uint32_t s_u32iscsiID = 0;
454
455/** Default LUN. */
456static const char *s_iscsiConfigDefaultLUN = "0";
457
458/** Default initiator name. */
459static const char *s_iscsiConfigDefaultInitiatorName = "iqn.2009-08.com.sun.virtualbox.initiator";
460
461/** Default timeout, 10 seconds. */
462static const char *s_iscsiConfigDefaultTimeout = "10000";
463
464/** Default write split value, less or equal to ISCSI_DATA_LENGTH_MAX. */
465static const char *s_iscsiConfigDefaultWriteSplit = "262144";
466
467/** Default host IP stack. */
468static const char *s_iscsiConfigDefaultHostIPStack = "1";
469
470/** Description of all accepted config parameters. */
471static const VDCONFIGINFO s_iscsiConfigInfo[] =
472{
473 { "TargetName", NULL, VDCFGVALUETYPE_STRING, VD_CFGKEY_MANDATORY },
474 /* LUN is defined of string type to handle the "enc" prefix. */
475 { "LUN", s_iscsiConfigDefaultLUN, VDCFGVALUETYPE_STRING, VD_CFGKEY_MANDATORY },
476 { "TargetAddress", NULL, VDCFGVALUETYPE_STRING, VD_CFGKEY_MANDATORY },
477 { "InitiatorName", s_iscsiConfigDefaultInitiatorName, VDCFGVALUETYPE_STRING, 0 },
478 { "InitiatorUsername", NULL, VDCFGVALUETYPE_STRING, 0 },
479 { "InitiatorSecret", NULL, VDCFGVALUETYPE_BYTES, 0 },
480 { "TargetUsername", NULL, VDCFGVALUETYPE_STRING, VD_CFGKEY_EXPERT },
481 { "TargetSecret", NULL, VDCFGVALUETYPE_BYTES, VD_CFGKEY_EXPERT },
482 { "WriteSplit", s_iscsiConfigDefaultWriteSplit, VDCFGVALUETYPE_INTEGER, VD_CFGKEY_EXPERT },
483 { "Timeout", s_iscsiConfigDefaultTimeout, VDCFGVALUETYPE_INTEGER, VD_CFGKEY_EXPERT },
484 { "HostIPStack", s_iscsiConfigDefaultHostIPStack, VDCFGVALUETYPE_INTEGER, VD_CFGKEY_EXPERT },
485 { NULL, NULL, VDCFGVALUETYPE_INTEGER, 0 }
486};
487
488/*******************************************************************************
489* Internal Functions *
490*******************************************************************************/
491
492/* iSCSI low-level functions (only to be used from the iSCSI high-level functions). */
493static uint32_t iscsiNewITT(PISCSIIMAGE pImage);
494static int iscsiSendPDU(PISCSIIMAGE pImage, PISCSIREQ paReq, uint32_t cnReq);
495static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes);
496static int drvISCSIValidatePDU(PISCSIRES paRes, uint32_t cnRes);
497static int iscsiTextAddKeyValue(uint8_t *pbBuf, size_t cbBuf, size_t *pcbBufCurr, const char *pcszKey, const char *pcszValue, size_t cbValue);
498static int iscsiTextGetKeyValue(const uint8_t *pbBuf, size_t cbBuf, const char *pcszKey, const char **ppcszValue);
499static int iscsiStrToBinary(const char *pcszValue, uint8_t *pbValue, size_t *pcbValue);
500static int iscsiUpdateParameters(PISCSIIMAGE pImage, const uint8_t *pbBuf, size_t cbBuf);
501
502/* Serial number arithmetic comparison. */
503static bool serial_number_less(uint32_t sn1, uint32_t sn2);
504
505/* CHAP-MD5 functions. */
506#ifdef IMPLEMENT_TARGET_AUTH
507static void chap_md5_generate_challenge(uint8_t *pbChallenge, size_t *pcbChallenge);
508#endif
509static void chap_md5_compute_response(uint8_t *pbResponse, uint8_t id, const uint8_t *pbChallenge, size_t cbChallenge,
510 const uint8_t *pbSecret, size_t cbSecret);
511
512
513/**
514 * Internal: signal an error to the frontend.
515 */
516DECLINLINE(int) iscsiError(PISCSIIMAGE pImage, int rc, RT_SRC_POS_DECL,
517 const char *pszFormat, ...)
518{
519 va_list va;
520 va_start(va, pszFormat);
521 if (pImage->pInterfaceError)
522 pImage->pInterfaceErrorCallbacks->pfnError(pImage->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS,
523 pszFormat, va);
524 va_end(va);
525
526#ifdef LOG_ENABLED
527 va_start(va, pszFormat);
528 Log(("iscsiError(%d/%s): %N\n", iLine, pszFunction, pszFormat, &va));
529 va_end(va);
530#endif
531 return rc;
532}
533
534
535static int iscsiTransportRead(PISCSIIMAGE pImage, PISCSIRES paResponse, unsigned int cnResponse)
536{
537 int rc = VINF_SUCCESS;
538 unsigned int i = 0;
539 size_t cbToRead, cbActuallyRead, residual, cbSegActual = 0, cbAHSLength, cbDataLength;
540 char *pDst;
541
542 LogFlowFunc(("cnResponse=%d (%s:%d)\n", cnResponse, pImage->pszHostname, pImage->uPort));
543 if (pImage->Socket == NIL_RTSOCKET)
544 {
545 /* Attempt to reconnect if the connection was previously broken. */
546 if (pImage->pszHostname != NULL)
547 {
548 rc = pImage->pInterfaceNetCallbacks->pfnClientConnect(pImage->pszHostname, pImage->uPort, &pImage->Socket);
549 if (RT_UNLIKELY( RT_FAILURE(rc)
550 && ( rc == VERR_NET_CONNECTION_REFUSED
551 || rc == VERR_NET_CONNECTION_RESET
552 || rc == VERR_NET_UNREACHABLE
553 || rc == VERR_NET_HOST_UNREACHABLE
554 || rc == VERR_NET_CONNECTION_TIMED_OUT)))
555 {
556 /* Standardize return value for no connection. */
557 rc = VERR_NET_CONNECTION_REFUSED;
558 }
559 }
560 }
561
562 if (RT_SUCCESS(rc) && paResponse[0].cbSeg >= 48)
563 {
564 cbToRead = 0;
565 residual = 48; /* Do not read more than the BHS length before the true PDU length is known. */
566 cbSegActual = residual;
567 pDst = (char *)paResponse[i].pvSeg;
568 uint64_t u64Timeout = RTTimeMilliTS() + pImage->uReadTimeout;
569 do
570 {
571 int64_t cMilliesRemaining = u64Timeout - RTTimeMilliTS();
572 if (cMilliesRemaining <= 0)
573 {
574 rc = VERR_TIMEOUT;
575 break;
576 }
577 Assert(cMilliesRemaining < 1000000);
578 rc = pImage->pInterfaceNetCallbacks->pfnSelectOne(pImage->Socket,
579 cMilliesRemaining);
580 if (RT_FAILURE(rc))
581 break;
582 rc = pImage->pInterfaceNetCallbacks->pfnRead(pImage->Socket,
583 pDst, residual,
584 &cbActuallyRead);
585 if (RT_FAILURE(rc))
586 break;
587 if (cbActuallyRead == 0)
588 {
589 /* The other end has closed the connection. */
590 pImage->pInterfaceNetCallbacks->pfnClientClose(pImage->Socket);
591 pImage->Socket = NIL_RTSOCKET;
592 rc = VERR_NET_CONNECTION_RESET;
593 break;
594 }
595 if (cbToRead == 0)
596 {
597 /* Currently reading the BHS. */
598 residual -= cbActuallyRead;
599 pDst += cbActuallyRead;
600 if (residual <= 40)
601 {
602 /* Enough data read to figure out the actual PDU size. */
603 uint32_t word1 = RT_N2H_U32(((uint32_t *)(paResponse[0].pvSeg))[1]);
604 cbAHSLength = (word1 & 0xff000000) >> 24;
605 cbAHSLength = ((cbAHSLength - 1) | 3) + 1; /* Add padding. */
606 cbDataLength = word1 & 0x00ffffff;
607 cbDataLength = ((cbDataLength - 1) | 3) + 1; /* Add padding. */
608 cbToRead = residual + cbAHSLength + cbDataLength;
609 residual += paResponse[0].cbSeg - 48;
610 if (residual > cbToRead)
611 residual = cbToRead;
612 cbSegActual = 48 + cbAHSLength + cbDataLength;
613 /* Check whether we are already done with this PDU (no payload). */
614 if (cbToRead == 0)
615 break;
616 }
617 }
618 else
619 {
620 cbToRead -= cbActuallyRead;
621 if (cbToRead == 0)
622 break;
623 pDst += cbActuallyRead;
624 residual -= cbActuallyRead;
625 }
626 if (residual == 0)
627 {
628 i++;
629 if (i >= cnResponse)
630 {
631 /* No space left in receive buffers. */
632 rc = VERR_BUFFER_OVERFLOW;
633 break;
634 }
635 pDst = (char *)paResponse[i].pvSeg;
636 residual = paResponse[i].cbSeg;
637 if (residual > cbToRead)
638 residual = cbToRead;
639 cbSegActual = residual;
640 }
641 } while (true);
642 }
643 else
644 {
645 if (RT_SUCCESS(rc))
646 rc = VERR_BUFFER_OVERFLOW;
647 }
648 if (RT_SUCCESS(rc))
649 {
650 paResponse[i].cbSeg = cbSegActual;
651 for (i++; i < cnResponse; i++)
652 paResponse[i].cbSeg = 0;
653 }
654
655 if (RT_UNLIKELY( RT_FAILURE(rc)
656 && ( rc == VERR_NET_CONNECTION_RESET
657 || rc == VERR_NET_CONNECTION_ABORTED
658 || rc == VERR_NET_CONNECTION_RESET_BY_PEER
659 || rc == VERR_NET_CONNECTION_REFUSED
660 || rc == VERR_BROKEN_PIPE)))
661 {
662 /* Standardize return value for broken connection. */
663 rc = VERR_BROKEN_PIPE;
664 }
665
666 LogFlowFunc(("returns %Rrc\n", rc));
667 return rc;
668}
669
670
671static int iscsiTransportWrite(PISCSIIMAGE pImage, PISCSIREQ paRequest, unsigned int cnRequest)
672{
673 int rc = VINF_SUCCESS;
674 uint32_t pad = 0;
675 unsigned int i;
676
677 LogFlow(("iscsiTransportWrite: cnRequest=%d (%s:%d)\n", cnRequest, pImage->pszHostname, pImage->uPort));
678 if (pImage->Socket == NIL_RTSOCKET)
679 {
680 /* Attempt to reconnect if the connection was previously broken. */
681 if (pImage->pszHostname != NULL)
682 {
683 rc = pImage->pInterfaceNetCallbacks->pfnClientConnect(pImage->pszHostname, pImage->uPort, &pImage->Socket);
684 if (RT_UNLIKELY( RT_FAILURE(rc)
685 && ( rc == VERR_NET_CONNECTION_REFUSED
686 || rc == VERR_NET_CONNECTION_RESET
687 || rc == VERR_NET_UNREACHABLE
688 || rc == VERR_NET_HOST_UNREACHABLE
689 || rc == VERR_NET_CONNECTION_TIMED_OUT)))
690 {
691 /* Standardize return value for no connection. */
692 rc = VERR_NET_CONNECTION_REFUSED;
693 }
694 }
695 }
696
697 if (RT_SUCCESS(rc))
698 {
699 for (i = 0; i < cnRequest; i++)
700 {
701 /* Write one chunk of data. */
702 rc = pImage->pInterfaceNetCallbacks->pfnWrite(pImage->Socket,
703 paRequest[i].pcvSeg,
704 paRequest[i].cbSeg);
705 if (RT_FAILURE(rc))
706 break;
707 /* Insert proper padding before the next chunk us written. */
708 if (paRequest[i].cbSeg & 3)
709 {
710 rc = pImage->pInterfaceNetCallbacks->pfnWrite(pImage->Socket,
711 &pad,
712 4 - (paRequest[i].cbSeg & 3));
713 if (RT_FAILURE(rc))
714 break;
715 }
716 }
717 /* Send out the request as soon as possible, otherwise the target will
718 * answer after an unnecessary delay. */
719 pImage->pInterfaceNetCallbacks->pfnFlush(pImage->Socket);
720 }
721
722 if (RT_UNLIKELY( RT_FAILURE(rc)
723 && ( rc == VERR_NET_CONNECTION_RESET
724 || rc == VERR_NET_CONNECTION_ABORTED
725 || rc == VERR_NET_CONNECTION_RESET_BY_PEER
726 || rc == VERR_NET_CONNECTION_REFUSED
727 || rc == VERR_BROKEN_PIPE)))
728 {
729 /* Standardize return value for broken connection. */
730 rc = VERR_BROKEN_PIPE;
731 }
732
733 LogFlow(("iscsiTransportWrite: returns %Rrc\n", rc));
734 return rc;
735}
736
737
738static int iscsiTransportOpen(PISCSIIMAGE pImage)
739{
740 int rc = VINF_SUCCESS;
741 size_t cbHostname = 0; /* shut up gcc */
742 const char *pcszPort = NULL; /* shut up gcc */
743 char *pszPortEnd;
744 uint16_t uPort;
745
746 /* Clean up previous connection data. */
747 if (pImage->Socket != NIL_RTSOCKET)
748 {
749 pImage->pInterfaceNetCallbacks->pfnClientClose(pImage->Socket);
750 pImage->Socket = NIL_RTSOCKET;
751 }
752 if (pImage->pszHostname)
753 {
754 RTMemFree(pImage->pszHostname);
755 pImage->pszHostname = NULL;
756 pImage->uPort = 0;
757 }
758
759 /* Locate the port number via the colon separating the hostname from the port. */
760 if (*pImage->pszTargetAddress)
761 {
762 if (*pImage->pszTargetAddress != '[')
763 {
764 /* Normal hostname or IPv4 dotted decimal. */
765 pcszPort = strchr(pImage->pszTargetAddress, ':');
766 if (pcszPort != NULL)
767 {
768 cbHostname = pcszPort - pImage->pszTargetAddress;
769 pcszPort++;
770 }
771 else
772 cbHostname = strlen(pImage->pszTargetAddress);
773 }
774 else
775 {
776 /* IPv6 literal address. Contains colons, so skip to closing square bracket. */
777 pcszPort = strchr(pImage->pszTargetAddress, ']');
778 if (pcszPort != NULL)
779 {
780 pcszPort++;
781 cbHostname = pcszPort - pImage->pszTargetAddress;
782 if (*pcszPort == '\0')
783 pcszPort = NULL;
784 else if (*pcszPort != ':')
785 rc = VERR_PARSE_ERROR;
786 else
787 pcszPort++;
788 }
789 else
790 rc = VERR_PARSE_ERROR;
791 }
792 }
793 else
794 rc = VERR_PARSE_ERROR;
795
796 /* Now split address into hostname and port. */
797 if (RT_SUCCESS(rc))
798 {
799 pImage->pszHostname = (char *)RTMemAlloc(cbHostname + 1);
800 if (!pImage->pszHostname)
801 rc = VERR_NO_MEMORY;
802 else
803 {
804 memcpy(pImage->pszHostname, pImage->pszTargetAddress, cbHostname);
805 pImage->pszHostname[cbHostname] = '\0';
806 if (pcszPort != NULL)
807 {
808 rc = RTStrToUInt16Ex(pcszPort, &pszPortEnd, 0, &uPort);
809 /* Note that RT_SUCCESS() macro to check the rc value is not strict enough in this case. */
810 if (rc == VINF_SUCCESS && *pszPortEnd == '\0' && uPort != 0)
811 {
812 pImage->uPort = uPort;
813 }
814 else
815 {
816 rc = VERR_PARSE_ERROR;
817 }
818 }
819 else
820 pImage->uPort = ISCSI_DEFAULT_PORT;
821 }
822 }
823
824 if (RT_FAILURE(rc))
825 {
826 if (pImage->pszHostname)
827 {
828 RTMemFree(pImage->pszHostname);
829 pImage->pszHostname = NULL;
830 }
831 pImage->uPort = 0;
832 }
833
834 /* Note that in this implementation the actual connection establishment is
835 * delayed until a PDU is read or written. */
836 LogFlowFunc(("returns %Rrc\n", rc));
837 return rc;
838}
839
840
841static int iscsiTransportClose(PISCSIIMAGE pImage)
842{
843 int rc;
844
845 LogFlowFunc(("(%s:%d)\n", pImage->pszHostname, pImage->uPort));
846 if (pImage->Socket != NIL_RTSOCKET)
847 {
848 rc = pImage->pInterfaceNetCallbacks->pfnClientClose(pImage->Socket);
849 pImage->Socket = NIL_RTSOCKET;
850 }
851 else
852 rc = VINF_SUCCESS;
853 LogFlowFunc(("returns %Rrc\n", rc));
854 return rc;
855}
856
857
858/**
859 * Attach to an iSCSI target. Performs all operations necessary to enter
860 * Full Feature Phase.
861 *
862 * @returns VBox status.
863 * @param pImage The iSCSI connection state to be used.
864 */
865static int iscsiAttach(PISCSIIMAGE pImage)
866{
867 int rc;
868 uint32_t itt;
869 uint32_t csg, nsg, substate;
870 uint64_t isid_tsih;
871 uint8_t bBuf[4096]; /* Should be large enough even for large authentication values. */
872 size_t cbBuf;
873 bool transit;
874 uint8_t pbChallenge[1024]; /* RFC3720 specifies this as maximum. */
875 size_t cbChallenge = 0; /* shut up gcc */
876 uint8_t bChapIdx;
877 uint8_t aResponse[RTMD5HASHSIZE];
878 uint32_t cnISCSIReq;
879 ISCSIREQ aISCSIReq[4];
880 uint32_t aReqBHS[12];
881 uint32_t cnISCSIRes;
882 ISCSIRES aISCSIRes[2];
883 uint32_t aResBHS[12];
884 char *pszNext;
885
886 bool fParameterNeg = true;;
887 pImage->cbRecvDataLength = ISCSI_DATA_LENGTH_MAX;
888 pImage->cbSendDataLength = RT_MIN(ISCSI_DATA_LENGTH_MAX, pImage->cbWriteSplit);
889 char szMaxDataLength[16];
890 RTStrPrintf(szMaxDataLength, sizeof(szMaxDataLength), "%u", ISCSI_DATA_LENGTH_MAX);
891 ISCSIPARAMETER aParameterNeg[] =
892 {
893 { "HeaderDigest", "None", 0 },
894 { "DataDigest", "None", 0 },
895 { "MaxConnections", "1", 0 },
896 { "InitialR2T", "No", 0 },
897 { "ImmediateData", "Yes", 0 },
898 { "MaxRecvDataSegmentLength", szMaxDataLength, 0 },
899 { "MaxBurstLength", szMaxDataLength, 0 },
900 { "FirstBurstLength", szMaxDataLength, 0 },
901 { "DefaultTime2Wait", "0", 0 },
902 { "DefaultTime2Retain", "60", 0 },
903 { "DataPDUInOrder", "Yes", 0 },
904 { "DataSequenceInOrder", "Yes", 0 },
905 { "ErrorRecoveryLevel", "0", 0 },
906 { "MaxOutstandingR2T", "1", 0 }
907 };
908
909 LogFlowFunc(("entering\n"));
910
911 Assert(pImage->state == ISCSISTATE_FREE);
912
913 RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
914
915 /* Make 100% sure the connection isn't reused for a new login. */
916 iscsiTransportClose(pImage);
917
918restart:
919 pImage->state = ISCSISTATE_IN_LOGIN;
920 pImage->ITT = 1;
921 pImage->FirstRecvPDU = true;
922 pImage->CmdSN = 1;
923 pImage->ExpCmdSN = 0;
924 pImage->MaxCmdSN = 1;
925 pImage->ExpStatSN = 1;
926
927 /*
928 * Send login request to target.
929 */
930 itt = iscsiNewITT(pImage);
931 csg = 0;
932 nsg = 0;
933 substate = 0;
934 isid_tsih = pImage->ISID << 16; /* TSIH field currently always 0 */
935
936 do {
937 transit = false;
938 cbBuf = 0;
939 /* Handle all cases with a single switch statement. */
940 switch (csg << 8 | substate)
941 {
942 case 0x0000: /* security negotiation, step 0: propose authentication. */
943 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "SessionType", "Normal", 0);
944 if (RT_FAILURE(rc))
945 goto out;
946 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "InitiatorName", pImage->pszInitiatorName, 0);
947 if (RT_FAILURE(rc))
948 goto out;
949 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "TargetName", pImage->pszTargetName, 0);
950 if (RT_FAILURE(rc))
951 goto out;
952 if (pImage->pszInitiatorUsername == NULL)
953 {
954 /* No authentication. Immediately switch to next phase. */
955 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "AuthMethod", "None", 0);
956 if (RT_FAILURE(rc))
957 goto out;
958 nsg = 1;
959 transit = true;
960 }
961 else
962 {
963 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "AuthMethod", "CHAP,None", 0);
964 if (RT_FAILURE(rc))
965 goto out;
966 }
967 break;
968 case 0x0001: /* security negotiation, step 1: propose CHAP_MD5 variant. */
969 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_A", "5", 0);
970 if (RT_FAILURE(rc))
971 goto out;
972 break;
973 case 0x0002: /* security negotiation, step 2: send authentication info. */
974 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_N", pImage->pszInitiatorUsername, 0);
975 if (RT_FAILURE(rc))
976 goto out;
977 chap_md5_compute_response(aResponse, bChapIdx, pbChallenge, cbChallenge,
978 pImage->pbInitiatorSecret, pImage->cbInitiatorSecret);
979 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_R", (const char *)aResponse, RTMD5HASHSIZE);
980 if (RT_FAILURE(rc))
981 goto out;
982 nsg = 1;
983 transit = true;
984 break;
985 case 0x0100: /* login operational negotiation, step 0: set parameters. */
986 if (fParameterNeg)
987 {
988 for (unsigned i = 0; i < RT_ELEMENTS(aParameterNeg); i++)
989 {
990 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf,
991 aParameterNeg[i].pszParamName,
992 aParameterNeg[i].pszParamValue,
993 aParameterNeg[i].cbParamValue);
994 if (RT_FAILURE(rc))
995 goto out;
996 }
997 fParameterNeg = false;
998 }
999
1000 nsg = 3;
1001 transit = true;
1002 break;
1003 case 0x0300: /* full feature phase. */
1004 default:
1005 /* Should never come here. */
1006 AssertMsgFailed(("send: Undefined login state %d substate %d\n", csg, substate));
1007 break;
1008 }
1009
1010 aReqBHS[0] = RT_H2N_U32( ISCSI_IMMEDIATE_DELIVERY_BIT
1011 | (csg << ISCSI_CSG_SHIFT)
1012 | (transit ? (nsg << ISCSI_NSG_SHIFT | ISCSI_TRANSIT_BIT) : 0)
1013 | ISCSI_MY_VERSION /* Minimum version. */
1014 | (ISCSI_MY_VERSION << 8) /* Maximum version. */
1015 | ISCSIOP_LOGIN_REQ); /* C=0 */
1016 aReqBHS[1] = RT_H2N_U32((uint32_t)cbBuf); /* TotalAHSLength=0 */
1017 aReqBHS[2] = RT_H2N_U32(isid_tsih >> 32);
1018 aReqBHS[3] = RT_H2N_U32(isid_tsih & 0xffffffff);
1019 aReqBHS[4] = itt;
1020 aReqBHS[5] = RT_H2N_U32(1 << 16); /* CID=1,reserved */
1021 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
1022#if 0 /** @todo This ExpStatSN hack is required to make the netbsd-iscsi target working. Could be a bug in the target,
1023 * but they claim a bunch of other initiators works fine with it... Needs looking into. */
1024 aReqBHS[7] = RT_H2N_U32(RT_MIN(pImage->ExpCmdSN, pImage->MaxCmdSN));
1025#else
1026 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
1027#endif
1028 aReqBHS[8] = 0; /* reserved */
1029 aReqBHS[9] = 0; /* reserved */
1030 aReqBHS[10] = 0; /* reserved */
1031 aReqBHS[11] = 0; /* reserved */
1032
1033 cnISCSIReq = 0;
1034 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
1035 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
1036 cnISCSIReq++;
1037
1038 aISCSIReq[cnISCSIReq].pcvSeg = bBuf;
1039 aISCSIReq[cnISCSIReq].cbSeg = cbBuf;
1040 cnISCSIReq++;
1041
1042 rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq);
1043 if (RT_SUCCESS(rc))
1044 {
1045 ISCSIOPCODE cmd;
1046 ISCSILOGINSTATUSCLASS loginStatusClass;
1047
1048 cnISCSIRes = 0;
1049 aISCSIRes[cnISCSIRes].pvSeg = aResBHS;
1050 aISCSIRes[cnISCSIRes].cbSeg = sizeof(aResBHS);
1051 cnISCSIRes++;
1052 aISCSIRes[cnISCSIRes].pvSeg = bBuf;
1053 aISCSIRes[cnISCSIRes].cbSeg = sizeof(bBuf);
1054 cnISCSIRes++;
1055
1056 rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes);
1057 if (RT_FAILURE(rc))
1058 break;
1059 /** @todo collect partial login responses with Continue bit set. */
1060 Assert(aISCSIRes[0].pvSeg == aResBHS);
1061 Assert(aISCSIRes[0].cbSeg >= ISCSI_BHS_SIZE);
1062 Assert((RT_N2H_U32(aResBHS[0]) & ISCSI_CONTINUE_BIT) == 0);
1063
1064 cmd = (ISCSIOPCODE)(RT_N2H_U32(aResBHS[0]) & ISCSIOP_MASK);
1065 if (cmd == ISCSIOP_LOGIN_RES)
1066 {
1067 if ((RT_N2H_U32(aResBHS[0]) & 0xff) != ISCSI_MY_VERSION)
1068 {
1069 iscsiTransportClose(pImage);
1070 rc = VERR_PARSE_ERROR;
1071 break; /* Give up immediately, as a RFC violation in version fields is very serious. */
1072 }
1073
1074 loginStatusClass = (ISCSILOGINSTATUSCLASS)(RT_N2H_U32(aResBHS[9]) >> 24);
1075 switch (loginStatusClass)
1076 {
1077 case ISCSI_LOGIN_STATUS_CLASS_SUCCESS:
1078 uint32_t targetCSG;
1079 uint32_t targetNSG;
1080 bool targetTransit;
1081
1082 if (pImage->FirstRecvPDU)
1083 {
1084 pImage->FirstRecvPDU = false;
1085 pImage->ExpStatSN = RT_N2H_U32(aResBHS[6]) + 1;
1086 }
1087
1088 targetCSG = (RT_N2H_U32(aResBHS[0]) & ISCSI_CSG_MASK) >> ISCSI_CSG_SHIFT;
1089 targetNSG = (RT_N2H_U32(aResBHS[0]) & ISCSI_NSG_MASK) >> ISCSI_NSG_SHIFT;
1090 targetTransit = !!(RT_N2H_U32(aResBHS[0]) & ISCSI_TRANSIT_BIT);
1091
1092 /* Handle all cases with a single switch statement. */
1093 switch (csg << 8 | substate)
1094 {
1095 case 0x0000: /* security negotiation, step 0: receive final authentication. */
1096 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1097 if (RT_FAILURE(rc))
1098 break;
1099
1100 const char *pcszAuthMethod;
1101
1102 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "AuthMethod", &pcszAuthMethod);
1103 if (RT_FAILURE(rc))
1104 {
1105 rc = VERR_PARSE_ERROR;
1106 break;
1107 }
1108 if (strcmp(pcszAuthMethod, "None") == 0)
1109 {
1110 /* Authentication offered, but none required. Skip to operational parameters. */
1111 csg = 1;
1112 nsg = 1;
1113 transit = true;
1114 substate = 0;
1115 break;
1116 }
1117 else if (strcmp(pcszAuthMethod, "CHAP") == 0 && targetNSG == 0 && !targetTransit)
1118 {
1119 /* CHAP authentication required, continue with next substate. */
1120 substate++;
1121 break;
1122 }
1123
1124 /* Unknown auth method or login response PDU headers incorrect. */
1125 rc = VERR_PARSE_ERROR;
1126 break;
1127 case 0x0001: /* security negotiation, step 1: receive final CHAP variant and challenge. */
1128 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1129 if (RT_FAILURE(rc))
1130 break;
1131
1132 const char *pcszChapAuthMethod;
1133 const char *pcszChapIdxTarget;
1134 const char *pcszChapChallengeStr;
1135
1136 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_A", &pcszChapAuthMethod);
1137 if (RT_FAILURE(rc))
1138 {
1139 rc = VERR_PARSE_ERROR;
1140 break;
1141 }
1142 if (strcmp(pcszChapAuthMethod, "5") != 0)
1143 {
1144 rc = VERR_PARSE_ERROR;
1145 break;
1146 }
1147 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_I", &pcszChapIdxTarget);
1148 if (RT_FAILURE(rc))
1149 {
1150 rc = VERR_PARSE_ERROR;
1151 break;
1152 }
1153 rc = RTStrToUInt8Ex(pcszChapIdxTarget, &pszNext, 0, &bChapIdx);
1154 if ((rc > VINF_SUCCESS) || *pszNext != '\0')
1155 {
1156 rc = VERR_PARSE_ERROR;
1157 break;
1158 }
1159 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_C", &pcszChapChallengeStr);
1160 if (RT_FAILURE(rc))
1161 {
1162 rc = VERR_PARSE_ERROR;
1163 break;
1164 }
1165 cbChallenge = sizeof(pbChallenge);
1166 rc = iscsiStrToBinary(pcszChapChallengeStr, pbChallenge, &cbChallenge);
1167 if (RT_FAILURE(rc))
1168 break;
1169 substate++;
1170 transit = true;
1171 break;
1172 case 0x0002: /* security negotiation, step 2: check authentication success. */
1173 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1174 if (RT_FAILURE(rc))
1175 break;
1176
1177 if (targetCSG == 0 && targetNSG == 1 && targetTransit)
1178 {
1179 /* Target wants to continue in login operational state, authentication success. */
1180 csg = 1;
1181 nsg = 3;
1182 substate = 0;
1183 break;
1184 }
1185 rc = VERR_PARSE_ERROR;
1186 break;
1187 case 0x0100: /* login operational negotiation, step 0: check results. */
1188 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1189 if (RT_FAILURE(rc))
1190 break;
1191
1192 if (targetCSG == 1 && targetNSG == 3 && targetTransit)
1193 {
1194 /* Target wants to continue in full feature phase, login finished. */
1195 csg = 3;
1196 nsg = 3;
1197 substate = 0;
1198 break;
1199 }
1200 else if (targetCSG == 1 && targetNSG == 1 && !targetTransit)
1201 {
1202 /* Target wants to negotiate certain parameters and
1203 * stay in login operational negotiation. */
1204 csg = 1;
1205 nsg = 3;
1206 substate = 0;
1207 }
1208 rc = VERR_PARSE_ERROR;
1209 break;
1210 case 0x0300: /* full feature phase. */
1211 default:
1212 AssertMsgFailed(("recv: Undefined login state %d substate %d\n", csg, substate));
1213 rc = VERR_PARSE_ERROR;
1214 break;
1215 }
1216 break;
1217 case ISCSI_LOGIN_STATUS_CLASS_REDIRECTION:
1218 const char *pcszTargetRedir;
1219
1220 /* Target has moved to some other location, as indicated in the TargetAddress key. */
1221 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "TargetAddress", &pcszTargetRedir);
1222 if (RT_FAILURE(rc))
1223 {
1224 rc = VERR_PARSE_ERROR;
1225 break;
1226 }
1227 if (pImage->pszTargetAddress)
1228 RTMemFree(pImage->pszTargetAddress);
1229 {
1230 size_t cb = strlen(pcszTargetRedir) + 1;
1231 pImage->pszTargetAddress = (char *)RTMemAlloc(cb);
1232 if (!pImage->pszTargetAddress)
1233 {
1234 rc = VERR_NO_MEMORY;
1235 break;
1236 }
1237 memcpy(pImage->pszTargetAddress, pcszTargetRedir, cb);
1238 }
1239 rc = iscsiTransportOpen(pImage);
1240 goto restart;
1241 case ISCSI_LOGIN_STATUS_CLASS_INITIATOR_ERROR:
1242 iscsiTransportClose(pImage);
1243 rc = VERR_IO_GEN_FAILURE;
1244 goto out;
1245 case ISCSI_LOGIN_STATUS_CLASS_TARGET_ERROR:
1246 iscsiTransportClose(pImage);
1247 rc = VINF_EOF;
1248 break;
1249 default:
1250 rc = VERR_PARSE_ERROR;
1251 }
1252
1253 if (csg == 3)
1254 {
1255 /*
1256 * Finished login, continuing with Full Feature Phase.
1257 */
1258 rc = VINF_SUCCESS;
1259 break;
1260 }
1261 }
1262 else
1263 {
1264 AssertMsgFailed(("%s: ignoring unexpected PDU with first word = %#08x\n", __FUNCTION__, RT_N2H_U32(aResBHS[0])));
1265 }
1266 }
1267 else
1268 break;
1269 } while (true);
1270
1271out:
1272 if (RT_FAILURE(rc))
1273 {
1274 /*
1275 * Close connection to target.
1276 */
1277 iscsiTransportClose(pImage);
1278 pImage->state = ISCSISTATE_FREE;
1279 }
1280 else
1281 pImage->state = ISCSISTATE_NORMAL;
1282
1283 RTSemMutexRelease(pImage->Mutex);
1284
1285 LogFlowFunc(("returning %Rrc\n", rc));
1286 LogRel(("iSCSI: login to target %s %s\n", pImage->pszTargetName, RT_SUCCESS(rc) ? "successful" : "failed"));
1287 return rc;
1288}
1289
1290
1291/**
1292 * Detach from an iSCSI target.
1293 *
1294 * @returns VBox status.
1295 * @param pImage The iSCSI connection state to be used.
1296 */
1297static int iscsiDetach(PISCSIIMAGE pImage)
1298{
1299 int rc;
1300 uint32_t itt;
1301 uint32_t cnISCSIReq = 0;
1302 ISCSIREQ aISCSIReq[4];
1303 uint32_t aReqBHS[12];
1304 LogFlow(("iscsiDetach: entering\n"));
1305
1306 RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
1307
1308 if (pImage->state != ISCSISTATE_FREE && pImage->state != ISCSISTATE_IN_LOGOUT)
1309 {
1310 pImage->state = ISCSISTATE_IN_LOGOUT;
1311
1312 /*
1313 * Send logout request to target.
1314 */
1315 itt = iscsiNewITT(pImage);
1316 aReqBHS[0] = RT_H2N_U32(ISCSI_FINAL_BIT | ISCSIOP_LOGOUT_REQ); /* I=0,F=1,Reason=close session */
1317 aReqBHS[1] = RT_H2N_U32(0); /* TotalAHSLength=0,DataSementLength=0 */
1318 aReqBHS[2] = 0; /* reserved */
1319 aReqBHS[3] = 0; /* reserved */
1320 aReqBHS[4] = itt;
1321 aReqBHS[5] = 0; /* reserved */
1322 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
1323 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
1324 aReqBHS[8] = 0; /* reserved */
1325 aReqBHS[9] = 0; /* reserved */
1326 aReqBHS[10] = 0; /* reserved */
1327 aReqBHS[11] = 0; /* reserved */
1328 pImage->CmdSN++;
1329
1330 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
1331 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
1332 cnISCSIReq++;
1333
1334 rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq);
1335 if (RT_SUCCESS(rc))
1336 {
1337 /*
1338 * Read logout response from target.
1339 */
1340 ISCSIRES aISCSIRes;
1341 uint32_t aResBHS[12];
1342
1343 aISCSIRes.pvSeg = aResBHS;
1344 aISCSIRes.cbSeg = sizeof(aResBHS);
1345 rc = iscsiRecvPDU(pImage, itt, &aISCSIRes, 1);
1346 if (RT_SUCCESS(rc))
1347 {
1348 if (RT_N2H_U32(aResBHS[0]) != (ISCSI_FINAL_BIT | ISCSIOP_LOGOUT_RES))
1349 AssertMsgFailed(("iSCSI Logout response invalid\n"));
1350 }
1351 else
1352 AssertMsgFailed(("iSCSI Logout response error, rc=%Rrc\n", rc));
1353 }
1354 else
1355 AssertMsgFailed(("Could not send iSCSI Logout request, rc=%Rrc\n", rc));
1356 }
1357
1358 if (pImage->state != ISCSISTATE_FREE)
1359 {
1360 /*
1361 * Close connection to target.
1362 */
1363 rc = iscsiTransportClose(pImage);
1364 if (RT_FAILURE(rc))
1365 AssertMsgFailed(("Could not close connection to target, rc=%Rrc\n", rc));
1366 }
1367
1368 pImage->state = ISCSISTATE_FREE;
1369
1370 RTSemMutexRelease(pImage->Mutex);
1371
1372 LogFlow(("iscsiDetach: leaving\n"));
1373 LogRel(("iSCSI: logout to target %s\n", pImage->pszTargetName));
1374 return VINF_SUCCESS;
1375}
1376
1377
1378/**
1379 * Perform a command on an iSCSI target. Target must be already in
1380 * Full Feature Phase.
1381 *
1382 * @returns VBOX status.
1383 * @param pImage The iSCSI connection state to be used.
1384 * @param pRequest Command descriptor. Contains all information about
1385 * the command, its transfer directions and pointers
1386 * to the buffer(s) used for transferring data and
1387 * status information.
1388 */
1389static int iscsiCommand(PISCSIIMAGE pImage, PSCSIREQ pRequest)
1390{
1391 int rc;
1392 uint32_t itt;
1393 uint32_t cbData;
1394 uint32_t cnISCSIReq = 0;
1395 ISCSIREQ aISCSIReq[4];
1396 uint32_t aReqBHS[12];
1397
1398 uint32_t *pDst = NULL;
1399 size_t cbBufLength;
1400 uint32_t aStat[64];
1401 uint32_t ExpDataSN = 0;
1402 bool final = false;
1403
1404 LogFlow(("iscsiCommand: entering, CmdSN=%d\n", pImage->CmdSN));
1405
1406 Assert(pRequest->enmXfer != SCSIXFER_TO_FROM_TARGET); /**< @todo not yet supported, would require AHS. */
1407 Assert(pRequest->cbI2TData <= 0xffffff); /* larger transfers would require R2T support. */
1408 Assert(pRequest->cbCmd <= 16); /* would cause buffer overrun below. */
1409
1410 /* If not in normal state, then the transport connection was dropped. Try
1411 * to reestablish by logging in, the target might be responsive again. */
1412 if (pImage->state == ISCSISTATE_FREE)
1413 rc = iscsiAttach(pImage);
1414
1415 /* If still not in normal state, then the underlying transport connection
1416 * cannot be established. Get out before bad things happen (and make
1417 * sure the caller suspends the VM again). */
1418 if (pImage->state != ISCSISTATE_NORMAL)
1419 {
1420 rc = VERR_NET_CONNECTION_REFUSED;
1421 goto out;
1422 }
1423
1424 /*
1425 * Send SCSI command to target with all I2T data included.
1426 */
1427 cbData = 0;
1428 if (pRequest->enmXfer == SCSIXFER_FROM_TARGET)
1429 cbData = (uint32_t)pRequest->cbT2IData;
1430 else
1431 cbData = (uint32_t)pRequest->cbI2TData;
1432
1433 RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
1434
1435 itt = iscsiNewITT(pImage);
1436 memset(aReqBHS, 0, sizeof(aReqBHS));
1437 aReqBHS[0] = RT_H2N_U32( ISCSI_FINAL_BIT | ISCSI_TASK_ATTR_ORDERED | ISCSIOP_SCSI_CMD
1438 | (pRequest->enmXfer << 21)); /* I=0,F=1,Attr=Ordered */
1439 aReqBHS[1] = RT_H2N_U32(0x00000000 | ((uint32_t)pRequest->cbI2TData & 0xffffff)); /* TotalAHSLength=0 */
1440 aReqBHS[2] = RT_H2N_U32(pImage->LUN >> 32);
1441 aReqBHS[3] = RT_H2N_U32(pImage->LUN & 0xffffffff);
1442 aReqBHS[4] = itt;
1443 aReqBHS[5] = RT_H2N_U32(cbData);
1444 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
1445 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
1446 memcpy(aReqBHS + 8, pRequest->pvCmd, pRequest->cbCmd);
1447 pImage->CmdSN++;
1448
1449 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
1450 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
1451 cnISCSIReq++;
1452
1453 if ( pRequest->enmXfer == SCSIXFER_TO_TARGET
1454 || pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET)
1455 {
1456 aISCSIReq[cnISCSIReq].pcvSeg = pRequest->pcvI2TData;
1457 aISCSIReq[cnISCSIReq].cbSeg = pRequest->cbI2TData; /* Padding done by transport. */
1458 cnISCSIReq++;
1459 }
1460
1461 rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq);
1462 if (RT_FAILURE(rc))
1463 goto out_release;
1464
1465 /* Place SCSI request in queue. */
1466 pImage->paCurrReq = aISCSIReq;
1467 pImage->cnCurrReq = cnISCSIReq;
1468
1469 /*
1470 * Read SCSI response/data in PDUs from target.
1471 */
1472 if ( pRequest->enmXfer == SCSIXFER_FROM_TARGET
1473 || pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET)
1474 {
1475 pDst = (uint32_t *)pRequest->pvT2IData;
1476 cbBufLength = pRequest->cbT2IData;
1477 }
1478 else
1479 cbBufLength = 0;
1480
1481 do {
1482 uint32_t cnISCSIRes = 0;
1483 ISCSIRES aISCSIRes[4];
1484 uint32_t aResBHS[12];
1485
1486 aISCSIRes[cnISCSIRes].pvSeg = aResBHS;
1487 aISCSIRes[cnISCSIRes].cbSeg = sizeof(aResBHS);
1488 cnISCSIRes++;
1489 if (cbBufLength != 0 &&
1490 ( pRequest->enmXfer == SCSIXFER_FROM_TARGET
1491 || pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET))
1492 {
1493 aISCSIRes[cnISCSIRes].pvSeg = pDst;
1494 aISCSIRes[cnISCSIRes].cbSeg = cbBufLength;
1495 cnISCSIRes++;
1496 }
1497 /* Always reserve space for the status - it's impossible to tell
1498 * beforehand whether this will be the final PDU or not. */
1499 aISCSIRes[cnISCSIRes].pvSeg = aStat;
1500 aISCSIRes[cnISCSIRes].cbSeg = sizeof(aStat);
1501 cnISCSIRes++;
1502
1503 rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes);
1504 if (RT_FAILURE(rc))
1505 break;
1506
1507 final = !!(RT_N2H_U32(aResBHS[0]) & ISCSI_FINAL_BIT);
1508 ISCSIOPCODE cmd = (ISCSIOPCODE)(RT_N2H_U32(aResBHS[0]) & ISCSIOP_MASK);
1509 if (cmd == ISCSIOP_SCSI_RES)
1510 {
1511 /* This is the final PDU which delivers the status (and may be omitted if
1512 * the last Data-In PDU included successful completion status). Note
1513 * that ExpStatSN has been bumped already in iscsiRecvPDU. */
1514 if (!final || ((RT_N2H_U32(aResBHS[0]) & 0x0000ff00) != 0) || (RT_N2H_U32(aResBHS[6]) != pImage->ExpStatSN - 1))
1515 {
1516 /* SCSI Response in the wrong place or with a (target) failure. */
1517 rc = VERR_PARSE_ERROR;
1518 break;
1519 }
1520 /* The following is a bit tricky, as in error situations we may
1521 * get the status only instead of the result data plus optional
1522 * status. Thus the status may have ended up partially in the
1523 * data area. */
1524 pRequest->status = RT_N2H_U32(aResBHS[0]) & 0x000000ff;
1525 cbData = RT_N2H_U32(aResBHS[1]) & 0x00ffffff;
1526 if (cbData >= 2)
1527 {
1528 uint32_t cbStat = RT_N2H_U32(((uint32_t *)aISCSIRes[1].pvSeg)[0]) >> 16;
1529 if (cbStat + 2 > cbData || cbStat > pRequest->cbSense)
1530 {
1531 rc = VERR_BUFFER_OVERFLOW;
1532 break;
1533 }
1534 pRequest->cbSense = cbStat;
1535 memcpy(pRequest->pvSense, ((const uint8_t *)aISCSIRes[1].pvSeg) + 2, aISCSIRes[1].cbSeg - 2);
1536 if (cnISCSIRes > 2 && aISCSIRes[2].cbSeg && (ssize_t)cbStat - aISCSIRes[1].cbSeg - 2 > 0)
1537 memcpy((char *)pRequest->pvSense + aISCSIRes[1].cbSeg, aISCSIRes[2].pvSeg, cbStat - aISCSIRes[1].cbSeg - 2);
1538 }
1539 else if (cbData == 1)
1540 {
1541 rc = VERR_PARSE_ERROR;
1542 break;
1543 }
1544 else
1545 pRequest->cbSense = 0;
1546 break;
1547 }
1548 else if (cmd == ISCSIOP_SCSI_DATA_IN)
1549 {
1550 /* A Data-In PDU carries some data that needs to be added to the received
1551 * data in response to the command. There may be both partial and complete
1552 * Data-In PDUs, so collect data until the status is included or the status
1553 * is sent in a separate SCSI Result frame (see above). */
1554 if (final && aISCSIRes[2].cbSeg != 0)
1555 {
1556 /* The received PDU is partially stored in the buffer for status.
1557 * Must not happen under normal circumstances and is a target error. */
1558 rc = VERR_BUFFER_OVERFLOW;
1559 break;
1560 }
1561 uint32_t len = RT_N2H_U32(aResBHS[1]) & 0x00ffffff;
1562 pDst = (uint32_t *)((char *)pDst + len);
1563 cbBufLength -= len;
1564 ExpDataSN++;
1565 if (final && (RT_N2H_U32(aResBHS[0]) & ISCSI_STATUS_BIT) != 0)
1566 {
1567 pRequest->status = RT_N2H_U32(aResBHS[0]) & 0x000000ff;
1568 pRequest->cbSense = 0;
1569 break;
1570 }
1571 }
1572 else
1573 {
1574 rc = VERR_PARSE_ERROR;
1575 break;
1576 }
1577 } while (true);
1578
1579 /* Remove SCSI request from queue. */
1580 pImage->paCurrReq = NULL;
1581 pImage->cnCurrReq = 0;
1582
1583out_release:
1584 if (rc == VERR_TIMEOUT)
1585 {
1586 /* Drop connection in case the target plays dead. Much better than
1587 * delaying the next requests until the timed out command actually
1588 * finishes. Also keep in mind that command shouldn't take longer than
1589 * about 30-40 seconds, or the guest will lose its patience. */
1590 iscsiTransportClose(pImage);
1591 pImage->state = ISCSISTATE_FREE;
1592 }
1593 RTSemMutexRelease(pImage->Mutex);
1594
1595out:
1596 LogFlow(("iscsiCommand: returns %Rrc\n", rc));
1597 return rc;
1598}
1599
1600
1601/**
1602 * Generate a new Initiator Task Tag.
1603 *
1604 * @returns Initiator Task Tag.
1605 * @param pImage The iSCSI connection state to be used.
1606 */
1607static uint32_t iscsiNewITT(PISCSIIMAGE pImage)
1608{
1609 uint32_t next_itt;
1610
1611 next_itt = pImage->ITT++;
1612 if (pImage->ITT == ISCSI_TASK_TAG_RSVD)
1613 pImage->ITT = 0;
1614 return RT_H2N_U32(next_itt);
1615}
1616
1617
1618/**
1619 * Send an iSCSI request. The request can consist of several segments, which
1620 * are padded to 4 byte boundaries and concatenated.
1621 *
1622 * @returns VBOX status
1623 * @param pImage The iSCSI connection state to be used.
1624 * @param paReq Pointer to array of iSCSI request sections.
1625 * @param cnReq Number of valid iSCSI request sections in the array.
1626 */
1627static int iscsiSendPDU(PISCSIIMAGE pImage, PISCSIREQ paReq, uint32_t cnReq)
1628{
1629 int rc = VINF_SUCCESS;
1630 uint32_t i;
1631 /** @todo return VERR_VD_ISCSI_INVALID_STATE in the appropriate situations,
1632 * needs cleaning up of timeout/disconnect handling a bit, as otherwise
1633 * too many incorrect errors are signalled. */
1634 Assert(pImage->paCurrReq == NULL);
1635 Assert(cnReq >= 1);
1636 Assert(paReq[0].cbSeg >= ISCSI_BHS_SIZE);
1637
1638 for (i = 0; i < pImage->cISCSIRetries; i++)
1639 {
1640 rc = iscsiTransportWrite(pImage, paReq, cnReq);
1641 if (RT_SUCCESS(rc))
1642 break;
1643 if (rc != VERR_BROKEN_PIPE && rc != VERR_NET_CONNECTION_REFUSED)
1644 break;
1645 /* No point in reestablishing the connection for a logout */
1646 if (pImage->state == ISCSISTATE_IN_LOGOUT)
1647 break;
1648 RTThreadSleep(500);
1649 if (pImage->state != ISCSISTATE_IN_LOGIN)
1650 {
1651 /* Attempt to re-login when a connection fails, but only when not
1652 * currently logging in. */
1653 rc = iscsiAttach(pImage);
1654 if (RT_FAILURE(rc))
1655 break;
1656 }
1657 }
1658 return rc;
1659}
1660
1661
1662/**
1663 * Wait for an iSCSI response with a matching Initiator Target Tag. The response is
1664 * split into several segments, as requested by the caller-provided buffer specification.
1665 * Remember that the response can be split into several PDUs by the sender, so make
1666 * sure that all parts are collected and processed appropriately by the caller.
1667 *
1668 * @returns VBOX status
1669 * @param pImage The iSCSI connection state to be used.
1670 * @param paRes Pointer to array of iSCSI response sections.
1671 * @param cnRes Number of valid iSCSI response sections in the array.
1672 */
1673static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes)
1674{
1675 int rc = VINF_SUCCESS;
1676 uint32_t i;
1677 ISCSIRES aResBuf;
1678
1679 for (i = 0; i < pImage->cISCSIRetries; i++)
1680 {
1681 aResBuf.pvSeg = pImage->pvRecvPDUBuf;
1682 aResBuf.cbSeg = pImage->cbRecvPDUBuf;
1683 rc = iscsiTransportRead(pImage, &aResBuf, 1);
1684 if (RT_FAILURE(rc))
1685 {
1686 if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
1687 {
1688 /* No point in reestablishing the connection for a logout */
1689 if (pImage->state == ISCSISTATE_IN_LOGOUT)
1690 break;
1691 /* Connection broken while waiting for a response - wait a while and
1692 * try to restart by re-sending the original request (if any).
1693 * This also handles the connection reestablishment (login etc.). */
1694 RTThreadSleep(500);
1695 if (pImage->state != ISCSISTATE_IN_LOGIN)
1696 {
1697 /* Attempt to re-login when a connection fails, but only when not
1698 * currently logging in. */
1699 rc = iscsiAttach(pImage);
1700 if (RT_FAILURE(rc))
1701 break;
1702 }
1703 if (pImage->paCurrReq != NULL)
1704 {
1705 rc = iscsiSendPDU(pImage, pImage->paCurrReq, pImage->cnCurrReq);
1706 if (RT_FAILURE(rc))
1707 break;
1708 }
1709 }
1710 else
1711 {
1712 /* Signal other errors (VERR_BUFFER_OVERFLOW etc.) to the caller. */
1713 break;
1714 }
1715 }
1716 else
1717 {
1718 ISCSIOPCODE cmd;
1719 const uint32_t *pcvResSeg = (const uint32_t *)aResBuf.pvSeg;
1720
1721 /* Check whether the received PDU is valid, and update the internal state of
1722 * the iSCSI connection/session. */
1723 rc = drvISCSIValidatePDU(&aResBuf, 1);
1724 if (RT_FAILURE(rc))
1725 continue;
1726 cmd = (ISCSIOPCODE)(RT_N2H_U32(pcvResSeg[0]) & ISCSIOP_MASK);
1727 switch (cmd)
1728 {
1729 case ISCSIOP_SCSI_RES:
1730 case ISCSIOP_SCSI_TASKMGMT_RES:
1731 case ISCSIOP_SCSI_DATA_IN:
1732 case ISCSIOP_R2T:
1733 case ISCSIOP_ASYN_MSG:
1734 case ISCSIOP_TEXT_RES:
1735 case ISCSIOP_LOGIN_RES:
1736 case ISCSIOP_LOGOUT_RES:
1737 case ISCSIOP_REJECT:
1738 case ISCSIOP_NOP_IN:
1739 if (serial_number_less(pImage->MaxCmdSN, RT_N2H_U32(pcvResSeg[8])))
1740 pImage->MaxCmdSN = RT_N2H_U32(pcvResSeg[8]);
1741 if (serial_number_less(pImage->ExpCmdSN, RT_N2H_U32(pcvResSeg[7])))
1742 pImage->ExpCmdSN = RT_N2H_U32(pcvResSeg[7]);
1743 break;
1744 default:
1745 rc = VERR_PARSE_ERROR;
1746 }
1747 if (RT_FAILURE(rc))
1748 continue;
1749 if ( !pImage->FirstRecvPDU
1750 && (cmd != ISCSIOP_SCSI_DATA_IN || (RT_N2H_U32(pcvResSeg[0]) & ISCSI_STATUS_BIT)))
1751 {
1752 if (pImage->ExpStatSN == RT_N2H_U32(pcvResSeg[6]))
1753 {
1754 /* StatSN counter is not advanced on R2T and on a target SN update NOP-In. */
1755 if ( (cmd != ISCSIOP_R2T)
1756 && ((cmd != ISCSIOP_NOP_IN) || (RT_N2H_U32(pcvResSeg[4]) != ISCSI_TASK_TAG_RSVD)))
1757 pImage->ExpStatSN++;
1758 }
1759 else
1760 {
1761 rc = VERR_PARSE_ERROR;
1762 continue;
1763 }
1764 }
1765 /* Finally check whether the received PDU matches what the caller wants. */
1766 if (itt == pcvResSeg[4])
1767 {
1768 /* Copy received PDU (one segment) to caller-provided buffers. */
1769 uint32_t j;
1770 size_t cbSeg;
1771 const uint8_t *pSrc;
1772
1773 pSrc = (const uint8_t *)aResBuf.pvSeg;
1774 cbSeg = aResBuf.cbSeg;
1775 for (j = 0; j < cnRes; j++)
1776 {
1777 if (cbSeg > paRes[j].cbSeg)
1778 {
1779 memcpy(paRes[j].pvSeg, pSrc, paRes[i].cbSeg);
1780 pSrc += paRes[j].cbSeg;
1781 cbSeg -= paRes[j].cbSeg;
1782 }
1783 else
1784 {
1785 memcpy(paRes[j].pvSeg, pSrc, cbSeg);
1786 paRes[j].cbSeg = cbSeg;
1787 cbSeg = 0;
1788 break;
1789 }
1790 }
1791 if (cbSeg != 0)
1792 {
1793 rc = VERR_BUFFER_OVERFLOW;
1794 break;
1795 }
1796 for (j++; j < cnRes; j++)
1797 paRes[j].cbSeg = 0;
1798 break;
1799 }
1800 else if ( cmd == ISCSIOP_NOP_IN
1801 && RT_N2H_U32(pcvResSeg[5]) != ISCSI_TASK_TAG_RSVD)
1802 {
1803 uint32_t cnISCSIReq;
1804 ISCSIREQ aISCSIReq[4];
1805 uint32_t aReqBHS[12];
1806
1807 aReqBHS[0] = RT_H2N_U32(ISCSI_IMMEDIATE_DELIVERY_BIT | ISCSI_FINAL_BIT | ISCSIOP_NOP_OUT);
1808 aReqBHS[1] = RT_H2N_U32(0); /* TotalAHSLength=0,DataSementLength=0 */
1809 aReqBHS[2] = pcvResSeg[2]; /* copy LUN from NOP-In */
1810 aReqBHS[3] = pcvResSeg[3]; /* copy LUN from NOP-In */
1811 aReqBHS[4] = RT_H2N_U32(ISCSI_TASK_TAG_RSVD); /* ITT, reply */
1812 aReqBHS[5] = pcvResSeg[5]; /* copy TTT from NOP-In */
1813 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
1814 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
1815 aReqBHS[8] = 0; /* reserved */
1816 aReqBHS[9] = 0; /* reserved */
1817 aReqBHS[10] = 0; /* reserved */
1818 aReqBHS[11] = 0; /* reserved */
1819
1820 cnISCSIReq = 0;
1821 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
1822 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
1823 cnISCSIReq++;
1824
1825 iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq);
1826 }
1827 }
1828 }
1829 return rc;
1830}
1831
1832
1833/**
1834 * Check the static (not dependent on the connection/session state) validity of an iSCSI response PDU.
1835 *
1836 * @returns VBOX status
1837 * @param paRes Pointer to array of iSCSI response sections.
1838 * @param cnRes Number of valid iSCSI response sections in the array.
1839 */
1840static int drvISCSIValidatePDU(PISCSIRES paRes, uint32_t cnRes)
1841{
1842 const uint32_t *pcrgResBHS;
1843 uint32_t hw0;
1844 Assert(cnRes >= 1);
1845 Assert(paRes[0].cbSeg >= ISCSI_BHS_SIZE);
1846
1847 pcrgResBHS = (const uint32_t *)(paRes[0].pvSeg);
1848 hw0 = RT_N2H_U32(pcrgResBHS[0]);
1849 switch (hw0 & ISCSIOP_MASK)
1850 {
1851 case ISCSIOP_NOP_IN:
1852 /* NOP-In responses must not be split into several PDUs nor it may contain
1853 * ping data for target-initiated pings nor may both task tags be valid task tags. */
1854 if ( (hw0 & ISCSI_FINAL_BIT) == 0
1855 || ( RT_N2H_U32(pcrgResBHS[4]) == ISCSI_TASK_TAG_RSVD
1856 && RT_N2H_U32(pcrgResBHS[1]) != 0)
1857 || ( RT_N2H_U32(pcrgResBHS[4]) != ISCSI_TASK_TAG_RSVD
1858 && RT_N2H_U32(pcrgResBHS[5]) != ISCSI_TASK_TAG_RSVD))
1859 return VERR_PARSE_ERROR;
1860 break;
1861 case ISCSIOP_SCSI_RES:
1862 /* SCSI responses must not be split into several PDUs nor must the residual
1863 * bits be contradicting each other nor may the residual bits be set for PDUs
1864 * containing anything else but a completed command response. Underflow
1865 * is no reason for declaring a PDU as invalid, as the target may choose
1866 * to return less data than we assume to get. */
1867 if ( (hw0 & ISCSI_FINAL_BIT) == 0
1868 || ((hw0 & ISCSI_BI_READ_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_BI_READ_RESIDUAL_UNFL_BIT))
1869 || ((hw0 & ISCSI_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_RESIDUAL_UNFL_BIT))
1870 || ( ((hw0 & ISCSI_SCSI_RESPONSE_MASK) == 0)
1871 && ((hw0 & ISCSI_SCSI_STATUS_MASK) == SCSI_STATUS_OK)
1872 && (hw0 & ( ISCSI_BI_READ_RESIDUAL_OVFL_BIT | ISCSI_BI_READ_RESIDUAL_UNFL_BIT
1873 | ISCSI_RESIDUAL_OVFL_BIT))))
1874 return VERR_PARSE_ERROR;
1875 break;
1876 case ISCSIOP_LOGIN_RES:
1877 /* Login responses must not contain contradicting transit and continue bits. */
1878 if ((hw0 & ISCSI_CONTINUE_BIT) && ((hw0 & ISCSI_TRANSIT_BIT) != 0))
1879 return VERR_PARSE_ERROR;
1880 break;
1881 case ISCSIOP_TEXT_RES:
1882 /* Text responses must not contain contradicting final and continue bits nor
1883 * may the final bit be set for PDUs containing a target transfer tag other than
1884 * the reserved transfer tag (and vice versa). */
1885 if ( (((hw0 & ISCSI_CONTINUE_BIT) && (hw0 & ISCSI_FINAL_BIT) != 0))
1886 || (((hw0 & ISCSI_FINAL_BIT) && (RT_N2H_U32(pcrgResBHS[5]) != ISCSI_TASK_TAG_RSVD)))
1887 || (((hw0 & ISCSI_FINAL_BIT) == 0) && (RT_N2H_U32(pcrgResBHS[5]) == ISCSI_TASK_TAG_RSVD)))
1888 return VERR_PARSE_ERROR;
1889 break;
1890 case ISCSIOP_SCSI_DATA_IN:
1891 /* SCSI Data-in responses must not contain contradicting residual bits when
1892 * status bit is set. */
1893 if ((hw0 & ISCSI_STATUS_BIT) && (hw0 & ISCSI_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_RESIDUAL_UNFL_BIT))
1894 return VERR_PARSE_ERROR;
1895 break;
1896 case ISCSIOP_LOGOUT_RES:
1897 /* Logout responses must not have the final bit unset and may not contain any
1898 * data or additional header segments. */
1899 if ( ((hw0 & ISCSI_FINAL_BIT) == 0)
1900 || (RT_N2H_U32(pcrgResBHS[1]) != 0))
1901 return VERR_PARSE_ERROR;
1902 break;
1903 case ISCSIOP_ASYN_MSG:
1904 /* Asynchronous Messages must not have the final bit unser and may not contain
1905 * an initiator task tag. */
1906 if ( ((hw0 & ISCSI_FINAL_BIT) == 0)
1907 || (RT_N2H_U32(pcrgResBHS[4]) != ISCSI_TASK_TAG_RSVD))
1908 return VERR_PARSE_ERROR;
1909 break;
1910 case ISCSIOP_SCSI_TASKMGMT_RES:
1911 case ISCSIOP_R2T:
1912 case ISCSIOP_REJECT:
1913 default:
1914 /* Do some logging, ignore PDU. */
1915 LogFlow(("drvISCSIValidatePDU: ignore unhandled PDU, first word %#08x\n", RT_N2H_U32(pcrgResBHS[0])));
1916 return VERR_PARSE_ERROR;
1917 }
1918 /* A target must not send PDUs with MaxCmdSN less than ExpCmdSN-1. */
1919
1920 if (serial_number_less(RT_N2H_U32(pcrgResBHS[8]), RT_N2H_U32(pcrgResBHS[7])-1))
1921 return VERR_PARSE_ERROR;
1922
1923 return VINF_SUCCESS;
1924}
1925
1926
1927/**
1928 * Appends a key-value pair to the buffer. Normal ASCII strings (cbValue == 0) and large binary values
1929 * of a given length (cbValue > 0) are directly supported. Other value types must be converted to ASCII
1930 * by the caller. Strings must be in UTF-8 encoding.
1931 *
1932 * @returns VBOX status
1933 * @param pbBuf Pointer to the key-value buffer.
1934 * @param cbBuf Length of the key-value buffer.
1935 * @param pcbBufCurr Currently used portion of the key-value buffer.
1936 * @param pszKey Pointer to a string containing the key.
1937 * @param pszValue Pointer to either a string containing the value or to a large binary value.
1938 * @param cbValue Length of the binary value if applicable.
1939 */
1940static int iscsiTextAddKeyValue(uint8_t *pbBuf, size_t cbBuf, size_t *pcbBufCurr, const char *pcszKey,
1941 const char *pcszValue, size_t cbValue)
1942{
1943 size_t cbBufTmp = *pcbBufCurr;
1944 size_t cbKey = strlen(pcszKey);
1945 size_t cbValueEnc;
1946 uint8_t *pbCurr;
1947
1948 if (cbValue == 0)
1949 cbValueEnc = strlen(pcszValue);
1950 else
1951 cbValueEnc = cbValue * 2 + 2; /* 2 hex bytes per byte, 2 bytes prefix */
1952
1953 if (cbBuf < cbBufTmp + cbKey + 1 + cbValueEnc + 1)
1954 {
1955 /* Buffer would overflow, signal error. */
1956 return VERR_BUFFER_OVERFLOW;
1957 }
1958
1959 /*
1960 * Append a key=value pair (zero terminated string) to the end of the buffer.
1961 */
1962 pbCurr = pbBuf + cbBufTmp;
1963 memcpy(pbCurr, pcszKey, cbKey);
1964 pbCurr += cbKey;
1965 *pbCurr++ = '=';
1966 if (cbValue == 0)
1967 {
1968 memcpy(pbCurr, pcszValue, cbValueEnc);
1969 pbCurr += cbValueEnc;
1970 }
1971 else
1972 {
1973 *pbCurr++ = '0';
1974 *pbCurr++ = 'x';
1975 for (uint32_t i = 0; i < cbValue; i++)
1976 {
1977 uint8_t b;
1978 b = pcszValue[i];
1979 *pbCurr++ = NUM_2_HEX(b >> 4);
1980 *pbCurr++ = NUM_2_HEX(b & 0xf);
1981 }
1982 }
1983 *pbCurr = '\0';
1984 *pcbBufCurr = cbBufTmp + cbKey + 1 + cbValueEnc + 1;
1985
1986 return VINF_SUCCESS;
1987}
1988
1989
1990/**
1991 * Retrieve the value for a given key from the key=value buffer.
1992 *
1993 * @returns VBOX status.
1994 * @param pbBuf Buffer containing key=value pairs.
1995 * @param cbBuf Length of buffer with key=value pairs.
1996 * @param pszKey Pointer to key for which to retrieve the value.
1997 * @param ppszValue Pointer to value string pointer.
1998 */
1999static int iscsiTextGetKeyValue(const uint8_t *pbBuf, size_t cbBuf, const char *pcszKey, const char **ppcszValue)
2000{
2001 size_t cbKey = strlen(pcszKey);
2002
2003 while (cbBuf != 0)
2004 {
2005 size_t cbKeyValNull = strlen((const char *)pbBuf) + 1;
2006
2007 if (strncmp(pcszKey, (const char *)pbBuf, cbKey) == 0 && pbBuf[cbKey] == '=')
2008 {
2009 *ppcszValue = (const char *)(pbBuf + cbKey + 1);
2010 return VINF_SUCCESS;
2011 }
2012 pbBuf += cbKeyValNull;
2013 cbBuf -= cbKeyValNull;
2014 }
2015 return VERR_INVALID_NAME;
2016}
2017
2018
2019/**
2020 * Convert a long-binary value from a value string to the binary representation.
2021 *
2022 * @returns VBOX status
2023 * @param pszValue Pointer to a string containing the textual value representation.
2024 * @param pbValue Pointer to the value buffer for the binary value.
2025 * @param pcbValue In: length of value buffer, out: actual length of binary value.
2026 */
2027static int iscsiStrToBinary(const char *pcszValue, uint8_t *pbValue, size_t *pcbValue)
2028{
2029 size_t cbValue = *pcbValue;
2030 char c1, c2, c3, c4;
2031 Assert(cbValue >= 1);
2032
2033 if (strlen(pcszValue) < 3)
2034 return VERR_PARSE_ERROR;
2035 if (*pcszValue++ != '0')
2036 return VERR_PARSE_ERROR;
2037 switch (*pcszValue++)
2038 {
2039 case 'x':
2040 case 'X':
2041 if (strlen(pcszValue) & 1)
2042 {
2043 c1 = *pcszValue++;
2044 *pbValue++ = HEX_2_NUM(c1);
2045 cbValue--;
2046 }
2047 while (*pcszValue != '\0')
2048 {
2049 if (cbValue == 0)
2050 return VERR_BUFFER_OVERFLOW;
2051 c1 = *pcszValue++;
2052 if ((c1 < '0' || c1 > '9') && (c1 < 'a' || c1 > 'f') && (c1 < 'A' || c1 > 'F'))
2053 return VERR_PARSE_ERROR;
2054 c2 = *pcszValue++;
2055 if ((c2 < '0' || c2 > '9') && (c2 < 'a' || c2 > 'f') && (c2 < 'A' || c2 > 'F'))
2056 return VERR_PARSE_ERROR;
2057 *pbValue++ = (HEX_2_NUM(c1) << 4) | HEX_2_NUM(c2);
2058 cbValue--;
2059 }
2060 *pcbValue -= cbValue;
2061 break;
2062 case 'b':
2063 case 'B':
2064 if ((strlen(pcszValue) & 3) != 0)
2065 return VERR_PARSE_ERROR;
2066 while (*pcszValue != '\0')
2067 {
2068 uint32_t temp;
2069 if (cbValue == 0)
2070 return VERR_BUFFER_OVERFLOW;
2071 c1 = *pcszValue++;
2072 if ((c1 < 'A' || c1 > 'Z') && (c1 < 'a' || c1 >'z') && (c1 < '0' || c1 > '9') && (c1 != '+') && (c1 != '/'))
2073 return VERR_PARSE_ERROR;
2074 c2 = *pcszValue++;
2075 if ((c2 < 'A' || c2 > 'Z') && (c2 < 'a' || c2 >'z') && (c2 < '0' || c2 > '9') && (c2 != '+') && (c2 != '/'))
2076 return VERR_PARSE_ERROR;
2077 c3 = *pcszValue++;
2078 if ((c3 < 'A' || c3 > 'Z') && (c3 < 'a' || c3 >'z') && (c3 < '0' || c3 > '9') && (c3 != '+') && (c3 != '/') && (c3 != '='))
2079 return VERR_PARSE_ERROR;
2080 c4 = *pcszValue++;
2081 if ( (c3 == '=' && c4 != '=')
2082 || ((c4 < 'A' || c4 > 'Z') && (c4 < 'a' || c4 >'z') && (c4 < '0' || c4 > '9') && (c4 != '+') && (c4 != '/') && (c4 != '=')))
2083 return VERR_PARSE_ERROR;
2084 temp = (B64_2_NUM(c1) << 18) | (B64_2_NUM(c2) << 12);
2085 if (c3 == '=') {
2086 if (*pcszValue != '\0')
2087 return VERR_PARSE_ERROR;
2088 *pbValue++ = temp >> 16;
2089 cbValue--;
2090 } else {
2091 temp |= B64_2_NUM(c3) << 6;
2092 if (c4 == '=') {
2093 if (*pcszValue != '\0')
2094 return VERR_PARSE_ERROR;
2095 if (cbValue < 2)
2096 return VERR_BUFFER_OVERFLOW;
2097 *pbValue++ = temp >> 16;
2098 *pbValue++ = (temp >> 8) & 0xff;
2099 cbValue -= 2;
2100 }
2101 else
2102 {
2103 temp |= B64_2_NUM(c4);
2104 if (cbValue < 3)
2105 return VERR_BUFFER_OVERFLOW;
2106 *pbValue++ = temp >> 16;
2107 *pbValue++ = (temp >> 8) & 0xff;
2108 *pbValue++ = temp & 0xff;
2109 cbValue -= 3;
2110 }
2111 }
2112 }
2113 *pcbValue -= cbValue;
2114 break;
2115 default:
2116 return VERR_PARSE_ERROR;
2117 }
2118 return VINF_SUCCESS;
2119}
2120
2121
2122/**
2123 * Retrieve the relevant parameter values and update the initiator state.
2124 *
2125 * @returns VBOX status.
2126 * @param pImage Current iSCSI initiator state.
2127 * @param pbBuf Buffer containing key=value pairs.
2128 * @param cbBuf Length of buffer with key=value pairs.
2129 */
2130static int iscsiUpdateParameters(PISCSIIMAGE pImage, const uint8_t *pbBuf, size_t cbBuf)
2131{
2132 int rc;
2133 const char *pcszMaxRecvDataSegmentLength = NULL;
2134 const char *pcszMaxBurstLength = NULL;
2135 const char *pcszFirstBurstLength = NULL;
2136 rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "MaxRecvDataSegmentLength", &pcszMaxRecvDataSegmentLength);
2137 if (rc == VERR_INVALID_NAME)
2138 rc = VINF_SUCCESS;
2139 if (RT_FAILURE(rc))
2140 return VERR_PARSE_ERROR;
2141 rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "MaxBurstLength", &pcszMaxBurstLength);
2142 if (rc == VERR_INVALID_NAME)
2143 rc = VINF_SUCCESS;
2144 if (RT_FAILURE(rc))
2145 return VERR_PARSE_ERROR;
2146 rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "FirstBurstLength", &pcszFirstBurstLength);
2147 if (rc == VERR_INVALID_NAME)
2148 rc = VINF_SUCCESS;
2149 if (RT_FAILURE(rc))
2150 return VERR_PARSE_ERROR;
2151 if (pcszMaxRecvDataSegmentLength)
2152 {
2153 uint32_t cb = pImage->cbSendDataLength;
2154 rc = RTStrToUInt32Full(pcszMaxRecvDataSegmentLength, 0, &cb);
2155 AssertRC(rc);
2156 pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
2157 }
2158 if (pcszMaxBurstLength)
2159 {
2160 uint32_t cb = pImage->cbSendDataLength;
2161 rc = RTStrToUInt32Full(pcszMaxBurstLength, 0, &cb);
2162 AssertRC(rc);
2163 pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
2164 }
2165 if (pcszFirstBurstLength)
2166 {
2167 uint32_t cb = pImage->cbSendDataLength;
2168 rc = RTStrToUInt32Full(pcszFirstBurstLength, 0, &cb);
2169 AssertRC(rc);
2170 pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
2171 }
2172 return VINF_SUCCESS;
2173}
2174
2175
2176static bool serial_number_less(uint32_t s1, uint32_t s2)
2177{
2178 return (s1 < s2 && s2 - s1 < 0x80000000) || (s1 > s2 && s1 - s2 > 0x80000000);
2179}
2180
2181
2182#ifdef IMPLEMENT_TARGET_AUTH
2183static void chap_md5_generate_challenge(uint8_t *pbChallenge, size_t *pcbChallenge)
2184{
2185 uint8_t cbChallenge;
2186
2187 cbChallenge = RTrand_U8(CHAP_MD5_CHALLENGE_MIN, CHAP_MD5_CHALLENGE_MAX);
2188 RTrand_bytes(pbChallenge, cbChallenge);
2189 *pcbChallenge = cbChallenge;
2190}
2191#endif
2192
2193
2194static void chap_md5_compute_response(uint8_t *pbResponse, uint8_t id, const uint8_t *pbChallenge, size_t cbChallenge,
2195 const uint8_t *pbSecret, size_t cbSecret)
2196{
2197 RTMD5CONTEXT ctx;
2198 uint8_t bId;
2199
2200 bId = id;
2201 RTMd5Init(&ctx);
2202 RTMd5Update(&ctx, &bId, 1);
2203 RTMd5Update(&ctx, pbSecret, cbSecret);
2204 RTMd5Update(&ctx, pbChallenge, cbChallenge);
2205 RTMd5Final(pbResponse, &ctx);
2206}
2207
2208/**
2209 * Internal. Free all allocated space for representing an image, and optionally
2210 * delete the image from disk.
2211 */
2212static void iscsiFreeImage(PISCSIIMAGE pImage, bool fDelete)
2213{
2214 Assert(pImage);
2215 Assert(!fDelete); /* This MUST be false, the flag isn't supported. */
2216
2217 if (pImage->Mutex != NIL_RTSEMMUTEX)
2218 {
2219 /* Detaching only makes sense when the mutex is there. Otherwise the
2220 * failure happened long before we could attach to the target. */
2221 iscsiDetach(pImage);
2222 RTSemMutexDestroy(pImage->Mutex);
2223 pImage->Mutex = NIL_RTSEMMUTEX;
2224 }
2225 if (pImage->pszTargetName)
2226 {
2227 RTMemFree(pImage->pszTargetName);
2228 pImage->pszTargetName = NULL;
2229 }
2230 if (pImage->pszInitiatorName)
2231 {
2232 RTMemFree(pImage->pszInitiatorName);
2233 pImage->pszInitiatorName = NULL;
2234 }
2235 if (pImage->pszInitiatorUsername)
2236 {
2237 RTMemFree(pImage->pszInitiatorUsername);
2238 pImage->pszInitiatorUsername = NULL;
2239 }
2240 if (pImage->pbInitiatorSecret)
2241 {
2242 RTMemFree(pImage->pbInitiatorSecret);
2243 pImage->pbInitiatorSecret = NULL;
2244 }
2245 if (pImage->pszTargetUsername)
2246 {
2247 RTMemFree(pImage->pszTargetUsername);
2248 pImage->pszTargetUsername = NULL;
2249 }
2250 if (pImage->pbTargetSecret)
2251 {
2252 RTMemFree(pImage->pbTargetSecret);
2253 pImage->pbTargetSecret = NULL;
2254 }
2255 if (pImage->pvRecvPDUBuf)
2256 {
2257 RTMemFree(pImage->pvRecvPDUBuf);
2258 pImage->pvRecvPDUBuf = NULL;
2259 }
2260}
2261
2262/**
2263 * Internal: Open an image, constructing all necessary data structures.
2264 */
2265static int iscsiOpenImage(PISCSIIMAGE pImage, unsigned uOpenFlags)
2266{
2267 int rc;
2268 char *pszLUN = NULL, *pszLUNInitial = NULL;
2269 bool fLunEncoded = false;
2270 uint32_t uWriteSplitDef = 0;
2271 uint32_t uTimeoutDef = 0;
2272 uint64_t uHostIPTmp = 0;
2273 bool fHostIPDef = 0;
2274 rc = RTStrToUInt32Full(s_iscsiConfigDefaultWriteSplit, 0, &uWriteSplitDef);
2275 AssertRC(rc);
2276 rc = RTStrToUInt32Full(s_iscsiConfigDefaultTimeout, 0, &uTimeoutDef);
2277 AssertRC(rc);
2278 rc = RTStrToUInt64Full(s_iscsiConfigDefaultHostIPStack, 0, &uHostIPTmp);
2279 AssertRC(rc);
2280 fHostIPDef = !!uHostIPTmp;
2281
2282 pImage->uOpenFlags = uOpenFlags;
2283
2284 /* Get error signalling interface. */
2285 pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
2286 if (pImage->pInterfaceError)
2287 pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
2288
2289 /* Get TCP network stack interface. */
2290 pImage->pInterfaceNet = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_TCPNET);
2291 if (pImage->pInterfaceNet)
2292 pImage->pInterfaceNetCallbacks = VDGetInterfaceTcpNet(pImage->pInterfaceNet);
2293 else
2294 {
2295 rc = iscsiError(pImage, VERR_VD_ISCSI_UNKNOWN_INTERFACE,
2296 RT_SRC_POS, N_("iSCSI: TCP network stack interface missing"));
2297 goto out;
2298 }
2299
2300 /* Get configuration interface. */
2301 pImage->pInterfaceConfig = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_CONFIG);
2302 if (pImage->pInterfaceConfig)
2303 pImage->pInterfaceConfigCallbacks = VDGetInterfaceConfig(pImage->pInterfaceConfig);
2304 else
2305 {
2306 rc = iscsiError(pImage, VERR_VD_ISCSI_UNKNOWN_INTERFACE,
2307 RT_SRC_POS, N_("iSCSI: configuration interface missing"));
2308 goto out;
2309 }
2310
2311 pImage->ISID = 0x800000000000ULL | 0x001234560000ULL | (0x00000000cba0ULL + ASMAtomicIncU32(&s_u32iscsiID));
2312 pImage->cISCSIRetries = 10;
2313 pImage->state = ISCSISTATE_FREE;
2314 pImage->pvRecvPDUBuf = RTMemAlloc(ISCSI_RECV_PDU_BUFFER_SIZE);
2315 pImage->cbRecvPDUBuf = ISCSI_RECV_PDU_BUFFER_SIZE;
2316 if (pImage->pvRecvPDUBuf == NULL)
2317 {
2318 rc = VERR_NO_MEMORY;
2319 goto out;
2320 }
2321 pImage->Mutex = NIL_RTSEMMUTEX;
2322 rc = RTSemMutexCreate(&pImage->Mutex);
2323 if (RT_FAILURE(rc))
2324 goto out;
2325
2326 /* Validate configuration, detect unknown keys. */
2327 if (!VDCFGAreKeysValid(pImage->pInterfaceConfigCallbacks,
2328 pImage->pInterfaceConfig->pvUser,
2329 "TargetName\0InitiatorName\0LUN\0TargetAddress\0InitiatorUsername\0InitiatorSecret\0TargetUsername\0TargetSecret\0WriteSplit\0Timeout\0HostIPStack\0"))
2330 {
2331 rc = iscsiError(pImage, VERR_VD_ISCSI_UNKNOWN_CFG_VALUES, RT_SRC_POS, N_("iSCSI: configuration error: unknown configuration keys present"));
2332 goto out;
2333 }
2334
2335 /* Query the iSCSI upper level configuration. */
2336 rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
2337 pImage->pInterfaceConfig->pvUser,
2338 "TargetName", &pImage->pszTargetName);
2339 if (RT_FAILURE(rc))
2340 {
2341 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetName as string"));
2342 goto out;
2343 }
2344 rc = VDCFGQueryStringAllocDef(pImage->pInterfaceConfigCallbacks,
2345 pImage->pInterfaceConfig->pvUser,
2346 "InitiatorName", &pImage->pszInitiatorName,
2347 s_iscsiConfigDefaultInitiatorName);
2348 if (RT_FAILURE(rc))
2349 {
2350 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorName as string"));
2351 goto out;
2352 }
2353 rc = VDCFGQueryStringAllocDef(pImage->pInterfaceConfigCallbacks,
2354 pImage->pInterfaceConfig->pvUser,
2355 "LUN", &pszLUN, s_iscsiConfigDefaultLUN);
2356 if (RT_FAILURE(rc))
2357 {
2358 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read LUN as string"));
2359 goto out;
2360 }
2361 pszLUNInitial = pszLUN;
2362 if (!strncmp(pszLUN, "enc", 3))
2363 {
2364 fLunEncoded = true;
2365 pszLUN += 3;
2366 }
2367 rc = RTStrToUInt64Full(pszLUN, 0, &pImage->LUN);
2368 if (RT_FAILURE(rc))
2369 {
2370 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to convert LUN to integer"));
2371 goto out;
2372 }
2373 if (!fLunEncoded)
2374 {
2375 if (pImage->LUN <= 255)
2376 {
2377 pImage->LUN = pImage->LUN << 48; /* uses peripheral device addressing method */
2378 }
2379 else if (pImage->LUN <= 16383)
2380 {
2381 pImage->LUN = (pImage->LUN << 48) | RT_BIT_64(62); /* uses flat space addressing method */
2382 }
2383 else
2384 {
2385 rc = VERR_OUT_OF_RANGE;
2386 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: LUN number out of range (0-16383)"));
2387 goto out;
2388 }
2389 }
2390 rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
2391 pImage->pInterfaceConfig->pvUser,
2392 "TargetAddress", &pImage->pszTargetAddress);
2393 if (RT_FAILURE(rc))
2394 {
2395 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetAddress as string"));
2396 goto out;
2397 }
2398 pImage->pszInitiatorUsername = NULL;
2399 rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
2400 pImage->pInterfaceConfig->pvUser,
2401 "InitiatorUsername",
2402 &pImage->pszInitiatorUsername);
2403 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
2404 rc = VINF_SUCCESS;
2405 if (RT_FAILURE(rc))
2406 {
2407 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorUsername as string"));
2408 goto out;
2409 }
2410 pImage->pbInitiatorSecret = NULL;
2411 pImage->cbInitiatorSecret = 0;
2412 rc = VDCFGQueryBytesAlloc(pImage->pInterfaceConfigCallbacks,
2413 pImage->pInterfaceConfig->pvUser,
2414 "InitiatorSecret",
2415 (void **)&pImage->pbInitiatorSecret,
2416 &pImage->cbInitiatorSecret);
2417 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
2418 rc = VINF_SUCCESS;
2419 if (RT_FAILURE(rc))
2420 {
2421 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorSecret as byte string"));
2422 goto out;
2423 }
2424 pImage->pszTargetUsername = NULL;
2425 rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
2426 pImage->pInterfaceConfig->pvUser,
2427 "TargetUsername",
2428 &pImage->pszTargetUsername);
2429 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
2430 rc = VINF_SUCCESS;
2431 if (RT_FAILURE(rc))
2432 {
2433 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetUsername as string"));
2434 goto out;
2435 }
2436 pImage->pbTargetSecret = NULL;
2437 pImage->cbTargetSecret = 0;
2438 rc = VDCFGQueryBytesAlloc(pImage->pInterfaceConfigCallbacks,
2439 pImage->pInterfaceConfig->pvUser,
2440 "TargetSecret", (void **)&pImage->pbTargetSecret,
2441 &pImage->cbTargetSecret);
2442 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
2443 rc = VINF_SUCCESS;
2444 if (RT_FAILURE(rc))
2445 {
2446 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetSecret as byte string"));
2447 goto out;
2448 }
2449 rc = VDCFGQueryU32Def(pImage->pInterfaceConfigCallbacks,
2450 pImage->pInterfaceConfig->pvUser,
2451 "WriteSplit", &pImage->cbWriteSplit,
2452 uWriteSplitDef);
2453 if (RT_FAILURE(rc))
2454 {
2455 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read WriteSplit as U32"));
2456 goto out;
2457 }
2458
2459 pImage->pszHostname = NULL;
2460 pImage->uPort = 0;
2461 pImage->Socket = NIL_RTSOCKET;
2462 /* Query the iSCSI lower level configuration. */
2463 rc = VDCFGQueryU32Def(pImage->pInterfaceConfigCallbacks,
2464 pImage->pInterfaceConfig->pvUser,
2465 "Timeout", &pImage->uReadTimeout,
2466 uTimeoutDef);
2467 if (RT_FAILURE(rc))
2468 {
2469 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read Timeout as U32"));
2470 goto out;
2471 }
2472 rc = VDCFGQueryBoolDef(pImage->pInterfaceConfigCallbacks,
2473 pImage->pInterfaceConfig->pvUser,
2474 "HostIPStack", &pImage->fHostIP,
2475 fHostIPDef);
2476 if (RT_FAILURE(rc))
2477 {
2478 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read HostIPStack as boolean"));
2479 goto out;
2480 }
2481
2482 /* Don't actually establish iSCSI transport connection if this is just an
2483 * open to query the image information and the host IP stack isn't used.
2484 * Even trying is rather useless, as in this context the InTnet IP stack
2485 * isn't present. Returning dummies is the best possible result anyway. */
2486 if ((uOpenFlags & VD_OPEN_FLAGS_INFO) && !pImage->fHostIP)
2487 {
2488 LogFunc(("Not opening the transport connection as IntNet IP stack is not available. Will return dummies\n"));
2489 goto out;
2490 }
2491
2492 /*
2493 * Establish the iSCSI transport connection.
2494 */
2495 rc = iscsiTransportOpen(pImage);
2496 if (RT_SUCCESS(rc))
2497 rc = iscsiAttach(pImage);
2498
2499 if (RT_FAILURE(rc))
2500 {
2501 LogRel(("iSCSI: could not open target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
2502 goto out;
2503 }
2504 LogFlowFunc(("target '%s' opened successfully\n", pImage->pszTargetName));
2505
2506 SCSIREQ sr;
2507 uint8_t sense[32];
2508 uint8_t data8[8];
2509 uint8_t data12[12];
2510
2511 /*
2512 * Inquire available LUNs - purely dummy request.
2513 */
2514 uint8_t cdb_rlun[12];
2515 uint8_t rlundata[16];
2516 cdb_rlun[0] = SCSI_REPORT_LUNS;
2517 cdb_rlun[1] = 0; /* reserved */
2518 cdb_rlun[2] = 0; /* reserved */
2519 cdb_rlun[3] = 0; /* reserved */
2520 cdb_rlun[4] = 0; /* reserved */
2521 cdb_rlun[5] = 0; /* reserved */
2522 cdb_rlun[6] = sizeof(rlundata) >> 24;
2523 cdb_rlun[7] = (sizeof(rlundata) >> 16) & 0xff;
2524 cdb_rlun[8] = (sizeof(rlundata) >> 8) & 0xff;
2525 cdb_rlun[9] = sizeof(rlundata) & 0xff;
2526 cdb_rlun[10] = 0; /* reserved */
2527 cdb_rlun[11] = 0; /* control */
2528
2529 sr.enmXfer = SCSIXFER_FROM_TARGET;
2530 sr.cbCmd = sizeof(cdb_rlun);
2531 sr.pvCmd = cdb_rlun;
2532 sr.cbI2TData = 0;
2533 sr.pcvI2TData = NULL;
2534 sr.cbT2IData = sizeof(rlundata);
2535 sr.pvT2IData = rlundata;
2536 sr.cbSense = sizeof(sense);
2537 sr.pvSense = sense;
2538
2539 rc = iscsiCommand(pImage, &sr);
2540 if (RT_FAILURE(rc))
2541 {
2542 LogRel(("iSCSI: Could not get LUN info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
2543 return rc;
2544 }
2545
2546 /*
2547 * Inquire device characteristics - no tapes, scanners etc., please.
2548 */
2549 uint8_t cdb_inq[6];
2550 cdb_inq[0] = SCSI_INQUIRY;
2551 cdb_inq[1] = 0; /* reserved */
2552 cdb_inq[2] = 0; /* reserved */
2553 cdb_inq[3] = 0; /* reserved */
2554 cdb_inq[4] = sizeof(data8);
2555 cdb_inq[5] = 0; /* control */
2556
2557 sr.enmXfer = SCSIXFER_FROM_TARGET;
2558 sr.cbCmd = sizeof(cdb_inq);
2559 sr.pvCmd = cdb_inq;
2560 sr.cbI2TData = 0;
2561 sr.pcvI2TData = NULL;
2562 sr.cbT2IData = sizeof(data8);
2563 sr.pvT2IData = data8;
2564 sr.cbSense = sizeof(sense);
2565 sr.pvSense = sense;
2566
2567 rc = iscsiCommand(pImage, &sr);
2568 if (RT_SUCCESS(rc))
2569 {
2570 if ((data8[0] & SCSI_DEVTYPE_MASK) != SCSI_DEVTYPE_DISK)
2571 {
2572 rc = iscsiError(pImage, VERR_VD_ISCSI_INVALID_TYPE,
2573 RT_SRC_POS, N_("iSCSI: target address %s, target name %s, SCSI LUN %lld reports device type=%u"),
2574 pImage->pszTargetAddress, pImage->pszTargetName,
2575 pImage->LUN, data8[0]);
2576 LogRel(("iSCSI: Unsupported SCSI peripheral device type %d for target %s\n", data8[0] & SCSI_DEVTYPE_MASK, pImage->pszTargetName));
2577 goto out;
2578 }
2579 }
2580 else
2581 {
2582 LogRel(("iSCSI: Could not get INQUIRY info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
2583 goto out;
2584 }
2585
2586 /*
2587 * Query write disable bit in the device specific parameter entry in the
2588 * mode parameter header. Refuse read/write opening of read only disks.
2589 */
2590
2591 uint8_t cdb_ms[6];
2592 uint8_t data4[4];
2593 cdb_ms[0] = SCSI_MODE_SENSE_6;
2594 cdb_ms[1] = 0; /* dbd=0/reserved */
2595 cdb_ms[2] = 0x3f; /* pc=0/page code=0x3f, ask for all pages */
2596 cdb_ms[3] = 0; /* subpage code=0, return everything in page_0 format */
2597 cdb_ms[4] = sizeof(data4); /* allocation length=4 */
2598 cdb_ms[5] = 0; /* control */
2599
2600 sr.enmXfer = SCSIXFER_FROM_TARGET;
2601 sr.cbCmd = sizeof(cdb_ms);
2602 sr.pvCmd = cdb_ms;
2603 sr.cbI2TData = 0;
2604 sr.pcvI2TData = NULL;
2605 sr.cbT2IData = sizeof(data4);
2606 sr.pvT2IData = data4;
2607 sr.cbSense = sizeof(sense);
2608 sr.pvSense = sense;
2609
2610 for (unsigned i = 0; i < 10; i++)
2611 {
2612 rc = iscsiCommand(pImage, &sr);
2613 if ( (RT_SUCCESS(rc) && !sr.cbSense)
2614 || RT_FAILURE(rc))
2615 break;
2616 rc = VERR_INVALID_STATE;
2617 }
2618 if (RT_SUCCESS(rc))
2619 {
2620 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY) && data4[2] & 0x80)
2621 {
2622 rc = VERR_VD_IMAGE_READ_ONLY;
2623 goto out;
2624 }
2625 }
2626 else
2627 {
2628 LogRel(("iSCSI: Could not get MODE SENSE info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
2629 goto out;
2630 }
2631
2632 /*
2633 * Determine sector size and capacity of the volume immediately.
2634 */
2635 uint8_t cdb_cap[16];
2636
2637 RT_ZERO(data12);
2638 RT_ZERO(cdb_cap);
2639 cdb_cap[0] = SCSI_SERVICE_ACTION_IN_16;
2640 cdb_cap[1] = SCSI_SVC_ACTION_IN_READ_CAPACITY_16; /* subcommand */
2641 cdb_cap[10+3] = sizeof(data12); /* allocation length (dword) */
2642
2643 sr.enmXfer = SCSIXFER_FROM_TARGET;
2644 sr.cbCmd = sizeof(cdb_cap);
2645 sr.pvCmd = cdb_cap;
2646 sr.cbI2TData = 0;
2647 sr.pcvI2TData = NULL;
2648 sr.cbT2IData = sizeof(data12);
2649 sr.pvT2IData = data12;
2650 sr.cbSense = sizeof(sense);
2651 sr.pvSense = sense;
2652
2653 rc = iscsiCommand(pImage, &sr);
2654 if ( RT_SUCCESS(rc)
2655 && sr.status == SCSI_STATUS_OK)
2656 {
2657 pImage->cVolume = RT_BE2H_U64(*(uint64_t *)&data12[0]);
2658 pImage->cVolume++;
2659 pImage->cbSector = RT_BE2H_U32(*(uint32_t *)&data12[8]);
2660 pImage->cbSize = pImage->cVolume * pImage->cbSector;
2661 if (pImage->cVolume == 0 || pImage->cbSector != 512 || pImage->cbSize < pImage->cVolume)
2662 {
2663 rc = iscsiError(pImage, VERR_VD_ISCSI_INVALID_TYPE,
2664 RT_SRC_POS, N_("iSCSI: target address %s, target name %s, SCSI LUN %lld reports media sector count=%llu sector size=%u"),
2665 pImage->pszTargetAddress, pImage->pszTargetName,
2666 pImage->LUN, pImage->cVolume, pImage->cbSector);
2667 }
2668 }
2669 else
2670 {
2671 uint8_t cdb_capfb[10];
2672
2673 RT_ZERO(data8);
2674 cdb_capfb[0] = SCSI_READ_CAPACITY;
2675 cdb_capfb[1] = 0; /* reserved */
2676 cdb_capfb[2] = 0; /* reserved */
2677 cdb_capfb[3] = 0; /* reserved */
2678 cdb_capfb[4] = 0; /* reserved */
2679 cdb_capfb[5] = 0; /* reserved */
2680 cdb_capfb[6] = 0; /* reserved */
2681 cdb_capfb[7] = 0; /* reserved */
2682 cdb_capfb[8] = 0; /* reserved */
2683 cdb_capfb[9] = 0; /* control */
2684
2685 sr.enmXfer = SCSIXFER_FROM_TARGET;
2686 sr.cbCmd = sizeof(cdb_capfb);
2687 sr.pvCmd = cdb_capfb;
2688 sr.cbI2TData = 0;
2689 sr.pcvI2TData = NULL;
2690 sr.cbT2IData = sizeof(data8);
2691 sr.pvT2IData = data8;
2692 sr.cbSense = sizeof(sense);
2693 sr.pvSense = sense;
2694
2695 rc = iscsiCommand(pImage, &sr);
2696 if (RT_SUCCESS(rc))
2697 {
2698 pImage->cVolume = (data8[0] << 24) | (data8[1] << 16) | (data8[2] << 8) | data8[3];
2699 pImage->cVolume++;
2700 pImage->cbSector = (data8[4] << 24) | (data8[5] << 16) | (data8[6] << 8) | data8[7];
2701 pImage->cbSize = pImage->cVolume * pImage->cbSector;
2702 if (pImage->cVolume == 0 || pImage->cbSector != 512)
2703 {
2704 rc = iscsiError(pImage, VERR_VD_ISCSI_INVALID_TYPE,
2705 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"),
2706 pImage->pszTargetAddress, pImage->pszTargetName,
2707 pImage->LUN, pImage->cVolume, pImage->cbSector);
2708 }
2709 }
2710 else
2711 {
2712 LogRel(("iSCSI: Could not determine capacity of target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
2713 goto out;
2714 }
2715 }
2716
2717 /*
2718 * Check the read and write cache bits.
2719 * Try to enable the cache if it is disabled.
2720 *
2721 * We already checked that this is a block access device. No need
2722 * to do it again.
2723 */
2724 uint8_t aCachingModePage[32];
2725 uint8_t aCDBModeSense6[6];
2726
2727 memset(aCachingModePage, '\0', sizeof(aCachingModePage));
2728 aCDBModeSense6[0] = SCSI_MODE_SENSE_6;
2729 aCDBModeSense6[1] = 0;
2730 aCDBModeSense6[2] = (0x00 << 6) | (0x08 & 0x3f); /* Current values and caching mode page */
2731 aCDBModeSense6[3] = 0; /* Sub page code. */
2732 aCDBModeSense6[4] = sizeof(aCachingModePage) & 0xff;
2733 aCDBModeSense6[5] = 0;
2734 sr.enmXfer = SCSIXFER_FROM_TARGET;
2735 sr.cbCmd = sizeof(aCDBModeSense6);
2736 sr.pvCmd = aCDBModeSense6;
2737 sr.cbI2TData = 0;
2738 sr.pcvI2TData = NULL;
2739 sr.cbT2IData = sizeof(aCachingModePage);
2740 sr.pvT2IData = aCachingModePage;
2741 sr.cbSense = sizeof(sense);
2742 sr.pvSense = sense;
2743 rc = iscsiCommand(pImage, &sr);
2744 if ( RT_SUCCESS(rc)
2745 && (sr.status == SCSI_STATUS_OK)
2746 && (aCachingModePage[0] >= 15)
2747 && (aCachingModePage[4 + aCachingModePage[3]] & 0x3f) == 0x08
2748 && (aCachingModePage[4 + aCachingModePage[3] + 1] > 3))
2749 {
2750 uint32_t Offset = 4 + aCachingModePage[3];
2751 /*
2752 * Check if the read and/or the write cache is disabled.
2753 * The write cache is disabled if bit 2 (WCE) is zero and
2754 * the read cache is disabled if bit 0 (RCD) is set.
2755 */
2756 if (!ASMBitTest(&aCachingModePage[Offset + 2], 2) || ASMBitTest(&aCachingModePage[Offset + 2], 0))
2757 {
2758 /*
2759 * Write Cache Enable (WCE) bit is zero or the Read Cache Disable (RCD) is one
2760 * So one of the caches is disabled. Enable both caches.
2761 * The rest is unchanged.
2762 */
2763 ASMBitSet(&aCachingModePage[Offset + 2], 2);
2764 ASMBitClear(&aCachingModePage[Offset + 2], 0);
2765
2766 uint8_t aCDBCaching[6];
2767 aCDBCaching[0] = SCSI_MODE_SELECT_6;
2768 aCDBCaching[1] = 0; /* Don't write the page into NV RAM. */
2769 aCDBCaching[2] = 0;
2770 aCDBCaching[3] = 0;
2771 aCDBCaching[4] = sizeof(aCachingModePage) & 0xff;
2772 aCDBCaching[5] = 0;
2773 sr.enmXfer = SCSIXFER_TO_TARGET;
2774 sr.cbCmd = sizeof(aCDBCaching);
2775 sr.pvCmd = aCDBCaching;
2776 sr.cbI2TData = sizeof(aCachingModePage);
2777 sr.pcvI2TData = aCachingModePage;
2778 sr.cbT2IData = 0;
2779 sr.pvT2IData = NULL;
2780 sr.cbSense = sizeof(sense);
2781 sr.pvSense = sense;
2782 sr.status = 0;
2783 rc = iscsiCommand(pImage, &sr);
2784 if ( RT_SUCCESS(rc)
2785 && (sr.status == SCSI_STATUS_OK))
2786 {
2787 LogRel(("iSCSI: Enabled read and write cache of target %s\n", pImage->pszTargetName));
2788 }
2789 else
2790 {
2791 /* Log failures but continue. */
2792 LogRel(("iSCSI: Could not enable read and write cache of target %s, rc=%Rrc status=%#x\n",
2793 pImage->pszTargetName, rc, sr.status));
2794 LogRel(("iSCSI: Sense:\n%.*Rhxd\n", sr.cbSense, sense));
2795 rc = VINF_SUCCESS;
2796 }
2797 }
2798 }
2799 else
2800 {
2801 /* Log errors but continue. */
2802 LogRel(("iSCSI: Could not check write cache of target %s, rc=%Rrc, got mode page %#x\n", pImage->pszTargetName, rc,aCachingModePage[0] & 0x3f));
2803 LogRel(("iSCSI: Sense:\n%.*Rhxd\n", sr.cbSense, sense));
2804 rc = VINF_SUCCESS;
2805 }
2806
2807
2808out:
2809 if (RT_FAILURE(rc))
2810 iscsiFreeImage(pImage, false);
2811 return rc;
2812}
2813
2814
2815/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
2816static int iscsiCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk)
2817{
2818 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
2819
2820 /* iSCSI images can't be checked for validity this way, as the filename
2821 * just can't supply enough configuration information. */
2822 int rc = VERR_VD_ISCSI_INVALID_HEADER;
2823
2824 LogFlowFunc(("returns %Rrc\n", rc));
2825 return rc;
2826}
2827
2828
2829/** @copydoc VBOXHDDBACKEND::pfnOpen */
2830static int iscsiOpen(const char *pszFilename, unsigned uOpenFlags,
2831 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
2832 void **ppBackendData)
2833{
2834 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
2835 int rc;
2836 PISCSIIMAGE pImage;
2837
2838 /* Check open flags. All valid flags are supported. */
2839 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
2840 {
2841 rc = VERR_INVALID_PARAMETER;
2842 goto out;
2843 }
2844
2845 /* Check remaining arguments. */
2846 if ( !VALID_PTR(pszFilename)
2847 || !*pszFilename
2848 || strchr(pszFilename, '"'))
2849 {
2850 rc = VERR_INVALID_PARAMETER;
2851 goto out;
2852 }
2853
2854 pImage = (PISCSIIMAGE)RTMemAllocZ(sizeof(ISCSIIMAGE));
2855 if (!pImage)
2856 {
2857 rc = VERR_NO_MEMORY;
2858 goto out;
2859 }
2860
2861 pImage->pszFilename = pszFilename;
2862 pImage->pszInitiatorName = NULL;
2863 pImage->pszTargetName = NULL;
2864 pImage->pszTargetAddress = NULL;
2865 pImage->pszInitiatorUsername = NULL;
2866 pImage->pbInitiatorSecret = NULL;
2867 pImage->pszTargetUsername = NULL;
2868 pImage->pbTargetSecret = NULL;
2869 pImage->paCurrReq = NULL;
2870 pImage->pvRecvPDUBuf = NULL;
2871 pImage->pszHostname = NULL;
2872 pImage->pVDIfsDisk = pVDIfsDisk;
2873 pImage->pVDIfsImage = pVDIfsImage;
2874
2875 rc = iscsiOpenImage(pImage, uOpenFlags);
2876 if (RT_SUCCESS(rc))
2877 {
2878 LogFlowFunc(("target %s cVolume %d, cbSector %d\n", pImage->pszTargetName, pImage->cVolume, pImage->cbSector));
2879 LogRel(("iSCSI: target address %s, target name %s, SCSI LUN %lld\n", pImage->pszTargetAddress, pImage->pszTargetName, pImage->LUN));
2880 *ppBackendData = pImage;
2881 }
2882
2883out:
2884 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
2885 return rc;
2886}
2887
2888
2889/** @copydoc VBOXHDDBACKEND::pfnCreate */
2890static int iscsiCreate(const char *pszFilename, uint64_t cbSize,
2891 unsigned uImageFlags, const char *pszComment,
2892 PCPDMMEDIAGEOMETRY pPCHSGeometry,
2893 PCPDMMEDIAGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
2894 unsigned uOpenFlags, unsigned uPercentStart,
2895 unsigned uPercentSpan, PVDINTERFACE pVDIfsDisk,
2896 PVDINTERFACE pVDIfsImage, PVDINTERFACE pVDIfsOperation,
2897 void **ppBackendData)
2898{
2899 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));
2900 int rc = VERR_NOT_SUPPORTED;
2901
2902 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
2903 return rc;
2904}
2905
2906
2907/** @copydoc VBOXHDDBACKEND::pfnRename */
2908static int iscsiRename(void *pBackendData, const char *pszFilename)
2909{
2910 LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
2911 int rc = VERR_NOT_SUPPORTED;
2912
2913 LogFlowFunc(("returns %Rrc\n", rc));
2914 return rc;
2915}
2916
2917
2918/** @copydoc VBOXHDDBACKEND::pfnClose */
2919static int iscsiClose(void *pBackendData, bool fDelete)
2920{
2921 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
2922 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
2923 int rc = VINF_SUCCESS;
2924
2925 Assert(!fDelete); /* This flag is unsupported. */
2926
2927 /* Freeing a never allocated image (e.g. because the open failed) is
2928 * not signalled as an error. After all nothing bad happens. */
2929 if (pImage)
2930 iscsiFreeImage(pImage, fDelete);
2931
2932 LogFlowFunc(("returns %Rrc\n", rc));
2933 return rc;
2934}
2935
2936
2937/** @copydoc VBOXHDDBACKEND::pfnRead */
2938static int iscsiRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
2939 size_t cbToRead, size_t *pcbActuallyRead)
2940{
2941 /** @todo reinstate logging of the target everywhere - dropped temporarily */
2942 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
2943 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
2944 uint64_t lba;
2945 uint16_t tls;
2946 int rc;
2947
2948 Assert(pImage);
2949 Assert(uOffset % 512 == 0);
2950 Assert(cbToRead % 512 == 0);
2951
2952 Assert(pImage->cbSector);
2953 AssertPtr(pvBuf);
2954
2955 if ( uOffset + cbToRead > pImage->cbSize
2956 || cbToRead == 0)
2957 {
2958 rc = VERR_INVALID_PARAMETER;
2959 goto out;
2960 }
2961
2962 /*
2963 * Clip read size to a value which is supported by the target.
2964 */
2965 cbToRead = RT_MIN(cbToRead, pImage->cbRecvDataLength);
2966
2967 lba = uOffset / pImage->cbSector;
2968 tls = (uint16_t)(cbToRead / pImage->cbSector);
2969 SCSIREQ sr;
2970 uint8_t cdb[10];
2971 uint8_t sense[32];
2972
2973 cdb[0] = SCSI_READ_10;
2974 cdb[1] = 0; /* reserved */
2975 cdb[2] = (lba >> 24) & 0xff;
2976 cdb[3] = (lba >> 16) & 0xff;
2977 cdb[4] = (lba >> 8) & 0xff;
2978 cdb[5] = lba & 0xff;
2979 cdb[6] = 0; /* reserved */
2980 cdb[7] = (tls >> 8) & 0xff;
2981 cdb[8] = tls & 0xff;
2982 cdb[9] = 0; /* control */
2983
2984 sr.enmXfer = SCSIXFER_FROM_TARGET;
2985 sr.cbCmd = sizeof(cdb);
2986 sr.pvCmd = cdb;
2987 sr.cbI2TData = 0;
2988 sr.pcvI2TData = NULL;
2989 sr.cbT2IData = cbToRead;
2990 sr.pvT2IData = pvBuf;
2991 sr.cbSense = sizeof(sense);
2992 sr.pvSense = sense;
2993
2994 for (unsigned i = 0; i < 10; i++)
2995 {
2996 rc = iscsiCommand(pImage, &sr);
2997 if ( (RT_SUCCESS(rc) && !sr.cbSense)
2998 || RT_FAILURE(rc))
2999 break;
3000 rc = VERR_READ_ERROR;
3001 }
3002 if (RT_FAILURE(rc))
3003 {
3004 AssertMsgFailed(("iscsiCommand(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
3005 *pcbActuallyRead = 0;
3006 }
3007 else
3008 *pcbActuallyRead = sr.cbT2IData;
3009
3010out:
3011 LogFlowFunc(("returns %Rrc\n", rc));
3012 return rc;
3013}
3014
3015
3016/** @copydoc VBOXHDDBACKEND::pfnWrite */
3017static int iscsiWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
3018 size_t cbToWrite, size_t *pcbWriteProcess,
3019 size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
3020{
3021 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
3022 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3023 uint64_t lba;
3024 uint16_t tls;
3025 int rc;
3026
3027 Assert(pImage);
3028 Assert(uOffset % 512 == 0);
3029 Assert(cbToWrite % 512 == 0);
3030
3031 Assert(pImage->cbSector);
3032 Assert(pvBuf);
3033
3034 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
3035 {
3036 rc = VERR_VD_IMAGE_READ_ONLY;
3037 goto out;
3038 }
3039
3040 *pcbPreRead = 0;
3041 *pcbPostRead = 0;
3042
3043 /*
3044 * Clip write size to a value which is supported by the target.
3045 */
3046 cbToWrite = RT_MIN(cbToWrite, pImage->cbSendDataLength);
3047
3048 lba = uOffset / pImage->cbSector;
3049 tls = (uint16_t)(cbToWrite / pImage->cbSector);
3050 SCSIREQ sr;
3051 uint8_t cdb[10];
3052 uint8_t sense[32];
3053
3054 cdb[0] = SCSI_WRITE_10;
3055 cdb[1] = 0; /* reserved */
3056 cdb[2] = (lba >> 24) & 0xff;
3057 cdb[3] = (lba >> 16) & 0xff;
3058 cdb[4] = (lba >> 8) & 0xff;
3059 cdb[5] = lba & 0xff;
3060 cdb[6] = 0; /* reserved */
3061 cdb[7] = (tls >> 8) & 0xff;
3062 cdb[8] = tls & 0xff;
3063 cdb[9] = 0; /* control */
3064
3065 sr.enmXfer = SCSIXFER_TO_TARGET;
3066 sr.cbCmd = sizeof(cdb);
3067 sr.pvCmd = cdb;
3068 sr.cbI2TData = cbToWrite;
3069 sr.pcvI2TData = pvBuf;
3070 sr.cbT2IData = 0;
3071 sr.pvT2IData = NULL;
3072 sr.cbSense = sizeof(sense);
3073 sr.pvSense = sense;
3074
3075 for (unsigned i = 0; i < 10; i++)
3076 {
3077 rc = iscsiCommand(pImage, &sr);
3078 if ( (RT_SUCCESS(rc) && !sr.cbSense)
3079 || RT_FAILURE(rc))
3080 break;
3081 rc = VERR_WRITE_ERROR;
3082 }
3083 if (RT_FAILURE(rc))
3084 {
3085 AssertMsgFailed(("iscsiCommand(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
3086 *pcbWriteProcess = 0;
3087 }
3088 else
3089 *pcbWriteProcess = cbToWrite;
3090
3091out:
3092 LogFlowFunc(("returns %Rrc\n", rc));
3093 return rc;
3094}
3095
3096
3097/** @copydoc VBOXHDDBACKEND::pfnFlush */
3098static int iscsiFlush(void *pBackendData)
3099{
3100 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
3101 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3102 int rc;
3103
3104 Assert(pImage);
3105
3106 SCSIREQ sr;
3107 uint8_t cdb[10];
3108 uint8_t sense[32];
3109
3110 cdb[0] = SCSI_SYNCHRONIZE_CACHE;
3111 cdb[1] = 0; /* reserved */
3112 cdb[2] = 0; /* LBA 0 */
3113 cdb[3] = 0; /* LBA 0 */
3114 cdb[4] = 0; /* LBA 0 */
3115 cdb[5] = 0; /* LBA 0 */
3116 cdb[6] = 0; /* reserved */
3117 cdb[7] = 0; /* transfer everything to disk */
3118 cdb[8] = 0; /* transfer everything to disk */
3119 cdb[9] = 0; /* control */
3120
3121 sr.enmXfer = SCSIXFER_TO_TARGET;
3122 sr.cbCmd = sizeof(cdb);
3123 sr.pvCmd = cdb;
3124 sr.cbI2TData = 0;
3125 sr.pcvI2TData = NULL;
3126 sr.cbT2IData = 0;
3127 sr.pvT2IData = NULL;
3128 sr.cbSense = sizeof(sense);
3129 sr.pvSense = sense;
3130
3131 rc = iscsiCommand(pImage, &sr);
3132 if (RT_FAILURE(rc))
3133 AssertMsgFailed(("iscsiCommand(%s) -> %Rrc\n", pImage->pszTargetName, rc));
3134 LogFlowFunc(("returns %Rrc\n", rc));
3135 return rc;
3136}
3137
3138
3139/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
3140static unsigned iscsiGetVersion(void *pBackendData)
3141{
3142 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
3143 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3144
3145 Assert(pImage);
3146 NOREF(pImage);
3147
3148 return 0;
3149}
3150
3151
3152/** @copydoc VBOXHDDBACKEND::pfnGetSize */
3153static uint64_t iscsiGetSize(void *pBackendData)
3154{
3155 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
3156 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3157
3158 Assert(pImage);
3159
3160 if (pImage)
3161 return pImage->cbSize;
3162 else
3163 return 0;
3164}
3165
3166
3167/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
3168static uint64_t iscsiGetFileSize(void *pBackendData)
3169{
3170 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
3171 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3172
3173 Assert(pImage);
3174 NOREF(pImage);
3175
3176 if (pImage)
3177 return pImage->cbSize;
3178 else
3179 return 0;
3180}
3181
3182
3183/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
3184static int iscsiGetPCHSGeometry(void *pBackendData,
3185 PPDMMEDIAGEOMETRY pPCHSGeometry)
3186{
3187 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
3188 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3189 int rc;
3190
3191 Assert(pImage);
3192
3193 if (pImage)
3194 rc = VERR_VD_GEOMETRY_NOT_SET;
3195 else
3196 rc = VERR_VD_NOT_OPENED;
3197
3198 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
3199 return rc;
3200}
3201
3202
3203/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
3204static int iscsiSetPCHSGeometry(void *pBackendData,
3205 PCPDMMEDIAGEOMETRY pPCHSGeometry)
3206{
3207 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
3208 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3209 int rc;
3210
3211 Assert(pImage);
3212
3213 if (pImage)
3214 {
3215 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
3216 {
3217 rc = VERR_VD_IMAGE_READ_ONLY;
3218 goto out;
3219 }
3220 rc = VERR_VD_GEOMETRY_NOT_SET;
3221 }
3222 else
3223 rc = VERR_VD_NOT_OPENED;
3224
3225out:
3226 LogFlowFunc(("returns %Rrc\n", rc));
3227 return rc;
3228}
3229
3230
3231/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
3232static int iscsiGetLCHSGeometry(void *pBackendData,
3233 PPDMMEDIAGEOMETRY pLCHSGeometry)
3234{
3235 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
3236 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3237 int rc;
3238
3239 Assert(pImage);
3240
3241 if (pImage)
3242 rc = VERR_VD_GEOMETRY_NOT_SET;
3243 else
3244 rc = VERR_VD_NOT_OPENED;
3245
3246 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
3247 return rc;
3248}
3249
3250
3251/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
3252static unsigned iscsiGetImageFlags(void *pBackendData)
3253{
3254 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
3255 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3256 unsigned uImageFlags;
3257
3258 Assert(pImage);
3259 NOREF(pImage);
3260
3261 uImageFlags = VD_IMAGE_FLAGS_FIXED;
3262
3263 LogFlowFunc(("returns %#x\n", uImageFlags));
3264 return uImageFlags;
3265}
3266
3267
3268/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
3269static unsigned iscsiGetOpenFlags(void *pBackendData)
3270{
3271 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
3272 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3273 unsigned uOpenFlags;
3274
3275 Assert(pImage);
3276
3277 if (pImage)
3278 uOpenFlags = pImage->uOpenFlags;
3279 else
3280 uOpenFlags = 0;
3281
3282 LogFlowFunc(("returns %#x\n", uOpenFlags));
3283 return uOpenFlags;
3284}
3285
3286
3287/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
3288static int iscsiSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
3289{
3290 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
3291 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3292 int rc;
3293
3294 /* Image must be opened and the new flags must be valid. Just readonly and
3295 * info flags are supported. */
3296 if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO)))
3297 {
3298 rc = VERR_INVALID_PARAMETER;
3299 goto out;
3300 }
3301
3302 /* Implement this operation via reopening the image. */
3303 iscsiFreeImage(pImage, false);
3304 rc = iscsiOpenImage(pImage, uOpenFlags);
3305
3306out:
3307 LogFlowFunc(("returns %Rrc\n", rc));
3308 return rc;
3309}
3310
3311
3312/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
3313static int iscsiSetLCHSGeometry(void *pBackendData,
3314 PCPDMMEDIAGEOMETRY pLCHSGeometry)
3315{
3316 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
3317 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3318 int rc;
3319
3320 Assert(pImage);
3321
3322 if (pImage)
3323 {
3324 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
3325 {
3326 rc = VERR_VD_IMAGE_READ_ONLY;
3327 goto out;
3328 }
3329 rc = VERR_VD_GEOMETRY_NOT_SET;
3330 }
3331 else
3332 rc = VERR_VD_NOT_OPENED;
3333
3334out:
3335 LogFlowFunc(("returns %Rrc\n", rc));
3336 return rc;
3337}
3338
3339
3340/** @copydoc VBOXHDDBACKEND::pfnGetComment */
3341static int iscsiGetComment(void *pBackendData, char *pszComment,
3342 size_t cbComment)
3343{
3344 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
3345 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3346 int rc;
3347
3348 Assert(pImage);
3349
3350 if (pImage)
3351 rc = VERR_NOT_SUPPORTED;
3352 else
3353 rc = VERR_VD_NOT_OPENED;
3354
3355 LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
3356 return rc;
3357}
3358
3359
3360/** @copydoc VBOXHDDBACKEND::pfnSetComment */
3361static int iscsiSetComment(void *pBackendData, const char *pszComment)
3362{
3363 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
3364 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3365 int rc;
3366
3367 Assert(pImage);
3368
3369 if (pImage)
3370 {
3371 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
3372 rc = VERR_NOT_SUPPORTED;
3373 else
3374 rc = VERR_VD_IMAGE_READ_ONLY;
3375 }
3376 else
3377 rc = VERR_VD_NOT_OPENED;
3378
3379 LogFlowFunc(("returns %Rrc\n", rc));
3380 return rc;
3381}
3382
3383
3384/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
3385static int iscsiGetUuid(void *pBackendData, PRTUUID pUuid)
3386{
3387 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
3388 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3389 int rc;
3390
3391 Assert(pImage);
3392
3393 if (pImage)
3394 rc = VERR_NOT_SUPPORTED;
3395 else
3396 rc = VERR_VD_NOT_OPENED;
3397
3398 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
3399 return rc;
3400}
3401
3402
3403/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
3404static int iscsiSetUuid(void *pBackendData, PCRTUUID pUuid)
3405{
3406 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
3407 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3408 int rc;
3409
3410 LogFlowFunc(("%RTuuid\n", pUuid));
3411 Assert(pImage);
3412
3413 if (pImage)
3414 {
3415 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
3416 rc = VERR_NOT_SUPPORTED;
3417 else
3418 rc = VERR_VD_IMAGE_READ_ONLY;
3419 }
3420 else
3421 rc = VERR_VD_NOT_OPENED;
3422
3423 LogFlowFunc(("returns %Rrc\n", rc));
3424 return rc;
3425}
3426
3427
3428/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
3429static int iscsiGetModificationUuid(void *pBackendData, PRTUUID pUuid)
3430{
3431 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
3432 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3433 int rc;
3434
3435 Assert(pImage);
3436
3437 if (pImage)
3438 rc = VERR_NOT_SUPPORTED;
3439 else
3440 rc = VERR_VD_NOT_OPENED;
3441
3442 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
3443 return rc;
3444}
3445
3446
3447/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
3448static int iscsiSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
3449{
3450 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
3451 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3452 int rc;
3453
3454 LogFlowFunc(("%RTuuid\n", pUuid));
3455 Assert(pImage);
3456
3457 if (pImage)
3458 {
3459 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
3460 rc = VERR_NOT_SUPPORTED;
3461 else
3462 rc = VERR_VD_IMAGE_READ_ONLY;
3463 }
3464 else
3465 rc = VERR_VD_NOT_OPENED;
3466
3467 LogFlowFunc(("returns %Rrc\n", rc));
3468 return rc;
3469}
3470
3471
3472/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
3473static int iscsiGetParentUuid(void *pBackendData, PRTUUID pUuid)
3474{
3475 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
3476 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3477 int rc;
3478
3479 Assert(pImage);
3480
3481 if (pImage)
3482 rc = VERR_NOT_SUPPORTED;
3483 else
3484 rc = VERR_VD_NOT_OPENED;
3485
3486 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
3487 return rc;
3488}
3489
3490
3491/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
3492static int iscsiSetParentUuid(void *pBackendData, PCRTUUID pUuid)
3493{
3494 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
3495 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3496 int rc;
3497
3498 LogFlowFunc(("%RTuuid\n", pUuid));
3499 Assert(pImage);
3500
3501 if (pImage)
3502 {
3503 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
3504 rc = VERR_NOT_SUPPORTED;
3505 else
3506 rc = VERR_VD_IMAGE_READ_ONLY;
3507 }
3508 else
3509 rc = VERR_VD_NOT_OPENED;
3510
3511 LogFlowFunc(("returns %Rrc\n", rc));
3512 return rc;
3513}
3514
3515
3516/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
3517static int iscsiGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
3518{
3519 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
3520 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3521 int rc;
3522
3523 Assert(pImage);
3524
3525 if (pImage)
3526 rc = VERR_NOT_SUPPORTED;
3527 else
3528 rc = VERR_VD_NOT_OPENED;
3529
3530 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
3531 return rc;
3532}
3533
3534
3535/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
3536static int iscsiSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
3537{
3538 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
3539 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3540 int rc;
3541
3542 LogFlowFunc(("%RTuuid\n", pUuid));
3543 Assert(pImage);
3544
3545 if (pImage)
3546 {
3547 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
3548 rc = VERR_NOT_SUPPORTED;
3549 else
3550 rc = VERR_VD_IMAGE_READ_ONLY;
3551 }
3552 else
3553 rc = VERR_VD_NOT_OPENED;
3554
3555 LogFlowFunc(("returns %Rrc\n", rc));
3556 return rc;
3557}
3558
3559
3560/** @copydoc VBOXHDDBACKEND::pfnDump */
3561static void iscsiDump(void *pBackendData)
3562{
3563 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3564
3565 Assert(pImage);
3566 if (pImage)
3567 {
3568 /** @todo put something useful here */
3569 pImage->pInterfaceErrorCallbacks->pfnMessage(pImage->pInterfaceError->pvUser, "Header: cVolume=%u\n", pImage->cVolume);
3570 }
3571}
3572
3573
3574/** @copydoc VBOXHDDBACKEND::pfnGetTimeStamp */
3575static int iscsiGetTimeStamp(void *pBackendData, PRTTIMESPEC pTimeStamp)
3576{
3577 LogFlowFunc(("pBackendData=%#p pTimeStamp=%#p\n", pBackendData, pTimeStamp));
3578 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3579 int rc = VERR_NOT_SUPPORTED;
3580
3581 Assert(pImage);
3582 NOREF(pImage);
3583
3584 LogFlowFunc(("returns %Rrc\n", rc));
3585 return rc;
3586}
3587
3588
3589/** @copydoc VBOXHDDBACKEND::pfnGetParentTimeStamp */
3590static int iscsiGetParentTimeStamp(void *pBackendData, PRTTIMESPEC pTimeStamp)
3591{
3592 LogFlowFunc(("pBackendData=%#p pTimeStamp=%#p\n", pBackendData, pTimeStamp));
3593 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3594 int rc = VERR_NOT_SUPPORTED;
3595
3596 Assert(pImage);
3597 NOREF(pImage);
3598
3599 LogFlowFunc(("returns %Rrc\n", rc));
3600 return rc;
3601}
3602
3603
3604/** @copydoc VBOXHDDBACKEND::pfnSetParentTimeStamp */
3605static int iscsiSetParentTimeStamp(void *pBackendData, PCRTTIMESPEC pTimeStamp)
3606{
3607 LogFlowFunc(("pBackendData=%#p pTimeStamp=%#p\n", pBackendData, pTimeStamp));
3608 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3609 int rc = VERR_NOT_SUPPORTED;
3610
3611 Assert(pImage);
3612 NOREF(pImage);
3613
3614 LogFlowFunc(("returns %Rrc\n", rc));
3615 return rc;
3616}
3617
3618
3619/** @copydoc VBOXHDDBACKEND::pfnGetParentFilename */
3620static int iscsiGetParentFilename(void *pBackendData, char **ppszParentFilename)
3621{
3622 LogFlowFunc(("pBackendData=%#p ppszParentFilename=%#p\n", pBackendData, ppszParentFilename));
3623 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3624 int rc = VERR_NOT_SUPPORTED;
3625
3626 Assert(pImage);
3627 NOREF(pImage);
3628
3629 LogFlowFunc(("returns %Rrc\n", rc));
3630 return rc;
3631}
3632
3633
3634/** @copydoc VBOXHDDBACKEND::pfnSetParentFilename */
3635static int iscsiSetParentFilename(void *pBackendData, const char *pszParentFilename)
3636{
3637 LogFlowFunc(("pBackendData=%#p pszParentFilename=%s\n", pBackendData, pszParentFilename));
3638 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3639 int rc = VERR_NOT_SUPPORTED;
3640
3641 Assert(pImage);
3642 NOREF(pImage);
3643
3644 LogFlowFunc(("returns %Rrc\n", rc));
3645 return rc;
3646}
3647
3648/** @copydoc VBOXHDDBACKEND::pfnComposeLocation */
3649static int iscsiComposeLocation(PVDINTERFACE pConfig, char **pszLocation)
3650{
3651 char *pszTarget = NULL;
3652 char *pszLUN = NULL;
3653 char *pszAddress = NULL;
3654 int rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetName", &pszTarget);
3655 if (RT_SUCCESS(rc))
3656 {
3657 rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "LUN", &pszLUN);
3658 if (RT_SUCCESS(rc))
3659 {
3660 rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetAddress", &pszAddress);
3661 if (RT_SUCCESS(rc))
3662 {
3663 if (RTStrAPrintf(pszLocation, "iscsi://%s/%s/%s",
3664 pszAddress, pszTarget, pszLUN) < 0)
3665 rc = VERR_NO_MEMORY;
3666 }
3667 }
3668 }
3669 RTMemFree(pszTarget);
3670 RTMemFree(pszLUN);
3671 RTMemFree(pszAddress);
3672 return rc;
3673}
3674
3675/** @copydoc VBOXHDDBACKEND::pfnComposeName */
3676static int iscsiComposeName(PVDINTERFACE pConfig, char **pszName)
3677{
3678 char *pszTarget = NULL;
3679 char *pszLUN = NULL;
3680 char *pszAddress = NULL;
3681 int rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetName", &pszTarget);
3682 if (RT_SUCCESS(rc))
3683 {
3684 rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "LUN", &pszLUN);
3685 if (RT_SUCCESS(rc))
3686 {
3687 rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetAddress", &pszAddress);
3688 if (RT_SUCCESS(rc))
3689 {
3690 /** @todo think about a nicer looking location scheme for iSCSI */
3691 if (RTStrAPrintf(pszName, "%s/%s/%s",
3692 pszAddress, pszTarget, pszLUN) < 0)
3693 rc = VERR_NO_MEMORY;
3694 }
3695 }
3696 }
3697 RTMemFree(pszTarget);
3698 RTMemFree(pszLUN);
3699 RTMemFree(pszAddress);
3700
3701 return rc;
3702}
3703
3704
3705VBOXHDDBACKEND g_ISCSIBackend =
3706{
3707 /* pszBackendName */
3708 "iSCSI",
3709 /* cbSize */
3710 sizeof(VBOXHDDBACKEND),
3711 /* uBackendCaps */
3712 VD_CAP_CONFIG | VD_CAP_TCPNET,
3713 /* papszFileExtensions */
3714 NULL,
3715 /* paConfigInfo */
3716 s_iscsiConfigInfo,
3717 /* hPlugin */
3718 NIL_RTLDRMOD,
3719 /* pfnCheckIfValid */
3720 iscsiCheckIfValid,
3721 /* pfnOpen */
3722 iscsiOpen,
3723 /* pfnCreate */
3724 iscsiCreate,
3725 /* pfnRename */
3726 iscsiRename,
3727 /* pfnClose */
3728 iscsiClose,
3729 /* pfnRead */
3730 iscsiRead,
3731 /* pfnWrite */
3732 iscsiWrite,
3733 /* pfnFlush */
3734 iscsiFlush,
3735 /* pfnGetVersion */
3736 iscsiGetVersion,
3737 /* pfnGetSize */
3738 iscsiGetSize,
3739 /* pfnGetFileSize */
3740 iscsiGetFileSize,
3741 /* pfnGetPCHSGeometry */
3742 iscsiGetPCHSGeometry,
3743 /* pfnSetPCHSGeometry */
3744 iscsiSetPCHSGeometry,
3745 /* pfnGetLCHSGeometry */
3746 iscsiGetLCHSGeometry,
3747 /* pfnSetLCHSGeometry */
3748 iscsiSetLCHSGeometry,
3749 /* pfnGetImageFlags */
3750 iscsiGetImageFlags,
3751 /* pfnGetOpenFlags */
3752 iscsiGetOpenFlags,
3753 /* pfnSetOpenFlags */
3754 iscsiSetOpenFlags,
3755 /* pfnGetComment */
3756 iscsiGetComment,
3757 /* pfnSetComment */
3758 iscsiSetComment,
3759 /* pfnGetUuid */
3760 iscsiGetUuid,
3761 /* pfnSetUuid */
3762 iscsiSetUuid,
3763 /* pfnGetModificationUuid */
3764 iscsiGetModificationUuid,
3765 /* pfnSetModificationUuid */
3766 iscsiSetModificationUuid,
3767 /* pfnGetParentUuid */
3768 iscsiGetParentUuid,
3769 /* pfnSetParentUuid */
3770 iscsiSetParentUuid,
3771 /* pfnGetParentModificationUuid */
3772 iscsiGetParentModificationUuid,
3773 /* pfnSetParentModificationUuid */
3774 iscsiSetParentModificationUuid,
3775 /* pfnDump */
3776 iscsiDump,
3777 /* pfnGetTimeStamp */
3778 iscsiGetTimeStamp,
3779 /* pfnGetParentTimeStamp */
3780 iscsiGetParentTimeStamp,
3781 /* pfnSetParentTimeStamp */
3782 iscsiSetParentTimeStamp,
3783 /* pfnGetParentFilename */
3784 iscsiGetParentFilename,
3785 /* pfnSetParentFilename */
3786 iscsiSetParentFilename,
3787 /* pfnIsAsyncIOSupported */
3788 NULL,
3789 /* pfnAsyncRead */
3790 NULL,
3791 /* pfnAsyncWrite */
3792 NULL,
3793 /* pfnComposeLocation */
3794 iscsiComposeLocation,
3795 /* pfnComposeName */
3796 iscsiComposeName
3797};
Note: See TracBrowser for help on using the repository browser.

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