VirtualBox

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

Last change on this file since 52902 was 51105, checked in by vboxsync, 11 years ago

Missing files for previous commit

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