VirtualBox

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

最後變更 在這個檔案從26960是 26960,由 vboxsync 提交於 15 年 前

whitespace

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

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