VirtualBox

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

Last change on this file since 50653 was 50304, checked in by vboxsync, 10 years ago

iSCSI: Add option to dump malformed response packets

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

© 2023 Oracle
ContactPrivacy policyTerms of Use