VirtualBox

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

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

Storage/iSCSI: be more robust when a target reports errors for an INQUIRY command, especially don't just blindly look at potentially uninitialized values off the stack.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 131.3 KB
Line 
1/* $Id: ISCSIHDDCore.cpp 26684 2010-02-22 16:58:27Z 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 for (unsigned i = 0; i < 10; i++)
2568 {
2569 rc = iscsiCommand(pImage, &sr);
2570 if ( (RT_SUCCESS(rc) && !sr.cbSense)
2571 || RT_FAILURE(rc))
2572 break;
2573 rc = VERR_INVALID_STATE;
2574 }
2575 if (RT_SUCCESS(rc))
2576 {
2577 uint8_t devType = (sr.cbT2IData > 0) ? data8[0] & SCSI_DEVTYPE_MASK : 255;
2578 if (devType != SCSI_DEVTYPE_DISK)
2579 {
2580 rc = iscsiError(pImage, VERR_VD_ISCSI_INVALID_TYPE,
2581 RT_SRC_POS, N_("iSCSI: target address %s, target name %s, SCSI LUN %lld reports device type=%u"),
2582 pImage->pszTargetAddress, pImage->pszTargetName,
2583 pImage->LUN, devType);
2584 LogRel(("iSCSI: Unsupported SCSI peripheral device type %d for target %s\n", devType & SCSI_DEVTYPE_MASK, pImage->pszTargetName));
2585 goto out;
2586 }
2587 }
2588 else
2589 {
2590 LogRel(("iSCSI: Could not get INQUIRY info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
2591 goto out;
2592 }
2593
2594 /*
2595 * Query write disable bit in the device specific parameter entry in the
2596 * mode parameter header. Refuse read/write opening of read only disks.
2597 */
2598
2599 uint8_t cdb_ms[6];
2600 uint8_t data4[4];
2601 cdb_ms[0] = SCSI_MODE_SENSE_6;
2602 cdb_ms[1] = 0; /* dbd=0/reserved */
2603 cdb_ms[2] = 0x3f; /* pc=0/page code=0x3f, ask for all pages */
2604 cdb_ms[3] = 0; /* subpage code=0, return everything in page_0 format */
2605 cdb_ms[4] = sizeof(data4); /* allocation length=4 */
2606 cdb_ms[5] = 0; /* control */
2607
2608 sr.enmXfer = SCSIXFER_FROM_TARGET;
2609 sr.cbCmd = sizeof(cdb_ms);
2610 sr.pvCmd = cdb_ms;
2611 sr.cbI2TData = 0;
2612 sr.pcvI2TData = NULL;
2613 sr.cbT2IData = sizeof(data4);
2614 sr.pvT2IData = data4;
2615 sr.cbSense = sizeof(sense);
2616 sr.pvSense = sense;
2617
2618 for (unsigned i = 0; i < 10; i++)
2619 {
2620 rc = iscsiCommand(pImage, &sr);
2621 if ( (RT_SUCCESS(rc) && !sr.cbSense)
2622 || RT_FAILURE(rc))
2623 break;
2624 rc = VERR_INVALID_STATE;
2625 }
2626 if (RT_SUCCESS(rc))
2627 {
2628 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY) && data4[2] & 0x80)
2629 {
2630 rc = VERR_VD_IMAGE_READ_ONLY;
2631 goto out;
2632 }
2633 }
2634 else
2635 {
2636 LogRel(("iSCSI: Could not get MODE SENSE info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
2637 goto out;
2638 }
2639
2640 /*
2641 * Determine sector size and capacity of the volume immediately.
2642 */
2643 uint8_t cdb_cap[16];
2644
2645 RT_ZERO(data12);
2646 RT_ZERO(cdb_cap);
2647 cdb_cap[0] = SCSI_SERVICE_ACTION_IN_16;
2648 cdb_cap[1] = SCSI_SVC_ACTION_IN_READ_CAPACITY_16; /* subcommand */
2649 cdb_cap[10+3] = sizeof(data12); /* allocation length (dword) */
2650
2651 sr.enmXfer = SCSIXFER_FROM_TARGET;
2652 sr.cbCmd = sizeof(cdb_cap);
2653 sr.pvCmd = cdb_cap;
2654 sr.cbI2TData = 0;
2655 sr.pcvI2TData = NULL;
2656 sr.cbT2IData = sizeof(data12);
2657 sr.pvT2IData = data12;
2658 sr.cbSense = sizeof(sense);
2659 sr.pvSense = sense;
2660
2661 rc = iscsiCommand(pImage, &sr);
2662 if ( RT_SUCCESS(rc)
2663 && sr.status == SCSI_STATUS_OK)
2664 {
2665 pImage->cVolume = RT_BE2H_U64(*(uint64_t *)&data12[0]);
2666 pImage->cVolume++;
2667 pImage->cbSector = RT_BE2H_U32(*(uint32_t *)&data12[8]);
2668 pImage->cbSize = pImage->cVolume * pImage->cbSector;
2669 if (pImage->cVolume == 0 || pImage->cbSector != 512 || pImage->cbSize < pImage->cVolume)
2670 {
2671 rc = iscsiError(pImage, VERR_VD_ISCSI_INVALID_TYPE,
2672 RT_SRC_POS, N_("iSCSI: target address %s, target name %s, SCSI LUN %lld reports media sector count=%llu sector size=%u"),
2673 pImage->pszTargetAddress, pImage->pszTargetName,
2674 pImage->LUN, pImage->cVolume, pImage->cbSector);
2675 }
2676 }
2677 else
2678 {
2679 uint8_t cdb_capfb[10];
2680
2681 RT_ZERO(data8);
2682 cdb_capfb[0] = SCSI_READ_CAPACITY;
2683 cdb_capfb[1] = 0; /* reserved */
2684 cdb_capfb[2] = 0; /* reserved */
2685 cdb_capfb[3] = 0; /* reserved */
2686 cdb_capfb[4] = 0; /* reserved */
2687 cdb_capfb[5] = 0; /* reserved */
2688 cdb_capfb[6] = 0; /* reserved */
2689 cdb_capfb[7] = 0; /* reserved */
2690 cdb_capfb[8] = 0; /* reserved */
2691 cdb_capfb[9] = 0; /* control */
2692
2693 sr.enmXfer = SCSIXFER_FROM_TARGET;
2694 sr.cbCmd = sizeof(cdb_capfb);
2695 sr.pvCmd = cdb_capfb;
2696 sr.cbI2TData = 0;
2697 sr.pcvI2TData = NULL;
2698 sr.cbT2IData = sizeof(data8);
2699 sr.pvT2IData = data8;
2700 sr.cbSense = sizeof(sense);
2701 sr.pvSense = sense;
2702
2703 rc = iscsiCommand(pImage, &sr);
2704 if (RT_SUCCESS(rc))
2705 {
2706 pImage->cVolume = (data8[0] << 24) | (data8[1] << 16) | (data8[2] << 8) | data8[3];
2707 pImage->cVolume++;
2708 pImage->cbSector = (data8[4] << 24) | (data8[5] << 16) | (data8[6] << 8) | data8[7];
2709 pImage->cbSize = pImage->cVolume * pImage->cbSector;
2710 if (pImage->cVolume == 0 || pImage->cbSector != 512)
2711 {
2712 rc = iscsiError(pImage, VERR_VD_ISCSI_INVALID_TYPE,
2713 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"),
2714 pImage->pszTargetAddress, pImage->pszTargetName,
2715 pImage->LUN, pImage->cVolume, pImage->cbSector);
2716 }
2717 }
2718 else
2719 {
2720 LogRel(("iSCSI: Could not determine capacity of target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
2721 goto out;
2722 }
2723 }
2724
2725 /*
2726 * Check the read and write cache bits.
2727 * Try to enable the cache if it is disabled.
2728 *
2729 * We already checked that this is a block access device. No need
2730 * to do it again.
2731 */
2732 uint8_t aCachingModePage[32];
2733 uint8_t aCDBModeSense6[6];
2734
2735 memset(aCachingModePage, '\0', sizeof(aCachingModePage));
2736 aCDBModeSense6[0] = SCSI_MODE_SENSE_6;
2737 aCDBModeSense6[1] = 0;
2738 aCDBModeSense6[2] = (0x00 << 6) | (0x08 & 0x3f); /* Current values and caching mode page */
2739 aCDBModeSense6[3] = 0; /* Sub page code. */
2740 aCDBModeSense6[4] = sizeof(aCachingModePage) & 0xff;
2741 aCDBModeSense6[5] = 0;
2742 sr.enmXfer = SCSIXFER_FROM_TARGET;
2743 sr.cbCmd = sizeof(aCDBModeSense6);
2744 sr.pvCmd = aCDBModeSense6;
2745 sr.cbI2TData = 0;
2746 sr.pcvI2TData = NULL;
2747 sr.cbT2IData = sizeof(aCachingModePage);
2748 sr.pvT2IData = aCachingModePage;
2749 sr.cbSense = sizeof(sense);
2750 sr.pvSense = sense;
2751 rc = iscsiCommand(pImage, &sr);
2752 if ( RT_SUCCESS(rc)
2753 && (sr.status == SCSI_STATUS_OK)
2754 && (aCachingModePage[0] >= 15)
2755 && (aCachingModePage[4 + aCachingModePage[3]] & 0x3f) == 0x08
2756 && (aCachingModePage[4 + aCachingModePage[3] + 1] > 3))
2757 {
2758 uint32_t Offset = 4 + aCachingModePage[3];
2759 /*
2760 * Check if the read and/or the write cache is disabled.
2761 * The write cache is disabled if bit 2 (WCE) is zero and
2762 * the read cache is disabled if bit 0 (RCD) is set.
2763 */
2764 if (!ASMBitTest(&aCachingModePage[Offset + 2], 2) || ASMBitTest(&aCachingModePage[Offset + 2], 0))
2765 {
2766 /*
2767 * Write Cache Enable (WCE) bit is zero or the Read Cache Disable (RCD) is one
2768 * So one of the caches is disabled. Enable both caches.
2769 * The rest is unchanged.
2770 */
2771 ASMBitSet(&aCachingModePage[Offset + 2], 2);
2772 ASMBitClear(&aCachingModePage[Offset + 2], 0);
2773
2774 uint8_t aCDBCaching[6];
2775 aCDBCaching[0] = SCSI_MODE_SELECT_6;
2776 aCDBCaching[1] = 0; /* Don't write the page into NV RAM. */
2777 aCDBCaching[2] = 0;
2778 aCDBCaching[3] = 0;
2779 aCDBCaching[4] = sizeof(aCachingModePage) & 0xff;
2780 aCDBCaching[5] = 0;
2781 sr.enmXfer = SCSIXFER_TO_TARGET;
2782 sr.cbCmd = sizeof(aCDBCaching);
2783 sr.pvCmd = aCDBCaching;
2784 sr.cbI2TData = sizeof(aCachingModePage);
2785 sr.pcvI2TData = aCachingModePage;
2786 sr.cbT2IData = 0;
2787 sr.pvT2IData = NULL;
2788 sr.cbSense = sizeof(sense);
2789 sr.pvSense = sense;
2790 sr.status = 0;
2791 rc = iscsiCommand(pImage, &sr);
2792 if ( RT_SUCCESS(rc)
2793 && (sr.status == SCSI_STATUS_OK))
2794 {
2795 LogRel(("iSCSI: Enabled read and write cache of target %s\n", pImage->pszTargetName));
2796 }
2797 else
2798 {
2799 /* Log failures but continue. */
2800 LogRel(("iSCSI: Could not enable read and write cache of target %s, rc=%Rrc status=%#x\n",
2801 pImage->pszTargetName, rc, sr.status));
2802 LogRel(("iSCSI: Sense:\n%.*Rhxd\n", sr.cbSense, sense));
2803 rc = VINF_SUCCESS;
2804 }
2805 }
2806 }
2807 else
2808 {
2809 /* Log errors but continue. */
2810 LogRel(("iSCSI: Could not check write cache of target %s, rc=%Rrc, got mode page %#x\n", pImage->pszTargetName, rc,aCachingModePage[0] & 0x3f));
2811 LogRel(("iSCSI: Sense:\n%.*Rhxd\n", sr.cbSense, sense));
2812 rc = VINF_SUCCESS;
2813 }
2814
2815
2816out:
2817 if (RT_FAILURE(rc))
2818 iscsiFreeImage(pImage, false);
2819 return rc;
2820}
2821
2822
2823/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
2824static int iscsiCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk)
2825{
2826 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
2827
2828 /* iSCSI images can't be checked for validity this way, as the filename
2829 * just can't supply enough configuration information. */
2830 int rc = VERR_VD_ISCSI_INVALID_HEADER;
2831
2832 LogFlowFunc(("returns %Rrc\n", rc));
2833 return rc;
2834}
2835
2836
2837/** @copydoc VBOXHDDBACKEND::pfnOpen */
2838static int iscsiOpen(const char *pszFilename, unsigned uOpenFlags,
2839 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
2840 void **ppBackendData)
2841{
2842 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
2843 int rc;
2844 PISCSIIMAGE pImage;
2845
2846 /* Check open flags. All valid flags are supported. */
2847 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
2848 {
2849 rc = VERR_INVALID_PARAMETER;
2850 goto out;
2851 }
2852
2853 /* Check remaining arguments. */
2854 if ( !VALID_PTR(pszFilename)
2855 || !*pszFilename
2856 || strchr(pszFilename, '"'))
2857 {
2858 rc = VERR_INVALID_PARAMETER;
2859 goto out;
2860 }
2861
2862 pImage = (PISCSIIMAGE)RTMemAllocZ(sizeof(ISCSIIMAGE));
2863 if (!pImage)
2864 {
2865 rc = VERR_NO_MEMORY;
2866 goto out;
2867 }
2868
2869 pImage->pszFilename = pszFilename;
2870 pImage->pszInitiatorName = NULL;
2871 pImage->pszTargetName = NULL;
2872 pImage->pszTargetAddress = NULL;
2873 pImage->pszInitiatorUsername = NULL;
2874 pImage->pbInitiatorSecret = NULL;
2875 pImage->pszTargetUsername = NULL;
2876 pImage->pbTargetSecret = NULL;
2877 pImage->paCurrReq = NULL;
2878 pImage->pvRecvPDUBuf = NULL;
2879 pImage->pszHostname = NULL;
2880 pImage->pVDIfsDisk = pVDIfsDisk;
2881 pImage->pVDIfsImage = pVDIfsImage;
2882
2883 rc = iscsiOpenImage(pImage, uOpenFlags);
2884 if (RT_SUCCESS(rc))
2885 {
2886 LogFlowFunc(("target %s cVolume %d, cbSector %d\n", pImage->pszTargetName, pImage->cVolume, pImage->cbSector));
2887 LogRel(("iSCSI: target address %s, target name %s, SCSI LUN %lld\n", pImage->pszTargetAddress, pImage->pszTargetName, pImage->LUN));
2888 *ppBackendData = pImage;
2889 }
2890
2891out:
2892 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
2893 return rc;
2894}
2895
2896
2897/** @copydoc VBOXHDDBACKEND::pfnCreate */
2898static int iscsiCreate(const char *pszFilename, uint64_t cbSize,
2899 unsigned uImageFlags, const char *pszComment,
2900 PCPDMMEDIAGEOMETRY pPCHSGeometry,
2901 PCPDMMEDIAGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
2902 unsigned uOpenFlags, unsigned uPercentStart,
2903 unsigned uPercentSpan, PVDINTERFACE pVDIfsDisk,
2904 PVDINTERFACE pVDIfsImage, PVDINTERFACE pVDIfsOperation,
2905 void **ppBackendData)
2906{
2907 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));
2908 int rc = VERR_NOT_SUPPORTED;
2909
2910 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
2911 return rc;
2912}
2913
2914
2915/** @copydoc VBOXHDDBACKEND::pfnRename */
2916static int iscsiRename(void *pBackendData, const char *pszFilename)
2917{
2918 LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
2919 int rc = VERR_NOT_SUPPORTED;
2920
2921 LogFlowFunc(("returns %Rrc\n", rc));
2922 return rc;
2923}
2924
2925
2926/** @copydoc VBOXHDDBACKEND::pfnClose */
2927static int iscsiClose(void *pBackendData, bool fDelete)
2928{
2929 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
2930 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
2931 int rc = VINF_SUCCESS;
2932
2933 Assert(!fDelete); /* This flag is unsupported. */
2934
2935 /* Freeing a never allocated image (e.g. because the open failed) is
2936 * not signalled as an error. After all nothing bad happens. */
2937 if (pImage)
2938 iscsiFreeImage(pImage, fDelete);
2939
2940 LogFlowFunc(("returns %Rrc\n", rc));
2941 return rc;
2942}
2943
2944
2945/** @copydoc VBOXHDDBACKEND::pfnRead */
2946static int iscsiRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
2947 size_t cbToRead, size_t *pcbActuallyRead)
2948{
2949 /** @todo reinstate logging of the target everywhere - dropped temporarily */
2950 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
2951 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
2952 uint64_t lba;
2953 uint16_t tls;
2954 int rc;
2955
2956 Assert(pImage);
2957 Assert(uOffset % 512 == 0);
2958 Assert(cbToRead % 512 == 0);
2959
2960 Assert(pImage->cbSector);
2961 AssertPtr(pvBuf);
2962
2963 if ( uOffset + cbToRead > pImage->cbSize
2964 || cbToRead == 0)
2965 {
2966 rc = VERR_INVALID_PARAMETER;
2967 goto out;
2968 }
2969
2970 /*
2971 * Clip read size to a value which is supported by the target.
2972 */
2973 cbToRead = RT_MIN(cbToRead, pImage->cbRecvDataLength);
2974
2975 lba = uOffset / pImage->cbSector;
2976 tls = (uint16_t)(cbToRead / pImage->cbSector);
2977 SCSIREQ sr;
2978 uint8_t cdb[10];
2979 uint8_t sense[32];
2980
2981 cdb[0] = SCSI_READ_10;
2982 cdb[1] = 0; /* reserved */
2983 cdb[2] = (lba >> 24) & 0xff;
2984 cdb[3] = (lba >> 16) & 0xff;
2985 cdb[4] = (lba >> 8) & 0xff;
2986 cdb[5] = lba & 0xff;
2987 cdb[6] = 0; /* reserved */
2988 cdb[7] = (tls >> 8) & 0xff;
2989 cdb[8] = tls & 0xff;
2990 cdb[9] = 0; /* control */
2991
2992 sr.enmXfer = SCSIXFER_FROM_TARGET;
2993 sr.cbCmd = sizeof(cdb);
2994 sr.pvCmd = cdb;
2995 sr.cbI2TData = 0;
2996 sr.pcvI2TData = NULL;
2997 sr.cbT2IData = cbToRead;
2998 sr.pvT2IData = pvBuf;
2999 sr.cbSense = sizeof(sense);
3000 sr.pvSense = sense;
3001
3002 for (unsigned i = 0; i < 10; i++)
3003 {
3004 rc = iscsiCommand(pImage, &sr);
3005 if ( (RT_SUCCESS(rc) && !sr.cbSense)
3006 || RT_FAILURE(rc))
3007 break;
3008 rc = VERR_READ_ERROR;
3009 }
3010 if (RT_FAILURE(rc))
3011 {
3012 AssertMsgFailed(("iscsiCommand(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
3013 *pcbActuallyRead = 0;
3014 }
3015 else
3016 *pcbActuallyRead = sr.cbT2IData;
3017
3018out:
3019 LogFlowFunc(("returns %Rrc\n", rc));
3020 return rc;
3021}
3022
3023
3024/** @copydoc VBOXHDDBACKEND::pfnWrite */
3025static int iscsiWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
3026 size_t cbToWrite, size_t *pcbWriteProcess,
3027 size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
3028{
3029 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
3030 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3031 uint64_t lba;
3032 uint16_t tls;
3033 int rc;
3034
3035 Assert(pImage);
3036 Assert(uOffset % 512 == 0);
3037 Assert(cbToWrite % 512 == 0);
3038
3039 Assert(pImage->cbSector);
3040 Assert(pvBuf);
3041
3042 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
3043 {
3044 rc = VERR_VD_IMAGE_READ_ONLY;
3045 goto out;
3046 }
3047
3048 *pcbPreRead = 0;
3049 *pcbPostRead = 0;
3050
3051 /*
3052 * Clip write size to a value which is supported by the target.
3053 */
3054 cbToWrite = RT_MIN(cbToWrite, pImage->cbSendDataLength);
3055
3056 lba = uOffset / pImage->cbSector;
3057 tls = (uint16_t)(cbToWrite / pImage->cbSector);
3058 SCSIREQ sr;
3059 uint8_t cdb[10];
3060 uint8_t sense[32];
3061
3062 cdb[0] = SCSI_WRITE_10;
3063 cdb[1] = 0; /* reserved */
3064 cdb[2] = (lba >> 24) & 0xff;
3065 cdb[3] = (lba >> 16) & 0xff;
3066 cdb[4] = (lba >> 8) & 0xff;
3067 cdb[5] = lba & 0xff;
3068 cdb[6] = 0; /* reserved */
3069 cdb[7] = (tls >> 8) & 0xff;
3070 cdb[8] = tls & 0xff;
3071 cdb[9] = 0; /* control */
3072
3073 sr.enmXfer = SCSIXFER_TO_TARGET;
3074 sr.cbCmd = sizeof(cdb);
3075 sr.pvCmd = cdb;
3076 sr.cbI2TData = cbToWrite;
3077 sr.pcvI2TData = pvBuf;
3078 sr.cbT2IData = 0;
3079 sr.pvT2IData = NULL;
3080 sr.cbSense = sizeof(sense);
3081 sr.pvSense = sense;
3082
3083 for (unsigned i = 0; i < 10; i++)
3084 {
3085 rc = iscsiCommand(pImage, &sr);
3086 if ( (RT_SUCCESS(rc) && !sr.cbSense)
3087 || RT_FAILURE(rc))
3088 break;
3089 rc = VERR_WRITE_ERROR;
3090 }
3091 if (RT_FAILURE(rc))
3092 {
3093 AssertMsgFailed(("iscsiCommand(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
3094 *pcbWriteProcess = 0;
3095 }
3096 else
3097 *pcbWriteProcess = cbToWrite;
3098
3099out:
3100 LogFlowFunc(("returns %Rrc\n", rc));
3101 return rc;
3102}
3103
3104
3105/** @copydoc VBOXHDDBACKEND::pfnFlush */
3106static int iscsiFlush(void *pBackendData)
3107{
3108 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
3109 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3110 int rc;
3111
3112 Assert(pImage);
3113
3114 SCSIREQ sr;
3115 uint8_t cdb[10];
3116 uint8_t sense[32];
3117
3118 cdb[0] = SCSI_SYNCHRONIZE_CACHE;
3119 cdb[1] = 0; /* reserved */
3120 cdb[2] = 0; /* LBA 0 */
3121 cdb[3] = 0; /* LBA 0 */
3122 cdb[4] = 0; /* LBA 0 */
3123 cdb[5] = 0; /* LBA 0 */
3124 cdb[6] = 0; /* reserved */
3125 cdb[7] = 0; /* transfer everything to disk */
3126 cdb[8] = 0; /* transfer everything to disk */
3127 cdb[9] = 0; /* control */
3128
3129 sr.enmXfer = SCSIXFER_TO_TARGET;
3130 sr.cbCmd = sizeof(cdb);
3131 sr.pvCmd = cdb;
3132 sr.cbI2TData = 0;
3133 sr.pcvI2TData = NULL;
3134 sr.cbT2IData = 0;
3135 sr.pvT2IData = NULL;
3136 sr.cbSense = sizeof(sense);
3137 sr.pvSense = sense;
3138
3139 rc = iscsiCommand(pImage, &sr);
3140 if (RT_FAILURE(rc))
3141 AssertMsgFailed(("iscsiCommand(%s) -> %Rrc\n", pImage->pszTargetName, rc));
3142 LogFlowFunc(("returns %Rrc\n", rc));
3143 return rc;
3144}
3145
3146
3147/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
3148static unsigned iscsiGetVersion(void *pBackendData)
3149{
3150 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
3151 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3152
3153 Assert(pImage);
3154 NOREF(pImage);
3155
3156 return 0;
3157}
3158
3159
3160/** @copydoc VBOXHDDBACKEND::pfnGetSize */
3161static uint64_t iscsiGetSize(void *pBackendData)
3162{
3163 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
3164 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3165
3166 Assert(pImage);
3167
3168 if (pImage)
3169 return pImage->cbSize;
3170 else
3171 return 0;
3172}
3173
3174
3175/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
3176static uint64_t iscsiGetFileSize(void *pBackendData)
3177{
3178 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
3179 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3180
3181 Assert(pImage);
3182 NOREF(pImage);
3183
3184 if (pImage)
3185 return pImage->cbSize;
3186 else
3187 return 0;
3188}
3189
3190
3191/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
3192static int iscsiGetPCHSGeometry(void *pBackendData,
3193 PPDMMEDIAGEOMETRY pPCHSGeometry)
3194{
3195 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
3196 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3197 int rc;
3198
3199 Assert(pImage);
3200
3201 if (pImage)
3202 rc = VERR_VD_GEOMETRY_NOT_SET;
3203 else
3204 rc = VERR_VD_NOT_OPENED;
3205
3206 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
3207 return rc;
3208}
3209
3210
3211/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
3212static int iscsiSetPCHSGeometry(void *pBackendData,
3213 PCPDMMEDIAGEOMETRY pPCHSGeometry)
3214{
3215 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
3216 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3217 int rc;
3218
3219 Assert(pImage);
3220
3221 if (pImage)
3222 {
3223 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
3224 {
3225 rc = VERR_VD_IMAGE_READ_ONLY;
3226 goto out;
3227 }
3228 rc = VERR_VD_GEOMETRY_NOT_SET;
3229 }
3230 else
3231 rc = VERR_VD_NOT_OPENED;
3232
3233out:
3234 LogFlowFunc(("returns %Rrc\n", rc));
3235 return rc;
3236}
3237
3238
3239/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
3240static int iscsiGetLCHSGeometry(void *pBackendData,
3241 PPDMMEDIAGEOMETRY pLCHSGeometry)
3242{
3243 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
3244 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3245 int rc;
3246
3247 Assert(pImage);
3248
3249 if (pImage)
3250 rc = VERR_VD_GEOMETRY_NOT_SET;
3251 else
3252 rc = VERR_VD_NOT_OPENED;
3253
3254 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
3255 return rc;
3256}
3257
3258
3259/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
3260static unsigned iscsiGetImageFlags(void *pBackendData)
3261{
3262 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
3263 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3264 unsigned uImageFlags;
3265
3266 Assert(pImage);
3267 NOREF(pImage);
3268
3269 uImageFlags = VD_IMAGE_FLAGS_FIXED;
3270
3271 LogFlowFunc(("returns %#x\n", uImageFlags));
3272 return uImageFlags;
3273}
3274
3275
3276/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
3277static unsigned iscsiGetOpenFlags(void *pBackendData)
3278{
3279 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
3280 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3281 unsigned uOpenFlags;
3282
3283 Assert(pImage);
3284
3285 if (pImage)
3286 uOpenFlags = pImage->uOpenFlags;
3287 else
3288 uOpenFlags = 0;
3289
3290 LogFlowFunc(("returns %#x\n", uOpenFlags));
3291 return uOpenFlags;
3292}
3293
3294
3295/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
3296static int iscsiSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
3297{
3298 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
3299 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3300 int rc;
3301
3302 /* Image must be opened and the new flags must be valid. Just readonly and
3303 * info flags are supported. */
3304 if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO)))
3305 {
3306 rc = VERR_INVALID_PARAMETER;
3307 goto out;
3308 }
3309
3310 /* Implement this operation via reopening the image. */
3311 iscsiFreeImage(pImage, false);
3312 rc = iscsiOpenImage(pImage, uOpenFlags);
3313
3314out:
3315 LogFlowFunc(("returns %Rrc\n", rc));
3316 return rc;
3317}
3318
3319
3320/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
3321static int iscsiSetLCHSGeometry(void *pBackendData,
3322 PCPDMMEDIAGEOMETRY pLCHSGeometry)
3323{
3324 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
3325 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3326 int rc;
3327
3328 Assert(pImage);
3329
3330 if (pImage)
3331 {
3332 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
3333 {
3334 rc = VERR_VD_IMAGE_READ_ONLY;
3335 goto out;
3336 }
3337 rc = VERR_VD_GEOMETRY_NOT_SET;
3338 }
3339 else
3340 rc = VERR_VD_NOT_OPENED;
3341
3342out:
3343 LogFlowFunc(("returns %Rrc\n", rc));
3344 return rc;
3345}
3346
3347
3348/** @copydoc VBOXHDDBACKEND::pfnGetComment */
3349static int iscsiGetComment(void *pBackendData, char *pszComment,
3350 size_t cbComment)
3351{
3352 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
3353 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3354 int rc;
3355
3356 Assert(pImage);
3357
3358 if (pImage)
3359 rc = VERR_NOT_SUPPORTED;
3360 else
3361 rc = VERR_VD_NOT_OPENED;
3362
3363 LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
3364 return rc;
3365}
3366
3367
3368/** @copydoc VBOXHDDBACKEND::pfnSetComment */
3369static int iscsiSetComment(void *pBackendData, const char *pszComment)
3370{
3371 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
3372 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3373 int rc;
3374
3375 Assert(pImage);
3376
3377 if (pImage)
3378 {
3379 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
3380 rc = VERR_NOT_SUPPORTED;
3381 else
3382 rc = VERR_VD_IMAGE_READ_ONLY;
3383 }
3384 else
3385 rc = VERR_VD_NOT_OPENED;
3386
3387 LogFlowFunc(("returns %Rrc\n", rc));
3388 return rc;
3389}
3390
3391
3392/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
3393static int iscsiGetUuid(void *pBackendData, PRTUUID pUuid)
3394{
3395 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
3396 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3397 int rc;
3398
3399 Assert(pImage);
3400
3401 if (pImage)
3402 rc = VERR_NOT_SUPPORTED;
3403 else
3404 rc = VERR_VD_NOT_OPENED;
3405
3406 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
3407 return rc;
3408}
3409
3410
3411/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
3412static int iscsiSetUuid(void *pBackendData, PCRTUUID pUuid)
3413{
3414 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
3415 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3416 int rc;
3417
3418 LogFlowFunc(("%RTuuid\n", pUuid));
3419 Assert(pImage);
3420
3421 if (pImage)
3422 {
3423 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
3424 rc = VERR_NOT_SUPPORTED;
3425 else
3426 rc = VERR_VD_IMAGE_READ_ONLY;
3427 }
3428 else
3429 rc = VERR_VD_NOT_OPENED;
3430
3431 LogFlowFunc(("returns %Rrc\n", rc));
3432 return rc;
3433}
3434
3435
3436/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
3437static int iscsiGetModificationUuid(void *pBackendData, PRTUUID pUuid)
3438{
3439 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
3440 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3441 int rc;
3442
3443 Assert(pImage);
3444
3445 if (pImage)
3446 rc = VERR_NOT_SUPPORTED;
3447 else
3448 rc = VERR_VD_NOT_OPENED;
3449
3450 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
3451 return rc;
3452}
3453
3454
3455/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
3456static int iscsiSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
3457{
3458 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
3459 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3460 int rc;
3461
3462 LogFlowFunc(("%RTuuid\n", pUuid));
3463 Assert(pImage);
3464
3465 if (pImage)
3466 {
3467 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
3468 rc = VERR_NOT_SUPPORTED;
3469 else
3470 rc = VERR_VD_IMAGE_READ_ONLY;
3471 }
3472 else
3473 rc = VERR_VD_NOT_OPENED;
3474
3475 LogFlowFunc(("returns %Rrc\n", rc));
3476 return rc;
3477}
3478
3479
3480/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
3481static int iscsiGetParentUuid(void *pBackendData, PRTUUID pUuid)
3482{
3483 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
3484 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3485 int rc;
3486
3487 Assert(pImage);
3488
3489 if (pImage)
3490 rc = VERR_NOT_SUPPORTED;
3491 else
3492 rc = VERR_VD_NOT_OPENED;
3493
3494 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
3495 return rc;
3496}
3497
3498
3499/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
3500static int iscsiSetParentUuid(void *pBackendData, PCRTUUID pUuid)
3501{
3502 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
3503 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3504 int rc;
3505
3506 LogFlowFunc(("%RTuuid\n", pUuid));
3507 Assert(pImage);
3508
3509 if (pImage)
3510 {
3511 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
3512 rc = VERR_NOT_SUPPORTED;
3513 else
3514 rc = VERR_VD_IMAGE_READ_ONLY;
3515 }
3516 else
3517 rc = VERR_VD_NOT_OPENED;
3518
3519 LogFlowFunc(("returns %Rrc\n", rc));
3520 return rc;
3521}
3522
3523
3524/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
3525static int iscsiGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
3526{
3527 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
3528 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3529 int rc;
3530
3531 Assert(pImage);
3532
3533 if (pImage)
3534 rc = VERR_NOT_SUPPORTED;
3535 else
3536 rc = VERR_VD_NOT_OPENED;
3537
3538 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
3539 return rc;
3540}
3541
3542
3543/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
3544static int iscsiSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
3545{
3546 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
3547 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3548 int rc;
3549
3550 LogFlowFunc(("%RTuuid\n", pUuid));
3551 Assert(pImage);
3552
3553 if (pImage)
3554 {
3555 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
3556 rc = VERR_NOT_SUPPORTED;
3557 else
3558 rc = VERR_VD_IMAGE_READ_ONLY;
3559 }
3560 else
3561 rc = VERR_VD_NOT_OPENED;
3562
3563 LogFlowFunc(("returns %Rrc\n", rc));
3564 return rc;
3565}
3566
3567
3568/** @copydoc VBOXHDDBACKEND::pfnDump */
3569static void iscsiDump(void *pBackendData)
3570{
3571 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3572
3573 Assert(pImage);
3574 if (pImage)
3575 {
3576 /** @todo put something useful here */
3577 pImage->pInterfaceErrorCallbacks->pfnMessage(pImage->pInterfaceError->pvUser, "Header: cVolume=%u\n", pImage->cVolume);
3578 }
3579}
3580
3581
3582/** @copydoc VBOXHDDBACKEND::pfnGetTimeStamp */
3583static int iscsiGetTimeStamp(void *pBackendData, PRTTIMESPEC pTimeStamp)
3584{
3585 LogFlowFunc(("pBackendData=%#p pTimeStamp=%#p\n", pBackendData, pTimeStamp));
3586 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3587 int rc = VERR_NOT_SUPPORTED;
3588
3589 Assert(pImage);
3590 NOREF(pImage);
3591
3592 LogFlowFunc(("returns %Rrc\n", rc));
3593 return rc;
3594}
3595
3596
3597/** @copydoc VBOXHDDBACKEND::pfnGetParentTimeStamp */
3598static int iscsiGetParentTimeStamp(void *pBackendData, PRTTIMESPEC pTimeStamp)
3599{
3600 LogFlowFunc(("pBackendData=%#p pTimeStamp=%#p\n", pBackendData, pTimeStamp));
3601 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3602 int rc = VERR_NOT_SUPPORTED;
3603
3604 Assert(pImage);
3605 NOREF(pImage);
3606
3607 LogFlowFunc(("returns %Rrc\n", rc));
3608 return rc;
3609}
3610
3611
3612/** @copydoc VBOXHDDBACKEND::pfnSetParentTimeStamp */
3613static int iscsiSetParentTimeStamp(void *pBackendData, PCRTTIMESPEC pTimeStamp)
3614{
3615 LogFlowFunc(("pBackendData=%#p pTimeStamp=%#p\n", pBackendData, pTimeStamp));
3616 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3617 int rc = VERR_NOT_SUPPORTED;
3618
3619 Assert(pImage);
3620 NOREF(pImage);
3621
3622 LogFlowFunc(("returns %Rrc\n", rc));
3623 return rc;
3624}
3625
3626
3627/** @copydoc VBOXHDDBACKEND::pfnGetParentFilename */
3628static int iscsiGetParentFilename(void *pBackendData, char **ppszParentFilename)
3629{
3630 LogFlowFunc(("pBackendData=%#p ppszParentFilename=%#p\n", pBackendData, ppszParentFilename));
3631 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3632 int rc = VERR_NOT_SUPPORTED;
3633
3634 Assert(pImage);
3635 NOREF(pImage);
3636
3637 LogFlowFunc(("returns %Rrc\n", rc));
3638 return rc;
3639}
3640
3641
3642/** @copydoc VBOXHDDBACKEND::pfnSetParentFilename */
3643static int iscsiSetParentFilename(void *pBackendData, const char *pszParentFilename)
3644{
3645 LogFlowFunc(("pBackendData=%#p pszParentFilename=%s\n", pBackendData, pszParentFilename));
3646 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3647 int rc = VERR_NOT_SUPPORTED;
3648
3649 Assert(pImage);
3650 NOREF(pImage);
3651
3652 LogFlowFunc(("returns %Rrc\n", rc));
3653 return rc;
3654}
3655
3656/** @copydoc VBOXHDDBACKEND::pfnComposeLocation */
3657static int iscsiComposeLocation(PVDINTERFACE pConfig, char **pszLocation)
3658{
3659 char *pszTarget = NULL;
3660 char *pszLUN = NULL;
3661 char *pszAddress = NULL;
3662 int rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetName", &pszTarget);
3663 if (RT_SUCCESS(rc))
3664 {
3665 rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "LUN", &pszLUN);
3666 if (RT_SUCCESS(rc))
3667 {
3668 rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetAddress", &pszAddress);
3669 if (RT_SUCCESS(rc))
3670 {
3671 if (RTStrAPrintf(pszLocation, "iscsi://%s/%s/%s",
3672 pszAddress, pszTarget, pszLUN) < 0)
3673 rc = VERR_NO_MEMORY;
3674 }
3675 }
3676 }
3677 RTMemFree(pszTarget);
3678 RTMemFree(pszLUN);
3679 RTMemFree(pszAddress);
3680 return rc;
3681}
3682
3683/** @copydoc VBOXHDDBACKEND::pfnComposeName */
3684static int iscsiComposeName(PVDINTERFACE pConfig, char **pszName)
3685{
3686 char *pszTarget = NULL;
3687 char *pszLUN = NULL;
3688 char *pszAddress = NULL;
3689 int rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetName", &pszTarget);
3690 if (RT_SUCCESS(rc))
3691 {
3692 rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "LUN", &pszLUN);
3693 if (RT_SUCCESS(rc))
3694 {
3695 rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetAddress", &pszAddress);
3696 if (RT_SUCCESS(rc))
3697 {
3698 /** @todo think about a nicer looking location scheme for iSCSI */
3699 if (RTStrAPrintf(pszName, "%s/%s/%s",
3700 pszAddress, pszTarget, pszLUN) < 0)
3701 rc = VERR_NO_MEMORY;
3702 }
3703 }
3704 }
3705 RTMemFree(pszTarget);
3706 RTMemFree(pszLUN);
3707 RTMemFree(pszAddress);
3708
3709 return rc;
3710}
3711
3712
3713VBOXHDDBACKEND g_ISCSIBackend =
3714{
3715 /* pszBackendName */
3716 "iSCSI",
3717 /* cbSize */
3718 sizeof(VBOXHDDBACKEND),
3719 /* uBackendCaps */
3720 VD_CAP_CONFIG | VD_CAP_TCPNET,
3721 /* papszFileExtensions */
3722 NULL,
3723 /* paConfigInfo */
3724 s_iscsiConfigInfo,
3725 /* hPlugin */
3726 NIL_RTLDRMOD,
3727 /* pfnCheckIfValid */
3728 iscsiCheckIfValid,
3729 /* pfnOpen */
3730 iscsiOpen,
3731 /* pfnCreate */
3732 iscsiCreate,
3733 /* pfnRename */
3734 iscsiRename,
3735 /* pfnClose */
3736 iscsiClose,
3737 /* pfnRead */
3738 iscsiRead,
3739 /* pfnWrite */
3740 iscsiWrite,
3741 /* pfnFlush */
3742 iscsiFlush,
3743 /* pfnGetVersion */
3744 iscsiGetVersion,
3745 /* pfnGetSize */
3746 iscsiGetSize,
3747 /* pfnGetFileSize */
3748 iscsiGetFileSize,
3749 /* pfnGetPCHSGeometry */
3750 iscsiGetPCHSGeometry,
3751 /* pfnSetPCHSGeometry */
3752 iscsiSetPCHSGeometry,
3753 /* pfnGetLCHSGeometry */
3754 iscsiGetLCHSGeometry,
3755 /* pfnSetLCHSGeometry */
3756 iscsiSetLCHSGeometry,
3757 /* pfnGetImageFlags */
3758 iscsiGetImageFlags,
3759 /* pfnGetOpenFlags */
3760 iscsiGetOpenFlags,
3761 /* pfnSetOpenFlags */
3762 iscsiSetOpenFlags,
3763 /* pfnGetComment */
3764 iscsiGetComment,
3765 /* pfnSetComment */
3766 iscsiSetComment,
3767 /* pfnGetUuid */
3768 iscsiGetUuid,
3769 /* pfnSetUuid */
3770 iscsiSetUuid,
3771 /* pfnGetModificationUuid */
3772 iscsiGetModificationUuid,
3773 /* pfnSetModificationUuid */
3774 iscsiSetModificationUuid,
3775 /* pfnGetParentUuid */
3776 iscsiGetParentUuid,
3777 /* pfnSetParentUuid */
3778 iscsiSetParentUuid,
3779 /* pfnGetParentModificationUuid */
3780 iscsiGetParentModificationUuid,
3781 /* pfnSetParentModificationUuid */
3782 iscsiSetParentModificationUuid,
3783 /* pfnDump */
3784 iscsiDump,
3785 /* pfnGetTimeStamp */
3786 iscsiGetTimeStamp,
3787 /* pfnGetParentTimeStamp */
3788 iscsiGetParentTimeStamp,
3789 /* pfnSetParentTimeStamp */
3790 iscsiSetParentTimeStamp,
3791 /* pfnGetParentFilename */
3792 iscsiGetParentFilename,
3793 /* pfnSetParentFilename */
3794 iscsiSetParentFilename,
3795 /* pfnIsAsyncIOSupported */
3796 NULL,
3797 /* pfnAsyncRead */
3798 NULL,
3799 /* pfnAsyncWrite */
3800 NULL,
3801 /* pfnComposeLocation */
3802 iscsiComposeLocation,
3803 /* pfnComposeName */
3804 iscsiComposeName,
3805 /* pfnCompact */
3806 NULL
3807};
Note: See TracBrowser for help on using the repository browser.

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