VirtualBox

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

Last change on this file since 86327 was 85121, checked in by vboxsync, 4 years ago

iprt/cdefs.h: Refactored the typedef use of DECLCALLBACK as well as DECLCALLBACKMEMBER to wrap the whole expression, similar to the DECLR?CALLBACKMEMBER macros. This allows adding a throw() at the end when compiling with the VC++ compiler to indicate that the callbacks won't throw anything, so we can stop supressing the C5039 warning about passing functions that can potential throw C++ exceptions to extern C code that can't necessarily cope with such (unwind,++). Introduced a few _EX variations that allows specifying different/no calling convention too, as that's handy when dynamically resolving host APIs. Fixed numerous places missing DECLCALLBACK and such. Left two angry @todos regarding use of CreateThread. bugref:9794

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