VirtualBox

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

Last change on this file since 97441 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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