VirtualBox

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

Last change on this file since 107464 was 107426, checked in by vboxsync, 13 days ago

Storage/ISCSI.cpp: Fix unused assignment parfait warning, transit is always reset to false at the beginning of the loop, bugref:3409

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