VirtualBox

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

Last change on this file since 48935 was 48851, checked in by vboxsync, 11 years ago

Storage: Addressing 64-bit windows warnings.

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

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