VirtualBox

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

Last change on this file since 103131 was 99739, checked in by vboxsync, 16 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 199.7 KB
RevLine 
[25981]1/* $Id: ISCSI.cpp 99739 2023-05-11 01:01:08Z vboxsync $ */
[17796]2/** @file
3 * iSCSI initiator driver, VD backend.
4 */
5
6/*
[98103]7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
[17796]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
[17796]26 */
27
28
[57358]29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
[17796]32#define LOG_GROUP LOG_GROUP_VD_ISCSI
[33567]33#include <VBox/vd-plugin.h>
[17796]34#include <VBox/err.h>
35
36#include <VBox/log.h>
37#include <iprt/alloc.h>
38#include <iprt/assert.h>
39#include <iprt/uuid.h>
40#include <iprt/string.h>
41#include <iprt/asm.h>
42#include <iprt/thread.h>
43#include <iprt/semaphore.h>
44#include <iprt/md5.h>
45#include <iprt/tcp.h>
46#include <iprt/time.h>
47#include <VBox/scsi.h>
48
[50988]49#include "VDBackends.h"
[66494]50#include "VDBackendsInline.h"
[17796]51
52
[57358]53/*********************************************************************************************************************************
54* Defined Constants And Macros *
55*********************************************************************************************************************************/
56
[36230]57/** The maximum number of release log entries per image. */
58#define MAX_LOG_REL_ERRORS 1024
59
[17796]60/** Default port number to use for iSCSI. */
61#define ISCSI_DEFAULT_PORT 3260
62
63
64/** Converts a number in the range of 0 - 15 into the corresponding hex char. */
65#define NUM_2_HEX(b) ('0' + (b) + (((b) > 9) ? 39 : 0))
66/** Converts a hex char into the corresponding number in the range 0-15. */
67#define HEX_2_NUM(c) (((c) <= '9') ? ((c) - '0') : (((c - 'A' + 10) & 0xf)))
68/* Converts a base64 char into the corresponding number in the range 0-63. */
69#define B64_2_NUM(c) ((c >= 'A' && c <= 'Z') ? (c - 'A') : (c >= 'a' && c <= 'z') ? (c - 'a' + 26) : (c >= '0' && c <= '9') ? (c - '0' + 52) : (c == '+') ? 62 : (c == '/') ? 63 : -1)
70
71
[33540]72/** Minimum CHAP_MD5 challenge length in bytes. */
[17796]73#define CHAP_MD5_CHALLENGE_MIN 16
74/** Maximum CHAP_MD5 challenge length in bytes. */
75#define CHAP_MD5_CHALLENGE_MAX 24
76
77
78/**
79 * SCSI peripheral device type. */
80typedef enum SCSIDEVTYPE
81{
82 /** direct-access device. */
83 SCSI_DEVTYPE_DISK = 0,
84 /** sequential-access device. */
85 SCSI_DEVTYPE_TAPE,
86 /** printer device. */
87 SCSI_DEVTYPE_PRINTER,
88 /** processor device. */
89 SCSI_DEVTYPE_PROCESSOR,
90 /** write-once device. */
91 SCSI_DEVTYPE_WORM,
92 /** CD/DVD device. */
93 SCSI_DEVTYPE_CDROM,
94 /** scanner device. */
95 SCSI_DEVTYPE_SCANNER,
96 /** optical memory device. */
97 SCSI_DEVTYPE_OPTICAL,
98 /** medium changer. */
99 SCSI_DEVTYPE_CHANGER,
100 /** communications device. */
101 SCSI_DEVTYPE_COMMUNICATION,
102 /** storage array controller device. */
103 SCSI_DEVTYPE_RAIDCTL = 0x0c,
104 /** enclosure services device. */
105 SCSI_DEVTYPE_ENCLOSURE,
106 /** simplified direct-access device. */
107 SCSI_DEVTYPE_SIMPLEDISK,
108 /** optical card reader/writer device. */
109 SCSI_DEVTYPE_OCRW,
110 /** bridge controller device. */
111 SCSI_DEVTYPE_BRIDGE,
112 /** object-based storage device. */
113 SCSI_DEVTYPE_OSD
114} SCSIDEVTYPE;
115
116/** Mask for extracting the SCSI device type out of the first byte of the INQUIRY response. */
117#define SCSI_DEVTYPE_MASK 0x1f
118
[33540]119/** Mask to extract the CmdQue bit out of the seventh byte of the INQUIRY response. */
[31714]120#define SCSI_INQUIRY_CMDQUE_MASK 0x02
[17796]121
[23594]122/** Maximum PDU payload size we can handle in one piece. Greater or equal than
123 * s_iscsiConfigDefaultWriteSplit. */
[22256]124#define ISCSI_DATA_LENGTH_MAX _256K
[21839]125
[17796]126/** Maximum PDU size we can handle in one piece. */
[22256]127#define ISCSI_RECV_PDU_BUFFER_SIZE (ISCSI_DATA_LENGTH_MAX + ISCSI_BHS_SIZE)
[17796]128
129
130/** Version of the iSCSI standard which this initiator driver can handle. */
131#define ISCSI_MY_VERSION 0
132
133
134/** Length of ISCSI basic header segment. */
135#define ISCSI_BHS_SIZE 48
136
137
138/** Reserved task tag value. */
139#define ISCSI_TASK_TAG_RSVD 0xffffffff
140
141
142/**
143 * iSCSI opcodes. */
144typedef enum ISCSIOPCODE
145{
146 /** NOP-Out. */
147 ISCSIOP_NOP_OUT = 0x00000000,
148 /** SCSI command. */
149 ISCSIOP_SCSI_CMD = 0x01000000,
150 /** SCSI task management request. */
151 ISCSIOP_SCSI_TASKMGMT_REQ = 0x02000000,
152 /** Login request. */
153 ISCSIOP_LOGIN_REQ = 0x03000000,
154 /** Text request. */
155 ISCSIOP_TEXT_REQ = 0x04000000,
156 /** SCSI Data-Out. */
157 ISCSIOP_SCSI_DATA_OUT = 0x05000000,
158 /** Logout request. */
159 ISCSIOP_LOGOUT_REQ = 0x06000000,
160 /** SNACK request. */
161 ISCSIOP_SNACK_REQ = 0x10000000,
162
163 /** NOP-In. */
164 ISCSIOP_NOP_IN = 0x20000000,
165 /** SCSI response. */
166 ISCSIOP_SCSI_RES = 0x21000000,
167 /** SCSI Task Management response. */
168 ISCSIOP_SCSI_TASKMGMT_RES = 0x22000000,
169 /** Login response. */
170 ISCSIOP_LOGIN_RES = 0x23000000,
171 /** Text response. */
172 ISCSIOP_TEXT_RES = 0x24000000,
173 /** SCSI Data-In. */
174 ISCSIOP_SCSI_DATA_IN = 0x25000000,
175 /** Logout response. */
176 ISCSIOP_LOGOUT_RES = 0x26000000,
177 /** Ready To Transfer (R2T). */
178 ISCSIOP_R2T = 0x31000000,
179 /** Asynchronous message. */
180 ISCSIOP_ASYN_MSG = 0x32000000,
181 /** Reject. */
182 ISCSIOP_REJECT = 0x3f000000
183} ISCSIOPCODE;
184
185/** Mask for extracting the iSCSI opcode out of the first header word. */
186#define ISCSIOP_MASK 0x3f000000
187
188
189/** ISCSI BHS word 0: Request should be processed immediately. */
190#define ISCSI_IMMEDIATE_DELIVERY_BIT 0x40000000
191
192/** ISCSI BHS word 0: This is the final PDU for this request/response. */
193#define ISCSI_FINAL_BIT 0x00800000
194/** ISCSI BHS word 0: Mask for extracting the CSG. */
195#define ISCSI_CSG_MASK 0x000c0000
196/** ISCSI BHS word 0: Shift offset for extracting the CSG. */
197#define ISCSI_CSG_SHIFT 18
198/** ISCSI BHS word 0: Mask for extracting the NSG. */
199#define ISCSI_NSG_MASK 0x00030000
200/** ISCSI BHS word 0: Shift offset for extracting the NSG. */
201#define ISCSI_NSG_SHIFT 16
202
203/** ISCSI BHS word 0: task attribute untagged */
204#define ISCSI_TASK_ATTR_UNTAGGED 0x00000000
205/** ISCSI BHS word 0: task attribute simple */
206#define ISCSI_TASK_ATTR_SIMPLE 0x00010000
207/** ISCSI BHS word 0: task attribute ordered */
208#define ISCSI_TASK_ATTR_ORDERED 0x00020000
209/** ISCSI BHS word 0: task attribute head of queue */
210#define ISCSI_TASK_ATTR_HOQ 0x00030000
211/** ISCSI BHS word 0: task attribute ACA */
212#define ISCSI_TASK_ATTR_ACA 0x00040000
213
214/** ISCSI BHS word 0: transit to next login phase. */
215#define ISCSI_TRANSIT_BIT 0x00800000
216/** ISCSI BHS word 0: continue with login negotiation. */
217#define ISCSI_CONTINUE_BIT 0x00400000
218
219/** ISCSI BHS word 0: residual underflow. */
220#define ISCSI_RESIDUAL_UNFL_BIT 0x00020000
221/** ISCSI BHS word 0: residual overflow. */
222#define ISCSI_RESIDUAL_OVFL_BIT 0x00040000
223/** ISCSI BHS word 0: Bidirectional read residual underflow. */
224#define ISCSI_BI_READ_RESIDUAL_UNFL_BIT 0x00080000
225/** ISCSI BHS word 0: Bidirectional read residual overflow. */
226#define ISCSI_BI_READ_RESIDUAL_OVFL_BIT 0x00100000
227
228/** ISCSI BHS word 0: SCSI response mask. */
229#define ISCSI_SCSI_RESPONSE_MASK 0x0000ff00
230/** ISCSI BHS word 0: SCSI status mask. */
231#define ISCSI_SCSI_STATUS_MASK 0x000000ff
232
233/** ISCSI BHS word 0: response includes status. */
234#define ISCSI_STATUS_BIT 0x00010000
235
[30309]236/** Maximum number of scatter/gather segments needed to send a PDU. */
237#define ISCSI_SG_SEGMENTS_MAX 4
[17796]238
[31587]239/** Number of entries in the command table. */
240#define ISCSI_CMD_WAITING_ENTRIES 32
[30309]241
[17796]242/**
243 * iSCSI login status class. */
244typedef enum ISCSILOGINSTATUSCLASS
245{
246 /** Success. */
247 ISCSI_LOGIN_STATUS_CLASS_SUCCESS = 0,
248 /** Redirection. */
249 ISCSI_LOGIN_STATUS_CLASS_REDIRECTION,
250 /** Initiator error. */
251 ISCSI_LOGIN_STATUS_CLASS_INITIATOR_ERROR,
252 /** Target error. */
253 ISCSI_LOGIN_STATUS_CLASS_TARGET_ERROR
254} ISCSILOGINSTATUSCLASS;
255
256
257/**
258 * iSCSI connection state. */
259typedef enum ISCSISTATE
260{
261 /** Not having a connection/session at all. */
262 ISCSISTATE_FREE,
263 /** Currently trying to login. */
264 ISCSISTATE_IN_LOGIN,
265 /** Normal operation, corresponds roughly to the Full Feature Phase. */
266 ISCSISTATE_NORMAL,
267 /** Currently trying to logout. */
268 ISCSISTATE_IN_LOGOUT
269} ISCSISTATE;
270
[30941]271/**
[44361]272 * iSCSI PDU send/receive flags (and maybe more in the future). */
[30941]273typedef enum ISCSIPDUFLAGS
274{
275 /** No special flags */
276 ISCSIPDU_DEFAULT = 0,
277 /** Do not attempt to re-attach to the target if the connection is lost */
278 ISCSIPDU_NO_REATTACH = RT_BIT(1)
279} ISCSIPDUFLAGS;
[17796]280
[30941]281
[57358]282/*********************************************************************************************************************************
283* Structures and Typedefs *
284*********************************************************************************************************************************/
[31098]285
[17796]286/**
[31098]287 * iSCSI login negotiation parameter
288 */
289typedef struct ISCSIPARAMETER
290{
291 /** Name of the parameter. */
292 const char *pszParamName;
293 /** Value of the parameter. */
294 const char *pszParamValue;
295 /** Length of the binary parameter. 0=zero-terminated string. */
296 size_t cbParamValue;
297} ISCSIPARAMETER;
298
299
300/**
301 * iSCSI Response PDU buffer (scatter).
302 */
303typedef struct ISCSIRES
304{
305 /** Length of PDU segment. */
306 size_t cbSeg;
307 /** Pointer to PDU segment. */
308 void *pvSeg;
309} ISCSIRES;
310/** Pointer to an iSCSI Response PDU buffer. */
311typedef ISCSIRES *PISCSIRES;
312/** Pointer to a const iSCSI Response PDU buffer. */
313typedef ISCSIRES const *PCISCSIRES;
314
315
316/**
[25981]317 * iSCSI Request PDU buffer (gather).
318 */
319typedef struct ISCSIREQ
320{
321 /** Length of PDU segment in bytes. */
322 size_t cbSeg;
323 /** Pointer to PDU segment. */
324 const void *pcvSeg;
325} ISCSIREQ;
326/** Pointer to an iSCSI Request PDU buffer. */
327typedef ISCSIREQ *PISCSIREQ;
328/** Pointer to a const iSCSI Request PDU buffer. */
329typedef ISCSIREQ const *PCISCSIREQ;
330
331
332/**
[31098]333 * SCSI transfer directions.
334 */
335typedef enum SCSIXFER
336{
337 SCSIXFER_NONE = 0,
338 SCSIXFER_TO_TARGET,
339 SCSIXFER_FROM_TARGET,
340 SCSIXFER_TO_FROM_TARGET
341} SCSIXFER, *PSCSIXFER;
342
[31587]343/** Forward declaration. */
344typedef struct ISCSIIMAGE *PISCSIIMAGE;
[31098]345
346/**
347 * SCSI request structure.
348 */
349typedef struct SCSIREQ
350{
[44256]351 /** I/O context associated with this request. */
352 PVDIOCTX pIoCtx;
[31098]353 /** Transfer direction. */
354 SCSIXFER enmXfer;
355 /** Length of command block. */
[32536]356 size_t cbCDB;
[31098]357 /** Length of Initiator2Target data buffer. */
358 size_t cbI2TData;
359 /** Length of Target2Initiator data buffer. */
360 size_t cbT2IData;
[31608]361 /** Length of sense buffer
362 * This contains the number of sense bytes received upon completion. */
[31098]363 size_t cbSense;
364 /** Completion status of the command. */
365 uint8_t status;
[44256]366 /** The CDB. */
367 uint8_t abCDB[16];
368 /** The sense buffer. */
369 uint8_t abSense[96];
370 /** Status code to return if we got sense data. */
371 int rcSense;
[31587]372 /** Pointer to the Initiator2Target S/G list. */
373 PRTSGSEG paI2TSegs;
374 /** Number of entries in the I2T S/G list. */
375 unsigned cI2TSegs;
376 /** Pointer to the Target2Initiator S/G list. */
377 PRTSGSEG paT2ISegs;
378 /** Number of entries in the T2I S/G list. */
379 unsigned cT2ISegs;
380 /** S/G buffer for the target to initiator bits. */
381 RTSGBUF SgBufT2I;
[31608]382 /** Number of retries if the command completes with sense
383 * data before we return with an error.
384 */
385 unsigned cSenseRetries;
[31587]386 /** The S/G list - variable in size.
387 * This array holds both the I2T and T2I segments.
388 * The I2T segments are first and the T2I are second.
389 */
390 RTSGSEG aSegs[1];
[44256]391} SCSIREQ, *PSCSIREQ;
[31098]392
393typedef enum ISCSICMDTYPE
394{
395 /** Process a SCSI request. */
396 ISCSICMDTYPE_REQ = 0,
397 /** Call a function in the I/O thread. */
398 ISCSICMDTYPE_EXEC,
399 /** Usual 32bit hack. */
400 ISCSICMDTYPE_32BIT_HACK = 0x7fffffff
401} ISCSICMDTYPE;
402
403
404/** The command completion function. */
[85121]405typedef DECLCALLBACKTYPE(void, FNISCSICMDCOMPLETED,(PISCSIIMAGE pImage, int rcReq, void *pvUser));
[31098]406/** Pointer to a command completion function. */
407typedef FNISCSICMDCOMPLETED *PFNISCSICMDCOMPLETED;
408
409/** The command execution function. */
[85121]410typedef DECLCALLBACKTYPE(int, FNISCSIEXEC,(void *pvUser));
[31098]411/** Pointer to a command execution function. */
412typedef FNISCSIEXEC *PFNISCSIEXEC;
413
414/**
[31587]415 * Structure used to complete a synchronous request.
416 */
417typedef struct ISCSICMDSYNC
418{
[33540]419 /** Event semaphore to wakeup the waiting thread. */
[31587]420 RTSEMEVENT EventSem;
421 /** Status code of the command. */
422 int rcCmd;
423} ISCSICMDSYNC, *PISCSICMDSYNC;
424
425/**
[31098]426 * iSCSI command.
427 * Used to forward requests to the I/O thread
428 * if existing.
429 */
430typedef struct ISCSICMD
431{
432 /** Next one in the list. */
[31587]433 struct ISCSICMD *pNext;
434 /** Assigned ITT. */
435 uint32_t Itt;
436 /** Completion callback. */
437 PFNISCSICMDCOMPLETED pfnComplete;
438 /** Opaque user data. */
439 void *pvUser;
[31098]440 /** Command to execute. */
[31587]441 ISCSICMDTYPE enmCmdType;
[31098]442 /** Command type dependent data. */
443 union
444 {
445 /** Process a SCSI request. */
446 struct
447 {
448 /** The SCSI request to process. */
[44256]449 PSCSIREQ pScsiReq;
[31098]450 } ScsiReq;
451 /** Call a function in the I/O thread. */
452 struct
453 {
454 /** The method to execute. */
[44256]455 PFNISCSIEXEC pfnExec;
[31098]456 /** User data. */
[44256]457 void *pvUser;
[31098]458 } Exec;
459 } CmdType;
460} ISCSICMD, *PISCSICMD;
461
462/**
[31587]463 * Send iSCSI PDU.
464 * Contains all necessary data to send a PDU.
465 */
466typedef struct ISCSIPDUTX
467{
468 /** Pointer to the next PDu to send. */
469 struct ISCSIPDUTX *pNext;
470 /** The BHS. */
471 uint32_t aBHS[12];
[32225]472 /** Assigned CmdSN for this PDU. */
473 uint32_t CmdSN;
[31587]474 /** The S/G buffer used for sending. */
475 RTSGBUF SgBuf;
476 /** Number of bytes to send until the PDU completed. */
477 size_t cbSgLeft;
478 /** The iSCSI command this PDU belongs to. */
479 PISCSICMD pIScsiCmd;
480 /** Number of segments in the request segments array. */
481 unsigned cISCSIReq;
482 /** The request segments - variable in size. */
483 RTSGSEG aISCSIReq[1];
484} ISCSIPDUTX, *PISCSIPDUTX;
485
486/**
[17796]487 * Block driver instance data.
488 */
489typedef struct ISCSIIMAGE
490{
491 /** Pointer to the filename (location). Not really used. */
492 const char *pszFilename;
493 /** Pointer to the initiator name. */
494 char *pszInitiatorName;
495 /** Pointer to the target name. */
496 char *pszTargetName;
497 /** Pointer to the target address. */
498 char *pszTargetAddress;
499 /** Pointer to the user name for authenticating the Initiator. */
500 char *pszInitiatorUsername;
501 /** Pointer to the secret for authenticating the Initiator. */
502 uint8_t *pbInitiatorSecret;
503 /** Length of the secret for authenticating the Initiator. */
504 size_t cbInitiatorSecret;
505 /** Pointer to the user name for authenticating the Target. */
506 char *pszTargetUsername;
507 /** Pointer to the secret for authenticating the Initiator. */
508 uint8_t *pbTargetSecret;
509 /** Length of the secret for authenticating the Initiator. */
510 size_t cbTargetSecret;
[23594]511 /** Limit for iSCSI writes, essentially limiting the amount of data
512 * written in a single write. This is negotiated with the target, so
513 * the actual size might be smaller. */
514 uint32_t cbWriteSplit;
[17796]515 /** Initiator session identifier. */
516 uint64_t ISID;
517 /** SCSI Logical Unit Number. */
518 uint64_t LUN;
519 /** Pointer to the per-disk VD interface list. */
520 PVDINTERFACE pVDIfsDisk;
521 /** Pointer to the per-image VD interface list. */
522 PVDINTERFACE pVDIfsImage;
[38469]523 /** Error interface. */
524 PVDINTERFACEERROR pIfError;
[17796]525 /** Config interface. */
[38469]526 PVDINTERFACECONFIG pIfConfig;
[31456]527 /** I/O interface. */
[38469]528 PVDINTERFACEIOINT pIfIo;
[32691]529 /** TCP network stack interface. */
[38469]530 PVDINTERFACETCPNET pIfNet;
[17796]531 /** Image open flags. */
532 unsigned uOpenFlags;
533 /** Number of re-login retries when a connection fails. */
534 uint32_t cISCSIRetries;
535 /** Sector size on volume. */
536 uint32_t cbSector;
[24144]537 /** Size of volume in sectors. */
538 uint64_t cVolume;
[26959]539 /** Total volume size in bytes. Easier than multiplying the above values all the time. */
[17796]540 uint64_t cbSize;
[22256]541
542 /** Negotiated maximum data length when sending to target. */
543 uint32_t cbSendDataLength;
544 /** Negotiated maximum data length when receiving from target. */
545 uint32_t cbRecvDataLength;
546
[17796]547 /** Current state of the connection/session. */
548 ISCSISTATE state;
549 /** Flag whether the first Login Response PDU has been seen. */
550 bool FirstRecvPDU;
551 /** Initiator Task Tag of the last iSCSI request PDU. */
552 uint32_t ITT;
553 /** Sequence number of the last command. */
554 uint32_t CmdSN;
555 /** Sequence number of the next command expected by the target. */
556 uint32_t ExpCmdSN;
557 /** Maximum sequence number accepted by the target (determines size of window). */
558 uint32_t MaxCmdSN;
559 /** Expected sequence number of next status. */
560 uint32_t ExpStatSN;
561 /** Currently active request. */
562 PISCSIREQ paCurrReq;
563 /** Segment number of currently active request. */
564 uint32_t cnCurrReq;
565 /** Pointer to receive PDU buffer. (Freed by RT) */
566 void *pvRecvPDUBuf;
567 /** Length of receive PDU buffer. */
568 size_t cbRecvPDUBuf;
569 /** Mutex protecting against concurrent use from several threads. */
570 RTSEMMUTEX Mutex;
571
572 /** Pointer to the target hostname. */
573 char *pszHostname;
[37688]574 /** Port to use on the target host. */
[17796]575 uint32_t uPort;
576 /** Socket handle of the TCP connection. */
[31098]577 VDSOCKET Socket;
[17796]578 /** Timeout for read operations on the TCP connection (in milliseconds). */
579 uint32_t uReadTimeout;
[26917]580 /** Flag whether to automatically generate the initiator name. */
581 bool fAutomaticInitiatorName;
[74990]582 /** Flag whether to automatically determine the LUN. */
583 bool fAutomaticLUN;
[17796]584 /** Flag whether to use the host IP stack or DevINIP. */
585 bool fHostIP;
[50304]586 /** Flag whether to dump malformed packets in the release log. */
587 bool fDumpMalformedPackets;
[61978]588 /** Flag whtether the target is readonly. */
589 bool fTargetReadOnly;
590 /** Flag whether to retry the connection before processing new requests. */
591 bool fTryReconnect;
[31098]592
593 /** Head of request queue */
594 PISCSICMD pScsiReqQueue;
595 /** Mutex protecting the request queue from concurrent access. */
596 RTSEMMUTEX MutexReqQueue;
597 /** I/O thread. */
598 RTTHREAD hThreadIo;
599 /** Flag whether the thread should be still running. */
600 volatile bool fRunning;
[31714]601 /* Flag whether the target supports command queuing. */
602 bool fCmdQueuingSupported;
[31098]603 /** Flag whether extended select is supported. */
604 bool fExtendedSelectSupported;
[31456]605 /** Padding used for aligning the PDUs. */
606 uint8_t aPadding[4];
607 /** Socket events to poll for. */
608 uint32_t fPollEvents;
[31587]609 /** Number of bytes to read to complete the current PDU. */
610 size_t cbRecvPDUResidual;
611 /** Current position in the PDU buffer. */
612 uint8_t *pbRecvPDUBufCur;
613 /** Flag whether we are currently reading the BHS. */
614 bool fRecvPDUBHS;
615 /** List of PDUs waiting to get transmitted. */
616 PISCSIPDUTX pIScsiPDUTxHead;
617 /** Tail of PDUs waiting to get transmitted. */
618 PISCSIPDUTX pIScsiPDUTxTail;
619 /** PDU we are currently transmitting. */
620 PISCSIPDUTX pIScsiPDUTxCur;
[31619]621 /** Number of commands waiting for an answer from the target.
622 * Used for timeout handling for poll.
623 */
624 unsigned cCmdsWaiting;
[31587]625 /** Table of commands waiting for a response from the target. */
626 PISCSICMD aCmdsWaiting[ISCSI_CMD_WAITING_ENTRIES];
[62004]627 /** Number of logins since last successful I/O.
628 * Used to catch the case where logging succeeds but
629 * processing read/write/flushes cause a disconnect.
630 */
631 volatile uint32_t cLoginsSinceIo;
[36230]632
633 /** Release log counter. */
634 unsigned cLogRelErrors;
[66486]635 /** The static region list. */
636 VDREGIONLIST RegionList;
[31587]637} ISCSIIMAGE;
[17796]638
[31587]639
[57358]640/*********************************************************************************************************************************
641* Static Variables *
642*********************************************************************************************************************************/
[17796]643
[26917]644/** Default initiator basename. */
645static const char *s_iscsiDefaultInitiatorBasename = "iqn.2009-08.com.sun.virtualbox.initiator";
[17796]646
647/** Default LUN. */
648static const char *s_iscsiConfigDefaultLUN = "0";
649
650/** Default timeout, 10 seconds. */
651static const char *s_iscsiConfigDefaultTimeout = "10000";
652
[23594]653/** Default write split value, less or equal to ISCSI_DATA_LENGTH_MAX. */
654static const char *s_iscsiConfigDefaultWriteSplit = "262144";
655
[17796]656/** Default host IP stack. */
657static const char *s_iscsiConfigDefaultHostIPStack = "1";
658
[50304]659/** Default dump malformed packet configuration value. */
660static const char *s_iscsiConfigDefaultDumpMalformedPackets = "0";
661
[17796]662/** Description of all accepted config parameters. */
663static const VDCONFIGINFO s_iscsiConfigInfo[] =
664{
[50304]665 { "TargetName", NULL, VDCFGVALUETYPE_STRING, VD_CFGKEY_MANDATORY },
[17796]666 /* LUN is defined of string type to handle the "enc" prefix. */
[50304]667 { "LUN", s_iscsiConfigDefaultLUN, VDCFGVALUETYPE_STRING, VD_CFGKEY_MANDATORY },
668 { "TargetAddress", NULL, VDCFGVALUETYPE_STRING, VD_CFGKEY_MANDATORY },
669 { "InitiatorName", NULL, VDCFGVALUETYPE_STRING, 0 },
670 { "InitiatorUsername", NULL, VDCFGVALUETYPE_STRING, 0 },
671 { "InitiatorSecret", NULL, VDCFGVALUETYPE_BYTES, 0 },
672 { "TargetUsername", NULL, VDCFGVALUETYPE_STRING, VD_CFGKEY_EXPERT },
673 { "TargetSecret", NULL, VDCFGVALUETYPE_BYTES, VD_CFGKEY_EXPERT },
674 { "WriteSplit", s_iscsiConfigDefaultWriteSplit, VDCFGVALUETYPE_INTEGER, VD_CFGKEY_EXPERT },
675 { "Timeout", s_iscsiConfigDefaultTimeout, VDCFGVALUETYPE_INTEGER, VD_CFGKEY_EXPERT },
676 { "HostIPStack", s_iscsiConfigDefaultHostIPStack, VDCFGVALUETYPE_INTEGER, VD_CFGKEY_EXPERT },
677 { "DumpMalformedPackets", s_iscsiConfigDefaultDumpMalformedPackets, VDCFGVALUETYPE_INTEGER, VD_CFGKEY_EXPERT },
678 { NULL, NULL, VDCFGVALUETYPE_INTEGER, 0 }
[17796]679};
680
681
[57358]682/*********************************************************************************************************************************
683* Internal Functions *
684*********************************************************************************************************************************/
685
[17796]686/* iSCSI low-level functions (only to be used from the iSCSI high-level functions). */
687static uint32_t iscsiNewITT(PISCSIIMAGE pImage);
[30941]688static int iscsiSendPDU(PISCSIIMAGE pImage, PISCSIREQ paReq, uint32_t cnReq, uint32_t uFlags);
[44361]689static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes, uint32_t fFlags);
[31587]690static int iscsiRecvPDUAsync(PISCSIIMAGE pImage);
691static int iscsiSendPDUAsync(PISCSIIMAGE pImage);
[31456]692static int iscsiValidatePDU(PISCSIRES paRes, uint32_t cnRes);
[31587]693static int iscsiRecvPDUProcess(PISCSIIMAGE pImage, PISCSIRES paRes, uint32_t cnRes);
694static int iscsiPDUTxPrepare(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd);
695static int iscsiRecvPDUUpdateRequest(PISCSIIMAGE pImage, PISCSIRES paRes, uint32_t cnRes);
696static void iscsiCmdComplete(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd, int rcCmd);
[17796]697static int iscsiTextAddKeyValue(uint8_t *pbBuf, size_t cbBuf, size_t *pcbBufCurr, const char *pcszKey, const char *pcszValue, size_t cbValue);
698static int iscsiTextGetKeyValue(const uint8_t *pbBuf, size_t cbBuf, const char *pcszKey, const char **ppcszValue);
699static int iscsiStrToBinary(const char *pcszValue, uint8_t *pbValue, size_t *pcbValue);
[22258]700static int iscsiUpdateParameters(PISCSIIMAGE pImage, const uint8_t *pbBuf, size_t cbBuf);
[17796]701
702/* Serial number arithmetic comparison. */
703static bool serial_number_less(uint32_t sn1, uint32_t sn2);
[32225]704static bool serial_number_greater(uint32_t sn1, uint32_t sn2);
[17796]705
706/* CHAP-MD5 functions. */
707#ifdef IMPLEMENT_TARGET_AUTH
708static void chap_md5_generate_challenge(uint8_t *pbChallenge, size_t *pcbChallenge);
709#endif
710static void chap_md5_compute_response(uint8_t *pbResponse, uint8_t id, const uint8_t *pbChallenge, size_t cbChallenge,
711 const uint8_t *pbSecret, size_t cbSecret);
712
[36230]713/**
714 * Internal: release log wrapper limiting the number of entries.
715 */
716DECLINLINE(void) iscsiLogRel(PISCSIIMAGE pImage, const char *pcszFormat, ...)
717{
718 if (pImage->cLogRelErrors++ < MAX_LOG_REL_ERRORS)
719 {
720 va_list va;
[17796]721
[36230]722 va_start(va, pcszFormat);
723 LogRel(("%N\n", pcszFormat, &va));
724 va_end(va);
725 }
726}
727
[31098]728DECLINLINE(bool) iscsiIsClientConnected(PISCSIIMAGE pImage)
729{
730 return pImage->Socket != NIL_VDSOCKET
[38469]731 && pImage->pIfNet->pfnIsClientConnected(pImage->Socket);
[31098]732}
[17796]733
[31587]734/**
735 * Calculates the hash for the given ITT used
736 * to look up the command in the table.
737 */
738DECLINLINE(uint32_t) iscsiIttHash(uint32_t Itt)
739{
740 return Itt % ISCSI_CMD_WAITING_ENTRIES;
741}
742
743static PISCSICMD iscsiCmdGetFromItt(PISCSIIMAGE pImage, uint32_t Itt)
744{
745 PISCSICMD pIScsiCmd = NULL;
746
747 pIScsiCmd = pImage->aCmdsWaiting[iscsiIttHash(Itt)];
748
749 while ( pIScsiCmd
750 && pIScsiCmd->Itt != Itt)
751 pIScsiCmd = pIScsiCmd->pNext;
752
753 return pIScsiCmd;
754}
755
756static void iscsiCmdInsert(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd)
757{
758 PISCSICMD pIScsiCmdOld;
759 uint32_t idx = iscsiIttHash(pIScsiCmd->Itt);
760
761 Assert(!pIScsiCmd->pNext);
762
763 pIScsiCmdOld = pImage->aCmdsWaiting[idx];
764 pIScsiCmd->pNext = pIScsiCmdOld;
765 pImage->aCmdsWaiting[idx] = pIScsiCmd;
[31619]766 pImage->cCmdsWaiting++;
[31587]767}
768
769static PISCSICMD iscsiCmdRemove(PISCSIIMAGE pImage, uint32_t Itt)
770{
771 PISCSICMD pIScsiCmd = NULL;
772 PISCSICMD pIScsiCmdPrev = NULL;
773 uint32_t idx = iscsiIttHash(Itt);
774
775 pIScsiCmd = pImage->aCmdsWaiting[idx];
776
777 while ( pIScsiCmd
778 && pIScsiCmd->Itt != Itt)
779 {
780 pIScsiCmdPrev = pIScsiCmd;
781 pIScsiCmd = pIScsiCmd->pNext;
782 }
783
784 if (pIScsiCmd)
785 {
786 if (pIScsiCmdPrev)
787 {
[90802]788 AssertPtrNull(pIScsiCmd->pNext);
[31587]789 pIScsiCmdPrev->pNext = pIScsiCmd->pNext;
790 }
791 else
792 {
793 pImage->aCmdsWaiting[idx] = pIScsiCmd->pNext;
[90802]794 AssertPtrNull(pImage->aCmdsWaiting[idx]);
[31587]795 }
[31619]796 pImage->cCmdsWaiting--;
[31587]797 }
798
799 return pIScsiCmd;
800}
801
[31608]802/**
803 * Removes all commands from the table and returns the
804 * list head
805 *
[41785]806 * @returns Pointer to the head of the command list.
[31608]807 * @param pImage iSCSI connection to use.
808 */
809static PISCSICMD iscsiCmdRemoveAll(PISCSIIMAGE pImage)
810{
811 PISCSICMD pIScsiCmdHead = NULL;
812
813 for (unsigned idx = 0; idx < RT_ELEMENTS(pImage->aCmdsWaiting); idx++)
814 {
815 PISCSICMD pHead;
816 PISCSICMD pTail;
817
818 pHead = pImage->aCmdsWaiting[idx];
819 pImage->aCmdsWaiting[idx] = NULL;
820
[31616]821 if (pHead)
822 {
823 /* Get the tail. */
824 pTail = pHead;
825 while (pTail->pNext)
826 pTail = pTail->pNext;
[31608]827
[31616]828 /* Concatenate. */
829 pTail->pNext = pIScsiCmdHead;
830 pIScsiCmdHead = pHead;
831 }
[31608]832 }
[33124]833 pImage->cCmdsWaiting = 0;
[31608]834
835 return pIScsiCmdHead;
836}
837
[50304]838/**
839 * Dumps an iSCSI packet if enabled.
840 *
841 * @param pImage The iSCSI image instance data.
842 * @param paISCSISegs Pointer to the segments array.
843 * @param cnISCSISegs Number of segments in the array.
844 * @param rc Status code for this packet.
845 * @param fRequest Flag whether this is request or response packet.
846 */
847static void iscsiDumpPacket(PISCSIIMAGE pImage, PISCSIREQ paISCSISegs, unsigned cnISCSISegs, int rc, bool fRequest)
848{
849 if (pImage->fDumpMalformedPackets)
850 {
851 LogRel(("iSCSI{%s}: Dumping %s packet completed with status code %Rrc\n", pImage->pszTargetName, fRequest ? "request" : "response", rc));
852 for (unsigned i = 0; i < cnISCSISegs; i++)
853 {
854 if (paISCSISegs[i].cbSeg)
855 {
856 LogRel(("iSCSI{%s}: Segment %u, size %zu\n"
857 "%.*Rhxd\n",
858 pImage->pszTargetName, i, paISCSISegs[i].cbSeg,
859 paISCSISegs[i].cbSeg, paISCSISegs[i].pcvSeg));
860 }
861 }
862 }
863}
864
[26917]865static int iscsiTransportConnect(PISCSIIMAGE pImage)
866{
867 int rc;
868 if (!pImage->pszHostname)
869 return VERR_NET_DEST_ADDRESS_REQUIRED;
870
[53539]871 rc = pImage->pIfNet->pfnClientConnect(pImage->Socket, pImage->pszHostname, pImage->uPort, pImage->uReadTimeout);
[28827]872 if (RT_FAILURE(rc))
[26917]873 {
[28827]874 if ( rc == VERR_NET_CONNECTION_REFUSED
875 || rc == VERR_NET_CONNECTION_RESET
876 || rc == VERR_NET_UNREACHABLE
877 || rc == VERR_NET_HOST_UNREACHABLE
878 || rc == VERR_NET_CONNECTION_TIMED_OUT)
879 {
880 /* Standardize return value for no connection. */
881 rc = VERR_NET_CONNECTION_REFUSED;
882 }
883 return rc;
[26917]884 }
885
[30309]886 /* Disable Nagle algorithm, we want things to be sent immediately. */
[38469]887 pImage->pIfNet->pfnSetSendCoalescing(pImage->Socket, false);
[30309]888
[26917]889 /* Make initiator name and ISID unique on this host. */
890 RTNETADDR LocalAddr;
[38469]891 rc = pImage->pIfNet->pfnGetLocalAddress(pImage->Socket, &LocalAddr);
[26917]892 if (RT_FAILURE(rc))
893 return rc;
894 if ( LocalAddr.uPort == RTNETADDR_PORT_NA
895 || LocalAddr.uPort > 65535)
896 return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED;
897 pImage->ISID &= ~65535ULL;
898 pImage->ISID |= LocalAddr.uPort;
899 /* Eliminate the port so that it isn't included below. */
900 LocalAddr.uPort = RTNETADDR_PORT_NA;
901 if (pImage->fAutomaticInitiatorName)
902 {
903 if (pImage->pszInitiatorName)
904 RTStrFree(pImage->pszInitiatorName);
905 RTStrAPrintf(&pImage->pszInitiatorName, "%s:01:%RTnaddr",
906 s_iscsiDefaultInitiatorBasename, &LocalAddr);
907 if (!pImage->pszInitiatorName)
908 return VERR_NO_MEMORY;
909 }
[37688]910 LogRel(("iSCSI: connect from initiator %s with source port %u\n", pImage->pszInitiatorName, pImage->ISID & 65535));
[26917]911 return VINF_SUCCESS;
912}
913
914
[37688]915static int iscsiTransportClose(PISCSIIMAGE pImage)
916{
917 int rc;
918
919 LogFlowFunc(("(%s:%d)\n", pImage->pszHostname, pImage->uPort));
920 if (iscsiIsClientConnected(pImage))
921 {
922 LogRel(("iSCSI: disconnect from initiator %s with source port %u\n", pImage->pszInitiatorName, pImage->ISID & 65535));
[38469]923 rc = pImage->pIfNet->pfnClientClose(pImage->Socket);
[37688]924 }
925 else
926 rc = VINF_SUCCESS;
927 LogFlowFunc(("returns %Rrc\n", rc));
928 return rc;
929}
930
931
[31608]932static int iscsiTransportRead(PISCSIIMAGE pImage, PISCSIRES paResponse, unsigned int cnResponse)
[17796]933{
934 int rc = VINF_SUCCESS;
935 unsigned int i = 0;
936 size_t cbToRead, cbActuallyRead, residual, cbSegActual = 0, cbAHSLength, cbDataLength;
937 char *pDst;
938
939 LogFlowFunc(("cnResponse=%d (%s:%d)\n", cnResponse, pImage->pszHostname, pImage->uPort));
[31098]940 if (!iscsiIsClientConnected(pImage))
[17796]941 {
[26917]942 /* Reconnecting makes no sense in this case, as there will be nothing
943 * to receive. We would just run into a timeout. */
944 rc = VERR_BROKEN_PIPE;
[17796]945 }
946
[31587]947 if (RT_SUCCESS(rc) && paResponse[0].cbSeg >= ISCSI_BHS_SIZE)
[17796]948 {
949 cbToRead = 0;
[31587]950 residual = ISCSI_BHS_SIZE; /* Do not read more than the BHS length before the true PDU length is known. */
[17796]951 cbSegActual = residual;
952 pDst = (char *)paResponse[i].pvSeg;
953 uint64_t u64Timeout = RTTimeMilliTS() + pImage->uReadTimeout;
954 do
955 {
956 int64_t cMilliesRemaining = u64Timeout - RTTimeMilliTS();
957 if (cMilliesRemaining <= 0)
958 {
959 rc = VERR_TIMEOUT;
960 break;
961 }
962 Assert(cMilliesRemaining < 1000000);
[40229]963 rc = pImage->pIfNet->pfnSelectOne(pImage->Socket, cMilliesRemaining);
[31608]964 if (RT_FAILURE(rc))
965 break;
[40229]966 rc = pImage->pIfNet->pfnRead(pImage->Socket, pDst, residual, &cbActuallyRead);
[17796]967 if (RT_FAILURE(rc))
968 break;
969 if (cbActuallyRead == 0)
970 {
971 /* The other end has closed the connection. */
[37688]972 iscsiTransportClose(pImage);
[30941]973 pImage->state = ISCSISTATE_FREE;
[17796]974 rc = VERR_NET_CONNECTION_RESET;
975 break;
976 }
977 if (cbToRead == 0)
978 {
979 /* Currently reading the BHS. */
980 residual -= cbActuallyRead;
981 pDst += cbActuallyRead;
982 if (residual <= 40)
983 {
984 /* Enough data read to figure out the actual PDU size. */
985 uint32_t word1 = RT_N2H_U32(((uint32_t *)(paResponse[0].pvSeg))[1]);
986 cbAHSLength = (word1 & 0xff000000) >> 24;
987 cbAHSLength = ((cbAHSLength - 1) | 3) + 1; /* Add padding. */
988 cbDataLength = word1 & 0x00ffffff;
989 cbDataLength = ((cbDataLength - 1) | 3) + 1; /* Add padding. */
990 cbToRead = residual + cbAHSLength + cbDataLength;
[31587]991 residual += paResponse[0].cbSeg - ISCSI_BHS_SIZE;
[17796]992 if (residual > cbToRead)
993 residual = cbToRead;
[31587]994 cbSegActual = ISCSI_BHS_SIZE + cbAHSLength + cbDataLength;
[17796]995 /* Check whether we are already done with this PDU (no payload). */
996 if (cbToRead == 0)
997 break;
998 }
999 }
1000 else
1001 {
1002 cbToRead -= cbActuallyRead;
1003 if (cbToRead == 0)
1004 break;
1005 pDst += cbActuallyRead;
1006 residual -= cbActuallyRead;
1007 }
1008 if (residual == 0)
1009 {
1010 i++;
1011 if (i >= cnResponse)
1012 {
1013 /* No space left in receive buffers. */
1014 rc = VERR_BUFFER_OVERFLOW;
1015 break;
1016 }
1017 pDst = (char *)paResponse[i].pvSeg;
1018 residual = paResponse[i].cbSeg;
1019 if (residual > cbToRead)
1020 residual = cbToRead;
1021 cbSegActual = residual;
1022 }
[31098]1023 LogFlowFunc(("cbToRead=%u residual=%u cbSegActual=%u cbActuallRead=%u\n",
1024 cbToRead, residual, cbSegActual, cbActuallyRead));
[17796]1025 } while (true);
1026 }
1027 else
1028 {
1029 if (RT_SUCCESS(rc))
1030 rc = VERR_BUFFER_OVERFLOW;
1031 }
1032 if (RT_SUCCESS(rc))
1033 {
1034 paResponse[i].cbSeg = cbSegActual;
1035 for (i++; i < cnResponse; i++)
1036 paResponse[i].cbSeg = 0;
1037 }
1038
1039 if (RT_UNLIKELY( RT_FAILURE(rc)
1040 && ( rc == VERR_NET_CONNECTION_RESET
1041 || rc == VERR_NET_CONNECTION_ABORTED
1042 || rc == VERR_NET_CONNECTION_RESET_BY_PEER
1043 || rc == VERR_NET_CONNECTION_REFUSED
1044 || rc == VERR_BROKEN_PIPE)))
1045 {
1046 /* Standardize return value for broken connection. */
1047 rc = VERR_BROKEN_PIPE;
1048 }
1049
1050 LogFlowFunc(("returns %Rrc\n", rc));
1051 return rc;
1052}
1053
1054
1055static int iscsiTransportWrite(PISCSIIMAGE pImage, PISCSIREQ paRequest, unsigned int cnRequest)
1056{
1057 int rc = VINF_SUCCESS;
1058 unsigned int i;
1059
[30941]1060 LogFlowFunc(("cnRequest=%d (%s:%d)\n", cnRequest, pImage->pszHostname, pImage->uPort));
[31098]1061 if (!iscsiIsClientConnected(pImage))
[17796]1062 {
1063 /* Attempt to reconnect if the connection was previously broken. */
[26917]1064 rc = iscsiTransportConnect(pImage);
[17796]1065 }
1066
1067 if (RT_SUCCESS(rc))
1068 {
[30309]1069 /* Construct scatter/gather buffer for entire request, worst case
1070 * needs twice as many entries to allow for padding. */
1071 unsigned cBuf = 0;
[17796]1072 for (i = 0; i < cnRequest; i++)
1073 {
[30309]1074 cBuf++;
[17796]1075 if (paRequest[i].cbSeg & 3)
[30309]1076 cBuf++;
1077 }
1078 Assert(cBuf < ISCSI_SG_SEGMENTS_MAX);
1079 RTSGBUF buf;
1080 RTSGSEG aSeg[ISCSI_SG_SEGMENTS_MAX];
1081 static char aPad[4] = { 0, 0, 0, 0 };
1082 RTSgBufInit(&buf, &aSeg[0], cBuf);
1083 unsigned iBuf = 0;
1084 for (i = 0; i < cnRequest; i++)
1085 {
1086 /* Actual data chunk. */
1087 aSeg[iBuf].pvSeg = (void *)paRequest[i].pcvSeg;
1088 aSeg[iBuf].cbSeg = paRequest[i].cbSeg;
1089 iBuf++;
1090 /* Insert proper padding before the next chunk. */
1091 if (paRequest[i].cbSeg & 3)
[17796]1092 {
[30309]1093 aSeg[iBuf].pvSeg = &aPad[0];
1094 aSeg[iBuf].cbSeg = 4 - (paRequest[i].cbSeg & 3);
1095 iBuf++;
[17796]1096 }
1097 }
[30309]1098 /* Send out the request, the socket is set to send data immediately,
1099 * avoiding unnecessary delays. */
[38469]1100 rc = pImage->pIfNet->pfnSgWrite(pImage->Socket, &buf);
[30309]1101
[17796]1102 }
1103
1104 if (RT_UNLIKELY( RT_FAILURE(rc)
1105 && ( rc == VERR_NET_CONNECTION_RESET
1106 || rc == VERR_NET_CONNECTION_ABORTED
1107 || rc == VERR_NET_CONNECTION_RESET_BY_PEER
1108 || rc == VERR_NET_CONNECTION_REFUSED
1109 || rc == VERR_BROKEN_PIPE)))
1110 {
1111 /* Standardize return value for broken connection. */
1112 rc = VERR_BROKEN_PIPE;
1113 }
1114
[30941]1115 LogFlowFunc(("returns %Rrc\n", rc));
[17796]1116 return rc;
1117}
1118
1119
1120static int iscsiTransportOpen(PISCSIIMAGE pImage)
1121{
1122 int rc = VINF_SUCCESS;
1123 size_t cbHostname = 0; /* shut up gcc */
1124 const char *pcszPort = NULL; /* shut up gcc */
1125 char *pszPortEnd;
1126 uint16_t uPort;
1127
1128 /* Clean up previous connection data. */
[37688]1129 iscsiTransportClose(pImage);
[17796]1130 if (pImage->pszHostname)
1131 {
1132 RTMemFree(pImage->pszHostname);
1133 pImage->pszHostname = NULL;
1134 pImage->uPort = 0;
1135 }
1136
1137 /* Locate the port number via the colon separating the hostname from the port. */
1138 if (*pImage->pszTargetAddress)
1139 {
1140 if (*pImage->pszTargetAddress != '[')
1141 {
1142 /* Normal hostname or IPv4 dotted decimal. */
1143 pcszPort = strchr(pImage->pszTargetAddress, ':');
1144 if (pcszPort != NULL)
1145 {
1146 cbHostname = pcszPort - pImage->pszTargetAddress;
1147 pcszPort++;
1148 }
1149 else
1150 cbHostname = strlen(pImage->pszTargetAddress);
1151 }
1152 else
1153 {
1154 /* IPv6 literal address. Contains colons, so skip to closing square bracket. */
1155 pcszPort = strchr(pImage->pszTargetAddress, ']');
1156 if (pcszPort != NULL)
1157 {
1158 pcszPort++;
1159 cbHostname = pcszPort - pImage->pszTargetAddress;
1160 if (*pcszPort == '\0')
1161 pcszPort = NULL;
1162 else if (*pcszPort != ':')
1163 rc = VERR_PARSE_ERROR;
1164 else
1165 pcszPort++;
1166 }
1167 else
1168 rc = VERR_PARSE_ERROR;
1169 }
1170 }
1171 else
1172 rc = VERR_PARSE_ERROR;
1173
1174 /* Now split address into hostname and port. */
1175 if (RT_SUCCESS(rc))
1176 {
1177 pImage->pszHostname = (char *)RTMemAlloc(cbHostname + 1);
1178 if (!pImage->pszHostname)
1179 rc = VERR_NO_MEMORY;
1180 else
1181 {
[43925]1182 if (pImage->pszTargetAddress[0] == '[')
1183 memcpy(pImage->pszHostname, pImage->pszTargetAddress + 1, cbHostname);
1184 else
1185 memcpy(pImage->pszHostname, pImage->pszTargetAddress, cbHostname);
[17796]1186 pImage->pszHostname[cbHostname] = '\0';
1187 if (pcszPort != NULL)
1188 {
1189 rc = RTStrToUInt16Ex(pcszPort, &pszPortEnd, 0, &uPort);
1190 /* Note that RT_SUCCESS() macro to check the rc value is not strict enough in this case. */
1191 if (rc == VINF_SUCCESS && *pszPortEnd == '\0' && uPort != 0)
1192 {
1193 pImage->uPort = uPort;
1194 }
1195 else
1196 {
1197 rc = VERR_PARSE_ERROR;
1198 }
1199 }
1200 else
1201 pImage->uPort = ISCSI_DEFAULT_PORT;
1202 }
1203 }
1204
[26917]1205 if (RT_SUCCESS(rc))
[17796]1206 {
[31098]1207 if (!iscsiIsClientConnected(pImage))
[26917]1208 rc = iscsiTransportConnect(pImage);
1209 }
1210 else
1211 {
[17796]1212 if (pImage->pszHostname)
1213 {
1214 RTMemFree(pImage->pszHostname);
1215 pImage->pszHostname = NULL;
1216 }
1217 pImage->uPort = 0;
1218 }
1219
1220 LogFlowFunc(("returns %Rrc\n", rc));
1221 return rc;
1222}
1223
[63880]1224/**
1225 * Returns a human readable version of the given initiator login error detail.
1226 *
1227 * @returns String with the error detail.
1228 * @param u8Detail The detail indicator from the response.
1229 */
1230static const char *iscsiGetLoginErrorDetail(uint8_t u8Detail)
1231{
1232 const char *pszDetail = NULL;
[17796]1233
[63880]1234 switch (u8Detail)
1235 {
1236 case 0x00:
1237 pszDetail = "Miscelleanous iSCSI intiaitor error";
1238 break;
1239 case 0x01:
1240 pszDetail = "Authentication failure";
1241 break;
1242 case 0x02:
1243 pszDetail = "Authorization failure";
1244 break;
1245 case 0x03:
1246 pszDetail = "Not found";
1247 break;
1248 case 0x04:
1249 pszDetail = "Target removed";
1250 break;
1251 case 0x05:
1252 pszDetail = "Unsupported version";
1253 break;
1254 case 0x06:
1255 pszDetail = "Too many connections";
1256 break;
1257 case 0x07:
1258 pszDetail = "Missing parameter";
1259 break;
1260 case 0x08:
1261 pszDetail = "Can't include in session";
1262 break;
1263 case 0x09:
1264 pszDetail = "Session type not supported";
1265 break;
1266 case 0x0a:
1267 pszDetail = "Session does not exist";
1268 break;
1269 case 0x0b:
1270 pszDetail = "Invalid request type during login";
1271 break;
1272 default:
1273 pszDetail = "Unknown status detail";
1274 }
1275
1276 return pszDetail;
1277}
1278
[17796]1279/**
[63880]1280 * Attempts one login attempt to the given target.
[17796]1281 *
[58170]1282 * @returns VBox status code.
[63880]1283 * @retval VINF_TRY_AGAIN when getting redirected and having to start over.
1284 * @retval VERR_TRY_AGAIN in case the connection was lost while receiving a reply
1285 * from the target and the login attempt can be repeated.
[26959]1286 * @param pImage The iSCSI connection state to be used.
[17796]1287 */
[63880]1288static int iscsiLogin(PISCSIIMAGE pImage)
[17796]1289{
[63880]1290 int rc = VINF_SUCCESS;
[17796]1291 uint32_t itt;
1292 uint32_t csg, nsg, substate;
1293 uint64_t isid_tsih;
1294 uint8_t bBuf[4096]; /* Should be large enough even for large authentication values. */
1295 size_t cbBuf;
1296 bool transit;
1297 uint8_t pbChallenge[1024]; /* RFC3720 specifies this as maximum. */
1298 size_t cbChallenge = 0; /* shut up gcc */
[62750]1299 uint8_t bChapIdx = 0; /* (MSC is used uninitialized) */
[17796]1300 uint8_t aResponse[RTMD5HASHSIZE];
[57529]1301 uint32_t cnISCSIReq = 0;
[17796]1302 ISCSIREQ aISCSIReq[4];
1303 uint32_t aReqBHS[12];
[57529]1304 uint32_t cnISCSIRes = 0;
[17796]1305 ISCSIRES aISCSIRes[2];
1306 uint32_t aResBHS[12];
1307 char *pszNext;
[63880]1308 bool fParameterNeg = true;
[22256]1309 pImage->cbRecvDataLength = ISCSI_DATA_LENGTH_MAX;
[23594]1310 pImage->cbSendDataLength = RT_MIN(ISCSI_DATA_LENGTH_MAX, pImage->cbWriteSplit);
[22258]1311 char szMaxDataLength[16];
1312 RTStrPrintf(szMaxDataLength, sizeof(szMaxDataLength), "%u", ISCSI_DATA_LENGTH_MAX);
[22256]1313 ISCSIPARAMETER aParameterNeg[] =
1314 {
1315 { "HeaderDigest", "None", 0 },
1316 { "DataDigest", "None", 0 },
1317 { "MaxConnections", "1", 0 },
1318 { "InitialR2T", "No", 0 },
1319 { "ImmediateData", "Yes", 0 },
[22258]1320 { "MaxRecvDataSegmentLength", szMaxDataLength, 0 },
1321 { "MaxBurstLength", szMaxDataLength, 0 },
1322 { "FirstBurstLength", szMaxDataLength, 0 },
[22256]1323 { "DefaultTime2Wait", "0", 0 },
1324 { "DefaultTime2Retain", "60", 0 },
1325 { "DataPDUInOrder", "Yes", 0 },
1326 { "DataSequenceInOrder", "Yes", 0 },
1327 { "ErrorRecoveryLevel", "0", 0 },
1328 { "MaxOutstandingR2T", "1", 0 }
1329 };
1330
[31098]1331 if (!iscsiIsClientConnected(pImage))
[26959]1332 {
[26917]1333 rc = iscsiTransportOpen(pImage);
[26959]1334 if (RT_FAILURE(rc))
[63880]1335 return rc;
[26959]1336 }
[26917]1337
[17796]1338 pImage->state = ISCSISTATE_IN_LOGIN;
1339 pImage->ITT = 1;
1340 pImage->FirstRecvPDU = true;
1341 pImage->CmdSN = 1;
1342 pImage->ExpCmdSN = 0;
1343 pImage->MaxCmdSN = 1;
[31714]1344 pImage->ExpStatSN = 0;
[17796]1345
1346 /*
1347 * Send login request to target.
1348 */
1349 itt = iscsiNewITT(pImage);
1350 csg = 0;
1351 nsg = 0;
1352 substate = 0;
1353 isid_tsih = pImage->ISID << 16; /* TSIH field currently always 0 */
1354
[63880]1355 do
1356 {
[17796]1357 transit = false;
1358 cbBuf = 0;
1359 /* Handle all cases with a single switch statement. */
1360 switch (csg << 8 | substate)
1361 {
1362 case 0x0000: /* security negotiation, step 0: propose authentication. */
1363 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "SessionType", "Normal", 0);
1364 if (RT_FAILURE(rc))
[63880]1365 break;
[17796]1366 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "InitiatorName", pImage->pszInitiatorName, 0);
1367 if (RT_FAILURE(rc))
[63880]1368 break;
[17796]1369 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "TargetName", pImage->pszTargetName, 0);
1370 if (RT_FAILURE(rc))
[63880]1371 break;
[17796]1372 if (pImage->pszInitiatorUsername == NULL)
1373 {
1374 /* No authentication. Immediately switch to next phase. */
1375 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "AuthMethod", "None", 0);
1376 if (RT_FAILURE(rc))
[63880]1377 break;
[17796]1378 nsg = 1;
1379 transit = true;
1380 }
1381 else
1382 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "AuthMethod", "CHAP,None", 0);
1383 break;
1384 case 0x0001: /* security negotiation, step 1: propose CHAP_MD5 variant. */
1385 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_A", "5", 0);
1386 break;
1387 case 0x0002: /* security negotiation, step 2: send authentication info. */
1388 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_N", pImage->pszInitiatorUsername, 0);
1389 if (RT_FAILURE(rc))
[63880]1390 break;
[17796]1391 chap_md5_compute_response(aResponse, bChapIdx, pbChallenge, cbChallenge,
1392 pImage->pbInitiatorSecret, pImage->cbInitiatorSecret);
1393 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_R", (const char *)aResponse, RTMD5HASHSIZE);
1394 if (RT_FAILURE(rc))
[63880]1395 break;
[17796]1396 nsg = 1;
1397 transit = true;
1398 break;
1399 case 0x0100: /* login operational negotiation, step 0: set parameters. */
[22256]1400 if (fParameterNeg)
1401 {
1402 for (unsigned i = 0; i < RT_ELEMENTS(aParameterNeg); i++)
1403 {
1404 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf,
1405 aParameterNeg[i].pszParamName,
1406 aParameterNeg[i].pszParamValue,
1407 aParameterNeg[i].cbParamValue);
1408 if (RT_FAILURE(rc))
[63880]1409 break;
[22256]1410 }
1411 fParameterNeg = false;
1412 }
1413
[17796]1414 nsg = 3;
1415 transit = true;
1416 break;
1417 case 0x0300: /* full feature phase. */
1418 default:
1419 /* Should never come here. */
1420 AssertMsgFailed(("send: Undefined login state %d substate %d\n", csg, substate));
1421 break;
1422 }
1423
[63880]1424 if (RT_FAILURE(rc))
1425 break;
1426
[17796]1427 aReqBHS[0] = RT_H2N_U32( ISCSI_IMMEDIATE_DELIVERY_BIT
1428 | (csg << ISCSI_CSG_SHIFT)
1429 | (transit ? (nsg << ISCSI_NSG_SHIFT | ISCSI_TRANSIT_BIT) : 0)
1430 | ISCSI_MY_VERSION /* Minimum version. */
1431 | (ISCSI_MY_VERSION << 8) /* Maximum version. */
1432 | ISCSIOP_LOGIN_REQ); /* C=0 */
[18507]1433 aReqBHS[1] = RT_H2N_U32((uint32_t)cbBuf); /* TotalAHSLength=0 */
[17796]1434 aReqBHS[2] = RT_H2N_U32(isid_tsih >> 32);
1435 aReqBHS[3] = RT_H2N_U32(isid_tsih & 0xffffffff);
1436 aReqBHS[4] = itt;
1437 aReqBHS[5] = RT_H2N_U32(1 << 16); /* CID=1,reserved */
1438 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
1439 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
1440 aReqBHS[8] = 0; /* reserved */
1441 aReqBHS[9] = 0; /* reserved */
1442 aReqBHS[10] = 0; /* reserved */
1443 aReqBHS[11] = 0; /* reserved */
1444
1445 cnISCSIReq = 0;
1446 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
1447 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
1448 cnISCSIReq++;
1449
1450 aISCSIReq[cnISCSIReq].pcvSeg = bBuf;
1451 aISCSIReq[cnISCSIReq].cbSeg = cbBuf;
1452 cnISCSIReq++;
1453
[30941]1454 rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_NO_REATTACH);
[17796]1455 if (RT_SUCCESS(rc))
1456 {
1457 ISCSIOPCODE cmd;
1458 ISCSILOGINSTATUSCLASS loginStatusClass;
1459
1460 cnISCSIRes = 0;
1461 aISCSIRes[cnISCSIRes].pvSeg = aResBHS;
1462 aISCSIRes[cnISCSIRes].cbSeg = sizeof(aResBHS);
1463 cnISCSIRes++;
1464 aISCSIRes[cnISCSIRes].pvSeg = bBuf;
1465 aISCSIRes[cnISCSIRes].cbSeg = sizeof(bBuf);
1466 cnISCSIRes++;
1467
[44361]1468 rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes, ISCSIPDU_NO_REATTACH);
[63880]1469 if (RT_FAILURE(rc))
[44361]1470 {
1471 /*
1472 * We lost connection to the target while receiving the answer,
1473 * start from the beginning.
1474 */
[63880]1475 if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
1476 rc = VERR_TRY_AGAIN;
1477 break;
[44361]1478 }
[63880]1479
[17796]1480 /** @todo collect partial login responses with Continue bit set. */
1481 Assert(aISCSIRes[0].pvSeg == aResBHS);
1482 Assert(aISCSIRes[0].cbSeg >= ISCSI_BHS_SIZE);
1483 Assert((RT_N2H_U32(aResBHS[0]) & ISCSI_CONTINUE_BIT) == 0);
1484
1485 cmd = (ISCSIOPCODE)(RT_N2H_U32(aResBHS[0]) & ISCSIOP_MASK);
1486 if (cmd == ISCSIOP_LOGIN_RES)
1487 {
1488 if ((RT_N2H_U32(aResBHS[0]) & 0xff) != ISCSI_MY_VERSION)
1489 {
1490 iscsiTransportClose(pImage);
1491 rc = VERR_PARSE_ERROR;
1492 break; /* Give up immediately, as a RFC violation in version fields is very serious. */
1493 }
1494
1495 loginStatusClass = (ISCSILOGINSTATUSCLASS)(RT_N2H_U32(aResBHS[9]) >> 24);
1496 switch (loginStatusClass)
1497 {
1498 case ISCSI_LOGIN_STATUS_CLASS_SUCCESS:
1499 uint32_t targetCSG;
1500 uint32_t targetNSG;
1501 bool targetTransit;
1502
1503 if (pImage->FirstRecvPDU)
1504 {
1505 pImage->FirstRecvPDU = false;
1506 pImage->ExpStatSN = RT_N2H_U32(aResBHS[6]) + 1;
1507 }
1508
1509 targetCSG = (RT_N2H_U32(aResBHS[0]) & ISCSI_CSG_MASK) >> ISCSI_CSG_SHIFT;
1510 targetNSG = (RT_N2H_U32(aResBHS[0]) & ISCSI_NSG_MASK) >> ISCSI_NSG_SHIFT;
1511 targetTransit = !!(RT_N2H_U32(aResBHS[0]) & ISCSI_TRANSIT_BIT);
1512
1513 /* Handle all cases with a single switch statement. */
1514 switch (csg << 8 | substate)
1515 {
1516 case 0x0000: /* security negotiation, step 0: receive final authentication. */
[22258]1517 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1518 if (RT_FAILURE(rc))
1519 break;
1520
[17796]1521 const char *pcszAuthMethod;
1522
1523 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "AuthMethod", &pcszAuthMethod);
1524 if (RT_FAILURE(rc))
1525 {
1526 rc = VERR_PARSE_ERROR;
1527 break;
1528 }
1529 if (strcmp(pcszAuthMethod, "None") == 0)
1530 {
1531 /* Authentication offered, but none required. Skip to operational parameters. */
1532 csg = 1;
1533 nsg = 1;
1534 transit = true;
1535 substate = 0;
1536 break;
1537 }
1538 else if (strcmp(pcszAuthMethod, "CHAP") == 0 && targetNSG == 0 && !targetTransit)
1539 {
1540 /* CHAP authentication required, continue with next substate. */
1541 substate++;
1542 break;
1543 }
1544
1545 /* Unknown auth method or login response PDU headers incorrect. */
1546 rc = VERR_PARSE_ERROR;
1547 break;
1548 case 0x0001: /* security negotiation, step 1: receive final CHAP variant and challenge. */
[62748]1549 {
[22258]1550 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1551 if (RT_FAILURE(rc))
1552 break;
1553
[17796]1554 const char *pcszChapAuthMethod;
1555 const char *pcszChapIdxTarget;
1556 const char *pcszChapChallengeStr;
1557
1558 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_A", &pcszChapAuthMethod);
1559 if (RT_FAILURE(rc))
1560 {
1561 rc = VERR_PARSE_ERROR;
1562 break;
1563 }
1564 if (strcmp(pcszChapAuthMethod, "5") != 0)
1565 {
1566 rc = VERR_PARSE_ERROR;
1567 break;
1568 }
1569 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_I", &pcszChapIdxTarget);
1570 if (RT_FAILURE(rc))
1571 {
1572 rc = VERR_PARSE_ERROR;
1573 break;
1574 }
1575 rc = RTStrToUInt8Ex(pcszChapIdxTarget, &pszNext, 0, &bChapIdx);
[62750]1576/** @todo r=bird: Unsafe use of pszNext on failure. The code should probably
1577 * use RTStrToUInt8Full and check for rc != VINF_SUCCESS. */
1578 if (rc > VINF_SUCCESS || *pszNext != '\0')
[17796]1579 {
1580 rc = VERR_PARSE_ERROR;
1581 break;
1582 }
1583 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_C", &pcszChapChallengeStr);
1584 if (RT_FAILURE(rc))
1585 {
1586 rc = VERR_PARSE_ERROR;
1587 break;
1588 }
1589 cbChallenge = sizeof(pbChallenge);
1590 rc = iscsiStrToBinary(pcszChapChallengeStr, pbChallenge, &cbChallenge);
1591 if (RT_FAILURE(rc))
1592 break;
1593 substate++;
1594 transit = true;
1595 break;
[62748]1596 }
[17796]1597 case 0x0002: /* security negotiation, step 2: check authentication success. */
[22258]1598 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1599 if (RT_FAILURE(rc))
1600 break;
1601
[17796]1602 if (targetCSG == 0 && targetNSG == 1 && targetTransit)
1603 {
1604 /* Target wants to continue in login operational state, authentication success. */
1605 csg = 1;
1606 nsg = 3;
1607 substate = 0;
1608 break;
1609 }
1610 rc = VERR_PARSE_ERROR;
1611 break;
1612 case 0x0100: /* login operational negotiation, step 0: check results. */
[22258]1613 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
[22256]1614 if (RT_FAILURE(rc))
1615 break;
1616
[17796]1617 if (targetCSG == 1 && targetNSG == 3 && targetTransit)
1618 {
1619 /* Target wants to continue in full feature phase, login finished. */
1620 csg = 3;
1621 nsg = 3;
1622 substate = 0;
1623 break;
1624 }
[74107]1625 else if (targetCSG == 1 && (targetNSG == 1 || !targetTransit))
[22256]1626 {
1627 /* Target wants to negotiate certain parameters and
1628 * stay in login operational negotiation. */
1629 csg = 1;
1630 nsg = 3;
1631 substate = 0;
[69154]1632 break;
[22256]1633 }
[17796]1634 rc = VERR_PARSE_ERROR;
1635 break;
1636 case 0x0300: /* full feature phase. */
1637 default:
1638 AssertMsgFailed(("recv: Undefined login state %d substate %d\n", csg, substate));
1639 rc = VERR_PARSE_ERROR;
1640 break;
1641 }
1642 break;
1643 case ISCSI_LOGIN_STATUS_CLASS_REDIRECTION:
1644 const char *pcszTargetRedir;
1645
1646 /* Target has moved to some other location, as indicated in the TargetAddress key. */
1647 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "TargetAddress", &pcszTargetRedir);
1648 if (RT_FAILURE(rc))
1649 {
1650 rc = VERR_PARSE_ERROR;
1651 break;
1652 }
1653 if (pImage->pszTargetAddress)
1654 RTMemFree(pImage->pszTargetAddress);
1655 {
1656 size_t cb = strlen(pcszTargetRedir) + 1;
1657 pImage->pszTargetAddress = (char *)RTMemAlloc(cb);
1658 if (!pImage->pszTargetAddress)
1659 {
1660 rc = VERR_NO_MEMORY;
1661 break;
1662 }
1663 memcpy(pImage->pszTargetAddress, pcszTargetRedir, cb);
1664 }
[63880]1665 rc = VINF_TRY_AGAIN;
1666 break;
[17796]1667 case ISCSI_LOGIN_STATUS_CLASS_INITIATOR_ERROR:
[42398]1668 {
[63880]1669 LogRel(("iSCSI: login to target failed with: %s\n",
1670 iscsiGetLoginErrorDetail((RT_N2H_U32(aResBHS[9]) >> 16) & 0xff)));
[17796]1671 iscsiTransportClose(pImage);
1672 rc = VERR_IO_GEN_FAILURE;
[63880]1673 break;
[42398]1674 }
[17796]1675 case ISCSI_LOGIN_STATUS_CLASS_TARGET_ERROR:
1676 iscsiTransportClose(pImage);
1677 rc = VINF_EOF;
1678 break;
1679 default:
1680 rc = VERR_PARSE_ERROR;
1681 }
1682
[63880]1683 if (RT_FAILURE(rc) || rc == VINF_TRY_AGAIN)
1684 break;
1685
[17796]1686 if (csg == 3)
1687 {
1688 /*
1689 * Finished login, continuing with Full Feature Phase.
1690 */
1691 rc = VINF_SUCCESS;
1692 break;
1693 }
1694 }
1695 else
1696 AssertMsgFailed(("%s: ignoring unexpected PDU with first word = %#08x\n", __FUNCTION__, RT_N2H_U32(aResBHS[0])));
1697 }
1698 else
1699 break;
1700 } while (true);
1701
[63880]1702 if ( RT_FAILURE(rc)
1703 && rc != VERR_TRY_AGAIN)
[17796]1704 {
1705 /*
[50304]1706 * Dump the last request and response of we are supposed to do so and there is a request
1707 * or response.
1708 */
1709 if (cnISCSIReq)
1710 iscsiDumpPacket(pImage, aISCSIReq, cnISCSIReq, VINF_SUCCESS, true /* fRequest */);
1711
1712 if (cnISCSIRes)
1713 iscsiDumpPacket(pImage, (PISCSIREQ)aISCSIRes, cnISCSIRes, rc, false /* fRequest */);
1714
1715 /*
[17796]1716 * Close connection to target.
1717 */
1718 iscsiTransportClose(pImage);
1719 pImage->state = ISCSISTATE_FREE;
1720 }
[69154]1721 else if (rc == VINF_SUCCESS)
[17796]1722 pImage->state = ISCSISTATE_NORMAL;
1723
[63880]1724 return rc;
1725}
1726
1727/**
1728 * Attach to an iSCSI target. Performs all operations necessary to enter
1729 * Full Feature Phase.
1730 *
1731 * @returns VBox status code.
[64272]1732 * @param pvUser The iSCSI connection state to be used as opaque user data.
[63880]1733 */
1734static DECLCALLBACK(int) iscsiAttach(void *pvUser)
1735{
1736 int rc = VINF_SUCCESS;
1737 unsigned cRetries = 5;
1738 PISCSIIMAGE pImage = (PISCSIIMAGE)pvUser;
1739
1740 LogFlowFunc(("entering\n"));
1741
1742 Assert(pImage->state == ISCSISTATE_FREE);
1743
1744 /*
1745 * If there were too many logins without any successful I/O just fail
1746 * and assume the target is not working properly.
1747 */
1748 if (ASMAtomicReadU32(&pImage->cLoginsSinceIo) == 3)
1749 return VERR_BROKEN_PIPE;
1750
1751 RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
1752
1753 /* Make 100% sure the connection isn't reused for a new login. */
1754 iscsiTransportClose(pImage);
1755
1756 /* Try to log in a few number of times. */
1757 while (cRetries > 0)
1758 {
1759 rc = iscsiLogin(pImage);
1760 if (rc == VINF_SUCCESS) /* Login succeeded, continue with full feature phase. */
1761 break;
1762 else if (rc == VERR_TRY_AGAIN) /* Lost connection during receive. */
1763 cRetries--;
1764 else if (RT_FAILURE(rc))
1765 break;
1766 else /* For redirects try again. */
1767 AssertMsg(rc == VINF_TRY_AGAIN, ("Unexpected status code %Rrc\n", rc));
1768 }
1769
[62004]1770 if (RT_SUCCESS(rc))
1771 ASMAtomicIncU32(&pImage->cLoginsSinceIo);
1772
[17796]1773 RTSemMutexRelease(pImage->Mutex);
1774
1775 LogFlowFunc(("returning %Rrc\n", rc));
[50304]1776 LogRel(("iSCSI: login to target %s %s (%Rrc)\n", pImage->pszTargetName, RT_SUCCESS(rc) ? "successful" : "failed", rc));
[17796]1777 return rc;
1778}
1779
1780
1781/**
1782 * Detach from an iSCSI target.
1783 *
[58170]1784 * @returns VBox status code.
[64272]1785 * @param pvUser The iSCSI connection state to be used as opaque user data.
[17796]1786 */
[57388]1787static DECLCALLBACK(int) iscsiDetach(void *pvUser)
[17796]1788{
1789 int rc;
1790 uint32_t itt;
1791 uint32_t cnISCSIReq = 0;
1792 ISCSIREQ aISCSIReq[4];
1793 uint32_t aReqBHS[12];
[31098]1794 PISCSIIMAGE pImage = (PISCSIIMAGE)pvUser;
1795
[30941]1796 LogFlowFunc(("entering\n"));
[17796]1797
1798 RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
1799
1800 if (pImage->state != ISCSISTATE_FREE && pImage->state != ISCSISTATE_IN_LOGOUT)
1801 {
1802 pImage->state = ISCSISTATE_IN_LOGOUT;
1803
1804 /*
1805 * Send logout request to target.
1806 */
1807 itt = iscsiNewITT(pImage);
1808 aReqBHS[0] = RT_H2N_U32(ISCSI_FINAL_BIT | ISCSIOP_LOGOUT_REQ); /* I=0,F=1,Reason=close session */
1809 aReqBHS[1] = RT_H2N_U32(0); /* TotalAHSLength=0,DataSementLength=0 */
1810 aReqBHS[2] = 0; /* reserved */
1811 aReqBHS[3] = 0; /* reserved */
1812 aReqBHS[4] = itt;
1813 aReqBHS[5] = 0; /* reserved */
1814 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
1815 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
1816 aReqBHS[8] = 0; /* reserved */
1817 aReqBHS[9] = 0; /* reserved */
1818 aReqBHS[10] = 0; /* reserved */
1819 aReqBHS[11] = 0; /* reserved */
1820 pImage->CmdSN++;
1821
1822 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
1823 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
1824 cnISCSIReq++;
1825
[30941]1826 rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_NO_REATTACH);
[17796]1827 if (RT_SUCCESS(rc))
1828 {
1829 /*
1830 * Read logout response from target.
1831 */
1832 ISCSIRES aISCSIRes;
1833 uint32_t aResBHS[12];
1834
1835 aISCSIRes.pvSeg = aResBHS;
1836 aISCSIRes.cbSeg = sizeof(aResBHS);
[44361]1837 rc = iscsiRecvPDU(pImage, itt, &aISCSIRes, 1, ISCSIPDU_NO_REATTACH);
[17796]1838 if (RT_SUCCESS(rc))
1839 {
1840 if (RT_N2H_U32(aResBHS[0]) != (ISCSI_FINAL_BIT | ISCSIOP_LOGOUT_RES))
1841 AssertMsgFailed(("iSCSI Logout response invalid\n"));
1842 }
1843 else
1844 AssertMsgFailed(("iSCSI Logout response error, rc=%Rrc\n", rc));
1845 }
1846 else
1847 AssertMsgFailed(("Could not send iSCSI Logout request, rc=%Rrc\n", rc));
1848 }
1849
1850 if (pImage->state != ISCSISTATE_FREE)
1851 {
1852 /*
1853 * Close connection to target.
1854 */
1855 rc = iscsiTransportClose(pImage);
1856 if (RT_FAILURE(rc))
1857 AssertMsgFailed(("Could not close connection to target, rc=%Rrc\n", rc));
1858 }
1859
1860 pImage->state = ISCSISTATE_FREE;
1861
1862 RTSemMutexRelease(pImage->Mutex);
1863
[30941]1864 LogFlowFunc(("leaving\n"));
[17796]1865 LogRel(("iSCSI: logout to target %s\n", pImage->pszTargetName));
1866 return VINF_SUCCESS;
1867}
1868
1869
1870/**
1871 * Perform a command on an iSCSI target. Target must be already in
1872 * Full Feature Phase.
1873 *
[58170]1874 * @returns VBox status code.
[26959]1875 * @param pImage The iSCSI connection state to be used.
1876 * @param pRequest Command descriptor. Contains all information about
1877 * the command, its transfer directions and pointers
1878 * to the buffer(s) used for transferring data and
1879 * status information.
[17796]1880 */
1881static int iscsiCommand(PISCSIIMAGE pImage, PSCSIREQ pRequest)
1882{
1883 int rc;
1884 uint32_t itt;
1885 uint32_t cbData;
1886 uint32_t cnISCSIReq = 0;
1887 ISCSIREQ aISCSIReq[4];
1888 uint32_t aReqBHS[12];
1889
1890 uint32_t *pDst = NULL;
1891 size_t cbBufLength;
[27181]1892 uint32_t aStatus[256]; /**< Plenty of buffer for status information. */
[17796]1893 uint32_t ExpDataSN = 0;
1894 bool final = false;
1895
[32278]1896
[30941]1897 LogFlowFunc(("entering, CmdSN=%d\n", pImage->CmdSN));
[17796]1898
1899 Assert(pRequest->enmXfer != SCSIXFER_TO_FROM_TARGET); /**< @todo not yet supported, would require AHS. */
1900 Assert(pRequest->cbI2TData <= 0xffffff); /* larger transfers would require R2T support. */
[32536]1901 Assert(pRequest->cbCDB <= 16); /* would cause buffer overrun below. */
[17796]1902
1903 /* If not in normal state, then the transport connection was dropped. Try
1904 * to reestablish by logging in, the target might be responsive again. */
1905 if (pImage->state == ISCSISTATE_FREE)
1906 rc = iscsiAttach(pImage);
1907
1908 /* If still not in normal state, then the underlying transport connection
1909 * cannot be established. Get out before bad things happen (and make
1910 * sure the caller suspends the VM again). */
[63880]1911 if (pImage->state == ISCSISTATE_NORMAL)
[17796]1912 {
[63880]1913 /*
1914 * Send SCSI command to target with all I2T data included.
1915 */
1916 cbData = 0;
1917 if (pRequest->enmXfer == SCSIXFER_FROM_TARGET)
1918 cbData = (uint32_t)pRequest->cbT2IData;
1919 else
1920 cbData = (uint32_t)pRequest->cbI2TData;
[17796]1921
[63880]1922 RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
[17796]1923
[63880]1924 itt = iscsiNewITT(pImage);
1925 memset(aReqBHS, 0, sizeof(aReqBHS));
1926 aReqBHS[0] = RT_H2N_U32( ISCSI_FINAL_BIT | ISCSI_TASK_ATTR_SIMPLE | ISCSIOP_SCSI_CMD
1927 | (pRequest->enmXfer << 21)); /* I=0,F=1,Attr=Simple */
1928 aReqBHS[1] = RT_H2N_U32(0x00000000 | ((uint32_t)pRequest->cbI2TData & 0xffffff)); /* TotalAHSLength=0 */
1929 aReqBHS[2] = RT_H2N_U32(pImage->LUN >> 32);
1930 aReqBHS[3] = RT_H2N_U32(pImage->LUN & 0xffffffff);
1931 aReqBHS[4] = itt;
1932 aReqBHS[5] = RT_H2N_U32(cbData);
1933 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
1934 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
1935 memcpy(aReqBHS + 8, pRequest->abCDB, pRequest->cbCDB);
1936 pImage->CmdSN++;
[17796]1937
[63880]1938 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
1939 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
[17796]1940 cnISCSIReq++;
1941
[63880]1942 if ( pRequest->enmXfer == SCSIXFER_TO_TARGET
1943 || pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET)
[17796]1944 {
[63880]1945 Assert(pRequest->cI2TSegs == 1);
1946 aISCSIReq[cnISCSIReq].pcvSeg = pRequest->paI2TSegs[0].pvSeg;
1947 aISCSIReq[cnISCSIReq].cbSeg = pRequest->paI2TSegs[0].cbSeg; /* Padding done by transport. */
1948 cnISCSIReq++;
[17796]1949 }
1950
[63880]1951 rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_DEFAULT);
1952 if (RT_SUCCESS(rc))
1953 {
1954 /* Place SCSI request in queue. */
1955 pImage->paCurrReq = aISCSIReq;
1956 pImage->cnCurrReq = cnISCSIReq;
[17796]1957
[63880]1958 /*
1959 * Read SCSI response/data in PDUs from target.
1960 */
1961 if ( pRequest->enmXfer == SCSIXFER_FROM_TARGET
1962 || pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET)
[17796]1963 {
[63880]1964 Assert(pRequest->cT2ISegs == 1);
1965 pDst = (uint32_t *)pRequest->paT2ISegs[0].pvSeg;
1966 cbBufLength = pRequest->paT2ISegs[0].cbSeg;
[17796]1967 }
[63880]1968 else
1969 cbBufLength = 0;
1970
1971 do
[17796]1972 {
[63880]1973 uint32_t cnISCSIRes = 0;
1974 ISCSIRES aISCSIRes[4];
1975 uint32_t aResBHS[12];
1976
1977 aISCSIRes[cnISCSIRes].pvSeg = aResBHS;
1978 aISCSIRes[cnISCSIRes].cbSeg = sizeof(aResBHS);
1979 cnISCSIRes++;
1980 if (cbBufLength != 0 &&
1981 ( pRequest->enmXfer == SCSIXFER_FROM_TARGET
1982 || pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET))
[17796]1983 {
[63880]1984 aISCSIRes[cnISCSIRes].pvSeg = pDst;
1985 aISCSIRes[cnISCSIRes].cbSeg = cbBufLength;
1986 cnISCSIRes++;
1987 }
1988 /* Always reserve space for the status - it's impossible to tell
1989 * beforehand whether this will be the final PDU or not. */
1990 aISCSIRes[cnISCSIRes].pvSeg = aStatus;
1991 aISCSIRes[cnISCSIRes].cbSeg = sizeof(aStatus);
1992 cnISCSIRes++;
1993
1994 rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes, ISCSIPDU_DEFAULT);
1995 if (RT_FAILURE(rc))
[17796]1996 break;
[63880]1997
1998 final = !!(RT_N2H_U32(aResBHS[0]) & ISCSI_FINAL_BIT);
1999 ISCSIOPCODE cmd = (ISCSIOPCODE)(RT_N2H_U32(aResBHS[0]) & ISCSIOP_MASK);
2000 if (cmd == ISCSIOP_SCSI_RES)
2001 {
2002 /* This is the final PDU which delivers the status (and may be omitted if
2003 * the last Data-In PDU included successful completion status). Note
2004 * that ExpStatSN has been bumped already in iscsiRecvPDU. */
2005 if (!final || ((RT_N2H_U32(aResBHS[0]) & 0x0000ff00) != 0) || (RT_N2H_U32(aResBHS[6]) != pImage->ExpStatSN - 1))
2006 {
2007 /* SCSI Response in the wrong place or with a (target) failure. */
2008 rc = VERR_PARSE_ERROR;
2009 break;
2010 }
2011 /* The following is a bit tricky, as in error situations we may
2012 * get the status only instead of the result data plus optional
2013 * status. Thus the status may have ended up partially in the
2014 * data area. */
2015 pRequest->status = RT_N2H_U32(aResBHS[0]) & 0x000000ff;
2016 cbData = RT_N2H_U32(aResBHS[1]) & 0x00ffffff;
2017 if (cbData >= 2)
2018 {
2019 uint32_t cbStat = RT_N2H_U32(((uint32_t *)aISCSIRes[1].pvSeg)[0]) >> 16;
2020 if (cbStat + 2 > cbData)
2021 {
2022 rc = VERR_BUFFER_OVERFLOW;
2023 break;
2024 }
2025 /* Truncate sense data if it doesn't fit into the buffer. */
2026 pRequest->cbSense = RT_MIN(cbStat, pRequest->cbSense);
2027 memcpy(pRequest->abSense,
2028 ((const char *)aISCSIRes[1].pvSeg) + 2,
2029 RT_MIN(aISCSIRes[1].cbSeg - 2, pRequest->cbSense));
2030 if ( cnISCSIRes > 2 && aISCSIRes[2].cbSeg
2031 && (ssize_t)pRequest->cbSense - aISCSIRes[1].cbSeg + 2 > 0)
2032 {
2033 memcpy((char *)pRequest->abSense + aISCSIRes[1].cbSeg - 2,
2034 aISCSIRes[2].pvSeg,
2035 pRequest->cbSense - aISCSIRes[1].cbSeg + 2);
2036 }
2037 }
2038 else if (cbData == 1)
2039 {
2040 rc = VERR_PARSE_ERROR;
2041 break;
2042 }
2043 else
2044 pRequest->cbSense = 0;
2045 break;
[17796]2046 }
[63880]2047 else if (cmd == ISCSIOP_SCSI_DATA_IN)
[27181]2048 {
[63880]2049 /* A Data-In PDU carries some data that needs to be added to the received
2050 * data in response to the command. There may be both partial and complete
2051 * Data-In PDUs, so collect data until the status is included or the status
2052 * is sent in a separate SCSI Result frame (see above). */
2053 if (final && aISCSIRes[2].cbSeg != 0)
2054 {
2055 /* The received PDU is partially stored in the buffer for status.
2056 * Must not happen under normal circumstances and is a target error. */
2057 rc = VERR_BUFFER_OVERFLOW;
2058 break;
2059 }
2060 uint32_t len = RT_N2H_U32(aResBHS[1]) & 0x00ffffff;
2061 pDst = (uint32_t *)((char *)pDst + len);
2062 cbBufLength -= len;
2063 ExpDataSN++;
2064 if (final && (RT_N2H_U32(aResBHS[0]) & ISCSI_STATUS_BIT) != 0)
2065 {
2066 pRequest->status = RT_N2H_U32(aResBHS[0]) & 0x000000ff;
2067 pRequest->cbSense = 0;
2068 break;
2069 }
[27181]2070 }
[63880]2071 else
2072 {
2073 rc = VERR_PARSE_ERROR;
2074 break;
2075 }
2076 } while (true);
2077
2078 /* Remove SCSI request from queue. */
2079 pImage->paCurrReq = NULL;
2080 pImage->cnCurrReq = 0;
[17796]2081 }
[63880]2082
2083 if (rc == VERR_TIMEOUT)
[17796]2084 {
[63880]2085 /* Drop connection in case the target plays dead. Much better than
2086 * delaying the next requests until the timed out command actually
2087 * finishes. Also keep in mind that command shouldn't take longer than
2088 * about 30-40 seconds, or the guest will lose its patience. */
2089 iscsiTransportClose(pImage);
2090 pImage->state = ISCSISTATE_FREE;
2091 rc = VERR_BROKEN_PIPE;
[17796]2092 }
[63880]2093 RTSemMutexRelease(pImage->Mutex);
[17796]2094 }
[63880]2095 else
2096 rc = VERR_NET_CONNECTION_REFUSED;
[17796]2097
[62004]2098 if (RT_SUCCESS(rc))
2099 ASMAtomicWriteU32(&pImage->cLoginsSinceIo, 0);
[30941]2100 LogFlowFunc(("returns %Rrc\n", rc));
[17796]2101 return rc;
2102}
2103
2104
2105/**
2106 * Generate a new Initiator Task Tag.
2107 *
2108 * @returns Initiator Task Tag.
[26959]2109 * @param pImage The iSCSI connection state to be used.
[17796]2110 */
2111static uint32_t iscsiNewITT(PISCSIIMAGE pImage)
2112{
2113 uint32_t next_itt;
2114
2115 next_itt = pImage->ITT++;
2116 if (pImage->ITT == ISCSI_TASK_TAG_RSVD)
2117 pImage->ITT = 0;
2118 return RT_H2N_U32(next_itt);
2119}
2120
2121
2122/**
2123 * Send an iSCSI request. The request can consist of several segments, which
2124 * are padded to 4 byte boundaries and concatenated.
2125 *
2126 * @returns VBOX status
[26959]2127 * @param pImage The iSCSI connection state to be used.
2128 * @param paReq Pointer to array of iSCSI request sections.
2129 * @param cnReq Number of valid iSCSI request sections in the array.
[30941]2130 * @param uFlags Flags controlling the exact send semantics.
[17796]2131 */
[30941]2132static int iscsiSendPDU(PISCSIIMAGE pImage, PISCSIREQ paReq, uint32_t cnReq,
2133 uint32_t uFlags)
[17796]2134{
2135 int rc = VINF_SUCCESS;
2136 /** @todo return VERR_VD_ISCSI_INVALID_STATE in the appropriate situations,
2137 * needs cleaning up of timeout/disconnect handling a bit, as otherwise
2138 * too many incorrect errors are signalled. */
2139 Assert(cnReq >= 1);
2140 Assert(paReq[0].cbSeg >= ISCSI_BHS_SIZE);
2141
[26959]2142 for (uint32_t i = 0; i < pImage->cISCSIRetries; i++)
[17796]2143 {
2144 rc = iscsiTransportWrite(pImage, paReq, cnReq);
2145 if (RT_SUCCESS(rc))
2146 break;
[30941]2147 if ( (uFlags & ISCSIPDU_NO_REATTACH)
2148 || (rc != VERR_BROKEN_PIPE && rc != VERR_NET_CONNECTION_REFUSED))
[17796]2149 break;
[22256]2150 /* No point in reestablishing the connection for a logout */
2151 if (pImage->state == ISCSISTATE_IN_LOGOUT)
2152 break;
[17796]2153 RTThreadSleep(500);
[22256]2154 if (pImage->state != ISCSISTATE_IN_LOGIN)
[17796]2155 {
2156 /* Attempt to re-login when a connection fails, but only when not
[22256]2157 * currently logging in. */
[17796]2158 rc = iscsiAttach(pImage);
2159 if (RT_FAILURE(rc))
2160 break;
2161 }
2162 }
2163 return rc;
2164}
2165
2166
2167/**
2168 * Wait for an iSCSI response with a matching Initiator Target Tag. The response is
2169 * split into several segments, as requested by the caller-provided buffer specification.
2170 * Remember that the response can be split into several PDUs by the sender, so make
2171 * sure that all parts are collected and processed appropriately by the caller.
2172 *
2173 * @returns VBOX status
[26959]2174 * @param pImage The iSCSI connection state to be used.
[69230]2175 * @param itt The initiator task tag.
[26959]2176 * @param paRes Pointer to array of iSCSI response sections.
2177 * @param cnRes Number of valid iSCSI response sections in the array.
[44361]2178 * @param fRecvFlags PDU receive flags.
[17796]2179 */
[44361]2180static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes,
2181 uint32_t fRecvFlags)
[17796]2182{
2183 int rc = VINF_SUCCESS;
2184 ISCSIRES aResBuf;
2185
[26959]2186 for (uint32_t i = 0; i < pImage->cISCSIRetries; i++)
[17796]2187 {
2188 aResBuf.pvSeg = pImage->pvRecvPDUBuf;
2189 aResBuf.cbSeg = pImage->cbRecvPDUBuf;
[31608]2190 rc = iscsiTransportRead(pImage, &aResBuf, 1);
[17796]2191 if (RT_FAILURE(rc))
2192 {
2193 if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
2194 {
[22256]2195 /* No point in reestablishing the connection for a logout */
2196 if (pImage->state == ISCSISTATE_IN_LOGOUT)
2197 break;
[17796]2198 /* Connection broken while waiting for a response - wait a while and
2199 * try to restart by re-sending the original request (if any).
2200 * This also handles the connection reestablishment (login etc.). */
2201 RTThreadSleep(500);
[44361]2202 if ( pImage->state != ISCSISTATE_IN_LOGIN
2203 && !(fRecvFlags & ISCSIPDU_NO_REATTACH))
[18678]2204 {
2205 /* Attempt to re-login when a connection fails, but only when not
[22256]2206 * currently logging in. */
[18678]2207 rc = iscsiAttach(pImage);
2208 if (RT_FAILURE(rc))
2209 break;
[44361]2210
2211 if (pImage->paCurrReq != NULL)
2212 {
2213 rc = iscsiSendPDU(pImage, pImage->paCurrReq, pImage->cnCurrReq, ISCSIPDU_DEFAULT);
2214 if (RT_FAILURE(rc))
2215 break;
2216 }
[18678]2217 }
[17796]2218 }
2219 else
2220 {
2221 /* Signal other errors (VERR_BUFFER_OVERFLOW etc.) to the caller. */
2222 break;
2223 }
2224 }
2225 else
2226 {
2227 ISCSIOPCODE cmd;
2228 const uint32_t *pcvResSeg = (const uint32_t *)aResBuf.pvSeg;
2229
2230 /* Check whether the received PDU is valid, and update the internal state of
2231 * the iSCSI connection/session. */
[31456]2232 rc = iscsiValidatePDU(&aResBuf, 1);
[17796]2233 if (RT_FAILURE(rc))
[50304]2234 {
2235 iscsiDumpPacket(pImage, (PISCSIREQ)&aResBuf, 1, rc, false /* fRequest */);
[17796]2236 continue;
[50304]2237 }
[17796]2238 cmd = (ISCSIOPCODE)(RT_N2H_U32(pcvResSeg[0]) & ISCSIOP_MASK);
2239 switch (cmd)
2240 {
2241 case ISCSIOP_SCSI_RES:
2242 case ISCSIOP_SCSI_TASKMGMT_RES:
2243 case ISCSIOP_SCSI_DATA_IN:
2244 case ISCSIOP_R2T:
2245 case ISCSIOP_ASYN_MSG:
2246 case ISCSIOP_TEXT_RES:
2247 case ISCSIOP_LOGIN_RES:
2248 case ISCSIOP_LOGOUT_RES:
2249 case ISCSIOP_REJECT:
2250 case ISCSIOP_NOP_IN:
2251 if (serial_number_less(pImage->MaxCmdSN, RT_N2H_U32(pcvResSeg[8])))
2252 pImage->MaxCmdSN = RT_N2H_U32(pcvResSeg[8]);
2253 if (serial_number_less(pImage->ExpCmdSN, RT_N2H_U32(pcvResSeg[7])))
2254 pImage->ExpCmdSN = RT_N2H_U32(pcvResSeg[7]);
2255 break;
2256 default:
2257 rc = VERR_PARSE_ERROR;
[50304]2258 iscsiDumpPacket(pImage, (PISCSIREQ)&aResBuf, 1, rc, false /* fRequest */);
[17796]2259 }
2260 if (RT_FAILURE(rc))
2261 continue;
2262 if ( !pImage->FirstRecvPDU
[40229]2263 && (cmd != ISCSIOP_SCSI_DATA_IN || (RT_N2H_U32(pcvResSeg[0]) & ISCSI_STATUS_BIT))
2264 && ( cmd != ISCSIOP_LOGIN_RES
2265 || (ISCSILOGINSTATUSCLASS)((RT_N2H_U32(pcvResSeg[9]) >> 24) == ISCSI_LOGIN_STATUS_CLASS_SUCCESS)))
[17796]2266 {
2267 if (pImage->ExpStatSN == RT_N2H_U32(pcvResSeg[6]))
2268 {
2269 /* StatSN counter is not advanced on R2T and on a target SN update NOP-In. */
2270 if ( (cmd != ISCSIOP_R2T)
2271 && ((cmd != ISCSIOP_NOP_IN) || (RT_N2H_U32(pcvResSeg[4]) != ISCSI_TASK_TAG_RSVD)))
2272 pImage->ExpStatSN++;
2273 }
2274 else
2275 {
2276 rc = VERR_PARSE_ERROR;
[50304]2277 iscsiDumpPacket(pImage, (PISCSIREQ)&aResBuf, 1, rc, false /* fRequest */);
[17796]2278 continue;
2279 }
2280 }
2281 /* Finally check whether the received PDU matches what the caller wants. */
[31098]2282 if ( itt == pcvResSeg[4]
2283 && itt != ISCSI_TASK_TAG_RSVD)
[17796]2284 {
2285 /* Copy received PDU (one segment) to caller-provided buffers. */
[25823]2286 uint32_t j;
[17796]2287 size_t cbSeg;
2288 const uint8_t *pSrc;
2289
2290 pSrc = (const uint8_t *)aResBuf.pvSeg;
2291 cbSeg = aResBuf.cbSeg;
[25823]2292 for (j = 0; j < cnRes; j++)
[17796]2293 {
[25823]2294 if (cbSeg > paRes[j].cbSeg)
[17796]2295 {
[26959]2296 memcpy(paRes[j].pvSeg, pSrc, paRes[j].cbSeg);
[25823]2297 pSrc += paRes[j].cbSeg;
2298 cbSeg -= paRes[j].cbSeg;
[17796]2299 }
2300 else
2301 {
[25823]2302 memcpy(paRes[j].pvSeg, pSrc, cbSeg);
2303 paRes[j].cbSeg = cbSeg;
[17796]2304 cbSeg = 0;
2305 break;
2306 }
2307 }
2308 if (cbSeg != 0)
2309 {
2310 rc = VERR_BUFFER_OVERFLOW;
2311 break;
2312 }
[25823]2313 for (j++; j < cnRes; j++)
2314 paRes[j].cbSeg = 0;
[17796]2315 break;
2316 }
[18678]2317 else if ( cmd == ISCSIOP_NOP_IN
2318 && RT_N2H_U32(pcvResSeg[5]) != ISCSI_TASK_TAG_RSVD)
2319 {
2320 uint32_t cnISCSIReq;
2321 ISCSIREQ aISCSIReq[4];
2322 uint32_t aReqBHS[12];
2323
2324 aReqBHS[0] = RT_H2N_U32(ISCSI_IMMEDIATE_DELIVERY_BIT | ISCSI_FINAL_BIT | ISCSIOP_NOP_OUT);
2325 aReqBHS[1] = RT_H2N_U32(0); /* TotalAHSLength=0,DataSementLength=0 */
[22584]2326 aReqBHS[2] = pcvResSeg[2]; /* copy LUN from NOP-In */
2327 aReqBHS[3] = pcvResSeg[3]; /* copy LUN from NOP-In */
[18678]2328 aReqBHS[4] = RT_H2N_U32(ISCSI_TASK_TAG_RSVD); /* ITT, reply */
[22584]2329 aReqBHS[5] = pcvResSeg[5]; /* copy TTT from NOP-In */
[18678]2330 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
2331 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
2332 aReqBHS[8] = 0; /* reserved */
2333 aReqBHS[9] = 0; /* reserved */
2334 aReqBHS[10] = 0; /* reserved */
2335 aReqBHS[11] = 0; /* reserved */
2336
2337 cnISCSIReq = 0;
2338 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
2339 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
2340 cnISCSIReq++;
2341
[30941]2342 iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_NO_REATTACH);
[31098]2343 /* Break if the caller wanted to process the NOP-in only. */
2344 if (itt == ISCSI_TASK_TAG_RSVD)
2345 break;
[18678]2346 }
[17796]2347 }
2348 }
[40229]2349
[54138]2350 LogFlowFunc(("returns rc=%Rrc\n", rc));
[17796]2351 return rc;
2352}
2353
2354
2355/**
[31608]2356 * Reset the PDU buffer
2357 *
2358 * @param pImage The iSCSI connection state to be used.
2359 */
2360static void iscsiRecvPDUReset(PISCSIIMAGE pImage)
2361{
2362 pImage->cbRecvPDUResidual = ISCSI_BHS_SIZE;
2363 pImage->fRecvPDUBHS = true;
2364 pImage->pbRecvPDUBufCur = (uint8_t *)pImage->pvRecvPDUBuf;
2365}
2366
2367static void iscsiPDUTxAdd(PISCSIIMAGE pImage, PISCSIPDUTX pIScsiPDUTx, bool fFront)
2368{
2369 if (!fFront)
2370 {
[36133]2371 /* Insert PDU at the tail of the list. */
[31608]2372 if (!pImage->pIScsiPDUTxHead)
2373 pImage->pIScsiPDUTxHead = pIScsiPDUTx;
2374 else
2375 pImage->pIScsiPDUTxTail->pNext = pIScsiPDUTx;
2376 pImage->pIScsiPDUTxTail = pIScsiPDUTx;
2377 }
2378 else
2379 {
[36133]2380 /* Insert PDU at the beginning of the list. */
[31608]2381 pIScsiPDUTx->pNext = pImage->pIScsiPDUTxHead;
2382 pImage->pIScsiPDUTxHead = pIScsiPDUTx;
2383 if (!pImage->pIScsiPDUTxTail)
2384 pImage->pIScsiPDUTxTail = pIScsiPDUTx;
2385 }
2386}
2387
2388/**
[31587]2389 * Receives a PDU in a non blocking way.
2390 *
2391 * @returns VBOX status code.
2392 * @param pImage The iSCSI connection state to be used.
2393 */
2394static int iscsiRecvPDUAsync(PISCSIIMAGE pImage)
2395{
2396 size_t cbActuallyRead = 0;
2397 int rc = VINF_SUCCESS;
2398
2399 LogFlowFunc(("pImage=%#p\n", pImage));
2400
2401 /* Check if we are in the middle of a PDU receive. */
2402 if (pImage->cbRecvPDUResidual == 0)
2403 {
2404 /*
2405 * We are receiving a new PDU, don't read more than the BHS initially
[31608]2406 * until we know the real size of the PDU.
[31587]2407 */
[31608]2408 iscsiRecvPDUReset(pImage);
[31587]2409 LogFlow(("Receiving new PDU\n"));
2410 }
2411
[38469]2412 rc = pImage->pIfNet->pfnReadNB(pImage->Socket, pImage->pbRecvPDUBufCur,
[31587]2413 pImage->cbRecvPDUResidual, &cbActuallyRead);
[31616]2414 if (RT_SUCCESS(rc) && cbActuallyRead == 0)
2415 rc = VERR_BROKEN_PIPE;
2416
[31587]2417 if (RT_SUCCESS(rc))
2418 {
2419 LogFlow(("Received %zu bytes\n", cbActuallyRead));
2420 pImage->cbRecvPDUResidual -= cbActuallyRead;
2421 pImage->pbRecvPDUBufCur += cbActuallyRead;
2422
2423 /* Check if we received everything we wanted. */
2424 if ( !pImage->cbRecvPDUResidual
2425 && pImage->fRecvPDUBHS)
2426 {
2427 size_t cbAHSLength, cbDataLength;
2428
2429 /* If we were reading the BHS first get the actual PDU size now. */
2430 uint32_t word1 = RT_N2H_U32(((uint32_t *)(pImage->pvRecvPDUBuf))[1]);
2431 cbAHSLength = (word1 & 0xff000000) >> 24;
2432 cbAHSLength = ((cbAHSLength - 1) | 3) + 1; /* Add padding. */
2433 cbDataLength = word1 & 0x00ffffff;
2434 cbDataLength = ((cbDataLength - 1) | 3) + 1; /* Add padding. */
2435 pImage->cbRecvPDUResidual = cbAHSLength + cbDataLength;
2436 pImage->fRecvPDUBHS = false; /* Start receiving the rest of the PDU. */
2437 }
2438
2439 if (!pImage->cbRecvPDUResidual)
2440 {
2441 /* We received the complete PDU with or without any payload now. */
2442 LogFlow(("Received complete PDU\n"));
2443 ISCSIRES aResBuf;
2444 aResBuf.pvSeg = pImage->pvRecvPDUBuf;
2445 aResBuf.cbSeg = pImage->cbRecvPDUBuf;
2446 rc = iscsiRecvPDUProcess(pImage, &aResBuf, 1);
2447 }
2448 }
2449 else
2450 LogFlowFunc(("Reading from the socket returned with rc=%Rrc\n", rc));
2451
2452 return rc;
2453}
2454
2455static int iscsiSendPDUAsync(PISCSIIMAGE pImage)
2456{
2457 size_t cbSent = 0;
2458 int rc = VINF_SUCCESS;
2459
2460 LogFlowFunc(("pImage=%#p\n", pImage));
2461
2462 do
2463 {
[32225]2464 /*
2465 * If there is no PDU active, get the first one from the list.
2466 * Check that we are allowed to transfer the PDU by comparing the
2467 * command sequence number and the maximum sequence number allowed by the target.
2468 */
[31587]2469 if (!pImage->pIScsiPDUTxCur)
2470 {
[32225]2471 if ( !pImage->pIScsiPDUTxHead
2472 || serial_number_greater(pImage->pIScsiPDUTxHead->CmdSN, pImage->MaxCmdSN))
[31587]2473 break;
2474
2475 pImage->pIScsiPDUTxCur = pImage->pIScsiPDUTxHead;
2476 pImage->pIScsiPDUTxHead = pImage->pIScsiPDUTxCur->pNext;
2477 if (!pImage->pIScsiPDUTxHead)
2478 pImage->pIScsiPDUTxTail = NULL;
2479 }
2480
2481 /* Send as much as we can. */
[38469]2482 rc = pImage->pIfNet->pfnSgWriteNB(pImage->Socket, &pImage->pIScsiPDUTxCur->SgBuf, &cbSent);
[32132]2483 LogFlow(("SgWriteNB returned rc=%Rrc cbSent=%zu\n", rc, cbSent));
[31587]2484 if (RT_SUCCESS(rc))
2485 {
2486 LogFlow(("Sent %zu bytes for PDU %#p\n", cbSent, pImage->pIScsiPDUTxCur));
2487 pImage->pIScsiPDUTxCur->cbSgLeft -= cbSent;
2488 RTSgBufAdvance(&pImage->pIScsiPDUTxCur->SgBuf, cbSent);
2489 if (!pImage->pIScsiPDUTxCur->cbSgLeft)
2490 {
2491 /* PDU completed, free it and place the command on the waiting for response list. */
2492 if (pImage->pIScsiPDUTxCur->pIScsiCmd)
2493 {
2494 LogFlow(("Sent complete PDU, placing on waiting list\n"));
2495 iscsiCmdInsert(pImage, pImage->pIScsiPDUTxCur->pIScsiCmd);
2496 }
2497 RTMemFree(pImage->pIScsiPDUTxCur);
2498 pImage->pIScsiPDUTxCur = NULL;
2499 }
2500 }
2501 } while ( RT_SUCCESS(rc)
2502 && !pImage->pIScsiPDUTxCur);
2503
[31845]2504 if (rc == VERR_TRY_AGAIN)
2505 rc = VINF_SUCCESS;
2506
[31587]2507 /* Add the write poll flag if we still have something to send, clear it otherwise. */
2508 if (pImage->pIScsiPDUTxCur)
2509 pImage->fPollEvents |= VD_INTERFACETCPNET_EVT_WRITE;
2510 else
2511 pImage->fPollEvents &= ~VD_INTERFACETCPNET_EVT_WRITE;
2512
[32132]2513 LogFlowFunc(("rc=%Rrc pIScsiPDUTxCur=%#p\n", rc, pImage->pIScsiPDUTxCur));
[31587]2514 return rc;
2515}
2516
2517/**
2518 * Process a received PDU.
2519 *
2520 * @return VBOX status code.
2521 * @param pImage The iSCSI connection state to be used.
[33540]2522 * @param paRes Pointer to the array of iSCSI response sections.
[31587]2523 * @param cnRes Number of valid iSCSI response sections in the array.
2524 */
2525static int iscsiRecvPDUProcess(PISCSIIMAGE pImage, PISCSIRES paRes, uint32_t cnRes)
2526{
2527 int rc = VINF_SUCCESS;
2528
2529 LogFlowFunc(("pImage=%#p paRes=%#p cnRes=%u\n", pImage, paRes, cnRes));
2530
2531 /* Validate the PDU first. */
2532 rc = iscsiValidatePDU(paRes, cnRes);
2533 if (RT_SUCCESS(rc))
2534 {
2535 ISCSIOPCODE cmd;
2536 const uint32_t *pcvResSeg = (const uint32_t *)paRes[0].pvSeg;
2537
2538 Assert(paRes[0].cbSeg > 9 * sizeof(uint32_t));
2539
2540 do
2541 {
2542 cmd = (ISCSIOPCODE)(RT_N2H_U32(pcvResSeg[0]) & ISCSIOP_MASK);
2543 switch (cmd)
2544 {
2545 case ISCSIOP_SCSI_RES:
2546 case ISCSIOP_SCSI_TASKMGMT_RES:
2547 case ISCSIOP_SCSI_DATA_IN:
2548 case ISCSIOP_R2T:
2549 case ISCSIOP_ASYN_MSG:
2550 case ISCSIOP_TEXT_RES:
2551 case ISCSIOP_LOGIN_RES:
2552 case ISCSIOP_LOGOUT_RES:
2553 case ISCSIOP_REJECT:
2554 case ISCSIOP_NOP_IN:
2555 if (serial_number_less(pImage->MaxCmdSN, RT_N2H_U32(pcvResSeg[8])))
2556 pImage->MaxCmdSN = RT_N2H_U32(pcvResSeg[8]);
2557 if (serial_number_less(pImage->ExpCmdSN, RT_N2H_U32(pcvResSeg[7])))
2558 pImage->ExpCmdSN = RT_N2H_U32(pcvResSeg[7]);
2559 break;
2560 default:
2561 rc = VERR_PARSE_ERROR;
[50304]2562 iscsiDumpPacket(pImage, (PISCSIREQ)paRes, cnRes, rc, false /* fRequest */);
[31587]2563 }
2564
2565 if (RT_FAILURE(rc))
2566 break;
2567
2568 if ( !pImage->FirstRecvPDU
2569 && (cmd != ISCSIOP_SCSI_DATA_IN || (RT_N2H_U32(pcvResSeg[0]) & ISCSI_STATUS_BIT)))
2570 {
2571 if (pImage->ExpStatSN == RT_N2H_U32(pcvResSeg[6]))
2572 {
2573 /* StatSN counter is not advanced on R2T and on a target SN update NOP-In. */
2574 if ( (cmd != ISCSIOP_R2T)
2575 && ((cmd != ISCSIOP_NOP_IN) || (RT_N2H_U32(pcvResSeg[4]) != ISCSI_TASK_TAG_RSVD)))
2576 pImage->ExpStatSN++;
2577 }
2578 else
2579 {
[50304]2580 rc = VERR_PARSE_ERROR;
2581 iscsiDumpPacket(pImage, (PISCSIREQ)paRes, cnRes, rc, false /* fRequest */);
2582 break;
[31587]2583 }
2584 }
2585
2586 if (pcvResSeg[4] != ISCSI_TASK_TAG_RSVD)
2587 {
2588 /*
2589 * This is a response from the target for a request from the initiator.
2590 * Get the request and update its state.
2591 */
2592 rc = iscsiRecvPDUUpdateRequest(pImage, paRes, cnRes);
[32225]2593 /* Try to send more PDUs now that we updated the MaxCmdSN field */
2594 if ( RT_SUCCESS(rc)
2595 && !pImage->pIScsiPDUTxCur)
2596 rc = iscsiSendPDUAsync(pImage);
[31587]2597 }
2598 else
2599 {
2600 /* This is a target initiated request (we handle only NOP-In request at the moment). */
2601 if ( cmd == ISCSIOP_NOP_IN
2602 && RT_N2H_U32(pcvResSeg[5]) != ISCSI_TASK_TAG_RSVD)
2603 {
2604 PISCSIPDUTX pIScsiPDUTx;
2605 uint32_t cnISCSIReq;
2606 uint32_t *paReqBHS;
2607
2608 LogFlowFunc(("Sending NOP-Out\n"));
2609
2610 /* Allocate a new PDU initialize it and put onto the waiting list. */
2611 pIScsiPDUTx = (PISCSIPDUTX)RTMemAllocZ(sizeof(ISCSIPDUTX));
2612 if (!pIScsiPDUTx)
2613 {
2614 rc = VERR_NO_MEMORY;
2615 break;
2616 }
2617 paReqBHS = &pIScsiPDUTx->aBHS[0];
2618 paReqBHS[0] = RT_H2N_U32(ISCSI_IMMEDIATE_DELIVERY_BIT | ISCSI_FINAL_BIT | ISCSIOP_NOP_OUT);
2619 paReqBHS[1] = RT_H2N_U32(0); /* TotalAHSLength=0,DataSementLength=0 */
2620 paReqBHS[2] = pcvResSeg[2]; /* copy LUN from NOP-In */
2621 paReqBHS[3] = pcvResSeg[3]; /* copy LUN from NOP-In */
2622 paReqBHS[4] = RT_H2N_U32(ISCSI_TASK_TAG_RSVD); /* ITT, reply */
2623 paReqBHS[5] = pcvResSeg[5]; /* copy TTT from NOP-In */
2624 paReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
2625 paReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
2626 paReqBHS[8] = 0; /* reserved */
2627 paReqBHS[9] = 0; /* reserved */
2628 paReqBHS[10] = 0; /* reserved */
2629 paReqBHS[11] = 0; /* reserved */
2630
2631 cnISCSIReq = 0;
2632 pIScsiPDUTx->aISCSIReq[cnISCSIReq].pvSeg = paReqBHS;
2633 pIScsiPDUTx->aISCSIReq[cnISCSIReq].cbSeg = sizeof(pIScsiPDUTx->aBHS);
2634 cnISCSIReq++;
2635 pIScsiPDUTx->cbSgLeft = sizeof(pIScsiPDUTx->aBHS);
2636 RTSgBufInit(&pIScsiPDUTx->SgBuf, pIScsiPDUTx->aISCSIReq, cnISCSIReq);
2637
[36133]2638 /*
2639 * Link the PDU to the list.
2640 * Insert at the front of the list to send the response as soon as possible
2641 * to avoid frequent reconnects for a slow connection when there are many PDUs
2642 * waiting.
2643 */
2644 iscsiPDUTxAdd(pImage, pIScsiPDUTx, true /* fFront */);
[31587]2645
2646 /* Start transfer of a PDU if there is no one active at the moment. */
2647 if (!pImage->pIScsiPDUTxCur)
2648 rc = iscsiSendPDUAsync(pImage);
2649 }
2650 }
2651 } while (0);
2652 }
[50304]2653 else
2654 iscsiDumpPacket(pImage, (PISCSIREQ)paRes, cnRes, rc, false /* fRequest */);
[31587]2655
2656 return rc;
2657}
2658
2659/**
[17796]2660 * Check the static (not dependent on the connection/session state) validity of an iSCSI response PDU.
2661 *
2662 * @returns VBOX status
[26959]2663 * @param paRes Pointer to array of iSCSI response sections.
2664 * @param cnRes Number of valid iSCSI response sections in the array.
[17796]2665 */
[31456]2666static int iscsiValidatePDU(PISCSIRES paRes, uint32_t cnRes)
[17796]2667{
[62748]2668 RT_NOREF1(cnRes);
[17796]2669 const uint32_t *pcrgResBHS;
2670 uint32_t hw0;
2671 Assert(cnRes >= 1);
2672 Assert(paRes[0].cbSeg >= ISCSI_BHS_SIZE);
2673
[31587]2674 LogFlowFunc(("paRes=%#p cnRes=%u\n", paRes, cnRes));
2675
[17796]2676 pcrgResBHS = (const uint32_t *)(paRes[0].pvSeg);
2677 hw0 = RT_N2H_U32(pcrgResBHS[0]);
2678 switch (hw0 & ISCSIOP_MASK)
2679 {
2680 case ISCSIOP_NOP_IN:
2681 /* NOP-In responses must not be split into several PDUs nor it may contain
2682 * ping data for target-initiated pings nor may both task tags be valid task tags. */
2683 if ( (hw0 & ISCSI_FINAL_BIT) == 0
2684 || ( RT_N2H_U32(pcrgResBHS[4]) == ISCSI_TASK_TAG_RSVD
2685 && RT_N2H_U32(pcrgResBHS[1]) != 0)
2686 || ( RT_N2H_U32(pcrgResBHS[4]) != ISCSI_TASK_TAG_RSVD
2687 && RT_N2H_U32(pcrgResBHS[5]) != ISCSI_TASK_TAG_RSVD))
2688 return VERR_PARSE_ERROR;
2689 break;
2690 case ISCSIOP_SCSI_RES:
2691 /* SCSI responses must not be split into several PDUs nor must the residual
2692 * bits be contradicting each other nor may the residual bits be set for PDUs
2693 * containing anything else but a completed command response. Underflow
2694 * is no reason for declaring a PDU as invalid, as the target may choose
2695 * to return less data than we assume to get. */
2696 if ( (hw0 & ISCSI_FINAL_BIT) == 0
2697 || ((hw0 & ISCSI_BI_READ_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_BI_READ_RESIDUAL_UNFL_BIT))
2698 || ((hw0 & ISCSI_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_RESIDUAL_UNFL_BIT))
2699 || ( ((hw0 & ISCSI_SCSI_RESPONSE_MASK) == 0)
2700 && ((hw0 & ISCSI_SCSI_STATUS_MASK) == SCSI_STATUS_OK)
2701 && (hw0 & ( ISCSI_BI_READ_RESIDUAL_OVFL_BIT | ISCSI_BI_READ_RESIDUAL_UNFL_BIT
2702 | ISCSI_RESIDUAL_OVFL_BIT))))
2703 return VERR_PARSE_ERROR;
[43448]2704 else
2705 LogFlowFunc(("good SCSI response, first word %#08x\n", RT_N2H_U32(pcrgResBHS[0])));
[17796]2706 break;
2707 case ISCSIOP_LOGIN_RES:
2708 /* Login responses must not contain contradicting transit and continue bits. */
2709 if ((hw0 & ISCSI_CONTINUE_BIT) && ((hw0 & ISCSI_TRANSIT_BIT) != 0))
2710 return VERR_PARSE_ERROR;
2711 break;
2712 case ISCSIOP_TEXT_RES:
2713 /* Text responses must not contain contradicting final and continue bits nor
2714 * may the final bit be set for PDUs containing a target transfer tag other than
2715 * the reserved transfer tag (and vice versa). */
2716 if ( (((hw0 & ISCSI_CONTINUE_BIT) && (hw0 & ISCSI_FINAL_BIT) != 0))
2717 || (((hw0 & ISCSI_FINAL_BIT) && (RT_N2H_U32(pcrgResBHS[5]) != ISCSI_TASK_TAG_RSVD)))
2718 || (((hw0 & ISCSI_FINAL_BIT) == 0) && (RT_N2H_U32(pcrgResBHS[5]) == ISCSI_TASK_TAG_RSVD)))
2719 return VERR_PARSE_ERROR;
2720 break;
2721 case ISCSIOP_SCSI_DATA_IN:
2722 /* SCSI Data-in responses must not contain contradicting residual bits when
2723 * status bit is set. */
2724 if ((hw0 & ISCSI_STATUS_BIT) && (hw0 & ISCSI_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_RESIDUAL_UNFL_BIT))
2725 return VERR_PARSE_ERROR;
2726 break;
2727 case ISCSIOP_LOGOUT_RES:
2728 /* Logout responses must not have the final bit unset and may not contain any
2729 * data or additional header segments. */
2730 if ( ((hw0 & ISCSI_FINAL_BIT) == 0)
2731 || (RT_N2H_U32(pcrgResBHS[1]) != 0))
2732 return VERR_PARSE_ERROR;
2733 break;
2734 case ISCSIOP_ASYN_MSG:
[31608]2735 /* Asynchronous Messages must not have the final bit unset and may not contain
[17796]2736 * an initiator task tag. */
2737 if ( ((hw0 & ISCSI_FINAL_BIT) == 0)
2738 || (RT_N2H_U32(pcrgResBHS[4]) != ISCSI_TASK_TAG_RSVD))
2739 return VERR_PARSE_ERROR;
2740 break;
2741 case ISCSIOP_SCSI_TASKMGMT_RES:
2742 case ISCSIOP_R2T:
2743 case ISCSIOP_REJECT:
2744 default:
2745 /* Do some logging, ignore PDU. */
[30941]2746 LogFlowFunc(("ignore unhandled PDU, first word %#08x\n", RT_N2H_U32(pcrgResBHS[0])));
[17796]2747 return VERR_PARSE_ERROR;
2748 }
2749 /* A target must not send PDUs with MaxCmdSN less than ExpCmdSN-1. */
2750
2751 if (serial_number_less(RT_N2H_U32(pcrgResBHS[8]), RT_N2H_U32(pcrgResBHS[7])-1))
2752 return VERR_PARSE_ERROR;
2753
2754 return VINF_SUCCESS;
2755}
2756
[31608]2757
[31587]2758/**
2759 * Prepares a PDU to transfer for the given command and adds it to the list.
2760 */
2761static int iscsiPDUTxPrepare(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd)
2762{
2763 int rc = VINF_SUCCESS;
2764 uint32_t *paReqBHS;
2765 size_t cbData = 0;
[31608]2766 size_t cbSegs = 0;
[31587]2767 PSCSIREQ pScsiReq;
2768 PISCSIPDUTX pIScsiPDU = NULL;
[17796]2769
[31587]2770 LogFlowFunc(("pImage=%#p pIScsiCmd=%#p\n", pImage, pIScsiCmd));
2771
2772 Assert(pIScsiCmd->enmCmdType == ISCSICMDTYPE_REQ);
2773
2774 pIScsiCmd->Itt = iscsiNewITT(pImage);
2775 pScsiReq = pIScsiCmd->CmdType.ScsiReq.pScsiReq;
2776
2777 if (pScsiReq->cT2ISegs)
2778 RTSgBufInit(&pScsiReq->SgBufT2I, pScsiReq->paT2ISegs, pScsiReq->cT2ISegs);
2779
2780 /*
2781 * Allocate twice as much entries as required for padding (worst case).
2782 * The additional segment is for the BHS.
2783 */
2784 size_t cI2TSegs = 2*(pScsiReq->cI2TSegs + 1);
[73097]2785 pIScsiPDU = (PISCSIPDUTX)RTMemAllocZ(RT_UOFFSETOF_DYN(ISCSIPDUTX, aISCSIReq[cI2TSegs]));
[31587]2786 if (!pIScsiPDU)
2787 return VERR_NO_MEMORY;
2788
2789 pIScsiPDU->pIScsiCmd = pIScsiCmd;
2790
2791 if (pScsiReq->enmXfer == SCSIXFER_FROM_TARGET)
2792 cbData = (uint32_t)pScsiReq->cbT2IData;
2793 else
2794 cbData = (uint32_t)pScsiReq->cbI2TData;
2795
2796 paReqBHS = pIScsiPDU->aBHS;
2797
2798 /* Setup the BHS. */
[40173]2799 paReqBHS[0] = RT_H2N_U32( ISCSI_FINAL_BIT | ISCSI_TASK_ATTR_SIMPLE | ISCSIOP_SCSI_CMD
2800 | (pScsiReq->enmXfer << 21)); /* I=0,F=1,Attr=Simple */
[31587]2801 paReqBHS[1] = RT_H2N_U32(0x00000000 | ((uint32_t)pScsiReq->cbI2TData & 0xffffff)); /* TotalAHSLength=0 */
2802 paReqBHS[2] = RT_H2N_U32(pImage->LUN >> 32);
2803 paReqBHS[3] = RT_H2N_U32(pImage->LUN & 0xffffffff);
2804 paReqBHS[4] = pIScsiCmd->Itt;
[48851]2805 paReqBHS[5] = RT_H2N_U32((uint32_t)cbData); Assert((uint32_t)cbData == cbData);
[31587]2806 paReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
2807 paReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
[44256]2808 memcpy(paReqBHS + 8, pScsiReq->abCDB, pScsiReq->cbCDB);
[32225]2809
2810 pIScsiPDU->CmdSN = pImage->CmdSN;
[31587]2811 pImage->CmdSN++;
2812
2813 /* Setup the S/G buffers. */
2814 uint32_t cnISCSIReq = 0;
2815 pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg = sizeof(pIScsiPDU->aBHS);
2816 pIScsiPDU->aISCSIReq[cnISCSIReq].pvSeg = pIScsiPDU->aBHS;
2817 cnISCSIReq++;
[31608]2818 cbSegs = sizeof(pIScsiPDU->aBHS);
[31587]2819 /* Padding is not necessary for the BHS. */
2820
2821 if (pScsiReq->cbI2TData)
2822 {
2823 for (unsigned cSeg = 0; cSeg < pScsiReq->cI2TSegs; cSeg++)
2824 {
2825 Assert(cnISCSIReq < cI2TSegs);
2826 pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg = pScsiReq->paI2TSegs[cSeg].cbSeg;
2827 pIScsiPDU->aISCSIReq[cnISCSIReq].pvSeg = pScsiReq->paI2TSegs[cSeg].pvSeg;
[31608]2828 cbSegs += pScsiReq->paI2TSegs[cSeg].cbSeg;
[31587]2829 cnISCSIReq++;
2830
2831 /* Add padding if necessary. */
2832 if (pScsiReq->paI2TSegs[cSeg].cbSeg & 3)
2833 {
2834 Assert(cnISCSIReq < cI2TSegs);
2835 pIScsiPDU->aISCSIReq[cnISCSIReq].pvSeg = &pImage->aPadding[0];
2836 pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg = 4 - (pScsiReq->paI2TSegs[cSeg].cbSeg & 3);
[31608]2837 cbSegs += pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg;
[31587]2838 cnISCSIReq++;
2839 }
2840 }
2841 }
2842
2843 pIScsiPDU->cISCSIReq = cnISCSIReq;
[31608]2844 pIScsiPDU->cbSgLeft = cbSegs;
[31587]2845 RTSgBufInit(&pIScsiPDU->SgBuf, pIScsiPDU->aISCSIReq, cnISCSIReq);
2846
2847 /* Link the PDU to the list. */
[31608]2848 iscsiPDUTxAdd(pImage, pIScsiPDU, false /* fFront */);
[31587]2849
2850 /* Start transfer of a PDU if there is no one active at the moment. */
2851 if (!pImage->pIScsiPDUTxCur)
2852 rc = iscsiSendPDUAsync(pImage);
2853
2854 return rc;
2855}
2856
2857
[17796]2858/**
[31587]2859 * Updates the state of a request from the PDU we received.
2860 *
2861 * @return VBox status code.
2862 * @param pImage iSCSI connection state to use.
2863 * @param paRes Pointer to array of iSCSI response sections.
2864 * @param cnRes Number of valid iSCSI response sections in the array.
2865 */
2866static int iscsiRecvPDUUpdateRequest(PISCSIIMAGE pImage, PISCSIRES paRes, uint32_t cnRes)
2867{
2868 int rc = VINF_SUCCESS;
2869 PISCSICMD pIScsiCmd;
2870 uint32_t *paResBHS;
2871
2872 LogFlowFunc(("pImage=%#p paRes=%#p cnRes=%u\n", pImage, paRes, cnRes));
2873
2874 Assert(cnRes == 1);
2875 Assert(paRes[0].cbSeg >= ISCSI_BHS_SIZE);
2876
2877 paResBHS = (uint32_t *)paRes[0].pvSeg;
2878
2879 pIScsiCmd = iscsiCmdGetFromItt(pImage, paResBHS[4]);
2880
2881 if (pIScsiCmd)
2882 {
2883 bool final = false;
2884 PSCSIREQ pScsiReq;
2885
2886 LogFlow(("Found SCSI command %#p for Itt=%#u\n", pIScsiCmd, paResBHS[4]));
2887
2888 Assert(pIScsiCmd->enmCmdType == ISCSICMDTYPE_REQ);
2889 pScsiReq = pIScsiCmd->CmdType.ScsiReq.pScsiReq;
2890
2891 final = !!(RT_N2H_U32(paResBHS[0]) & ISCSI_FINAL_BIT);
2892 ISCSIOPCODE cmd = (ISCSIOPCODE)(RT_N2H_U32(paResBHS[0]) & ISCSIOP_MASK);
2893 if (cmd == ISCSIOP_SCSI_RES)
2894 {
2895 /* This is the final PDU which delivers the status (and may be omitted if
2896 * the last Data-In PDU included successful completion status). Note
2897 * that ExpStatSN has been bumped already in iscsiRecvPDU. */
2898 if (!final || ((RT_N2H_U32(paResBHS[0]) & 0x0000ff00) != 0) || (RT_N2H_U32(paResBHS[6]) != pImage->ExpStatSN - 1))
2899 {
2900 /* SCSI Response in the wrong place or with a (target) failure. */
2901 LogFlow(("Wrong ExpStatSN value in PDU\n"));
2902 rc = VERR_PARSE_ERROR;
2903 }
2904 else
2905 {
2906 pScsiReq->status = RT_N2H_U32(paResBHS[0]) & 0x000000ff;
2907 size_t cbData = RT_N2H_U32(paResBHS[1]) & 0x00ffffff;
2908 void *pvSense = (uint8_t *)paRes[0].pvSeg + ISCSI_BHS_SIZE;
2909
2910 if (cbData >= 2)
2911 {
2912 uint32_t cbStat = RT_N2H_U32(((uint32_t *)pvSense)[0]) >> 16;
2913 if (cbStat + 2 > cbData)
2914 {
2915 rc = VERR_BUFFER_OVERFLOW;
2916 }
2917 else
2918 {
2919 /* Truncate sense data if it doesn't fit into the buffer. */
2920 pScsiReq->cbSense = RT_MIN(cbStat, pScsiReq->cbSense);
[44256]2921 memcpy(pScsiReq->abSense, (uint8_t *)pvSense + 2,
[31587]2922 RT_MIN(paRes[0].cbSeg - ISCSI_BHS_SIZE - 2, pScsiReq->cbSense));
2923 }
2924 }
2925 else if (cbData == 1)
2926 rc = VERR_PARSE_ERROR;
2927 else
2928 pScsiReq->cbSense = 0;
2929 }
2930 iscsiCmdComplete(pImage, pIScsiCmd, rc);
2931 }
2932 else if (cmd == ISCSIOP_SCSI_DATA_IN)
2933 {
2934 /* A Data-In PDU carries some data that needs to be added to the received
2935 * data in response to the command. There may be both partial and complete
2936 * Data-In PDUs, so collect data until the status is included or the status
2937 * is sent in a separate SCSI Result frame (see above). */
2938 size_t cbData = RT_N2H_U32(paResBHS[1]) & 0x00ffffff;
2939 void *pvData = (uint8_t *)paRes[0].pvSeg + ISCSI_BHS_SIZE;
2940
2941 if (final && cbData > pScsiReq->cbT2IData)
2942 {
[31608]2943 /* The received PDU is bigger than what we requested.
[31587]2944 * Must not happen under normal circumstances and is a target error. */
2945 rc = VERR_BUFFER_OVERFLOW;
2946 }
2947 else
2948 {
2949 /* Copy data from the received PDU into the T2I segments. */
2950 size_t cbCopied = RTSgBufCopyFromBuf(&pScsiReq->SgBufT2I, pvData, cbData);
[62748]2951 Assert(cbCopied == cbData); NOREF(cbCopied);
[31587]2952
2953 if (final && (RT_N2H_U32(paResBHS[0]) & ISCSI_STATUS_BIT) != 0)
2954 {
2955 pScsiReq->status = RT_N2H_U32(paResBHS[0]) & 0x000000ff;
2956 pScsiReq->cbSense = 0;
2957 iscsiCmdComplete(pImage, pIScsiCmd, VINF_SUCCESS);
2958 }
2959 }
2960 }
2961 else
2962 rc = VERR_PARSE_ERROR;
2963 }
2964
[31608]2965 /* Log any errors here but ignore the PDU. */
2966 if (RT_FAILURE(rc))
2967 {
2968 LogRel(("iSCSI: Received malformed PDU from target %s (rc=%Rrc), ignoring\n", pImage->pszTargetName, rc));
[50304]2969 iscsiDumpPacket(pImage, (PISCSIREQ)paRes, cnRes, rc, false /* fRequest */);
[31608]2970 rc = VINF_SUCCESS;
2971 }
2972
[31587]2973 return rc;
2974}
2975
2976/**
[17796]2977 * Appends a key-value pair to the buffer. Normal ASCII strings (cbValue == 0) and large binary values
2978 * of a given length (cbValue > 0) are directly supported. Other value types must be converted to ASCII
2979 * by the caller. Strings must be in UTF-8 encoding.
2980 *
2981 * @returns VBOX status
2982 * @param pbBuf Pointer to the key-value buffer.
2983 * @param cbBuf Length of the key-value buffer.
2984 * @param pcbBufCurr Currently used portion of the key-value buffer.
[64272]2985 * @param pcszKey Pointer to a string containing the key.
2986 * @param pcszValue Pointer to either a string containing the value or to a large binary value.
[17796]2987 * @param cbValue Length of the binary value if applicable.
2988 */
2989static int iscsiTextAddKeyValue(uint8_t *pbBuf, size_t cbBuf, size_t *pcbBufCurr, const char *pcszKey,
2990 const char *pcszValue, size_t cbValue)
2991{
2992 size_t cbBufTmp = *pcbBufCurr;
2993 size_t cbKey = strlen(pcszKey);
2994 size_t cbValueEnc;
2995 uint8_t *pbCurr;
2996
2997 if (cbValue == 0)
2998 cbValueEnc = strlen(pcszValue);
2999 else
3000 cbValueEnc = cbValue * 2 + 2; /* 2 hex bytes per byte, 2 bytes prefix */
3001
3002 if (cbBuf < cbBufTmp + cbKey + 1 + cbValueEnc + 1)
3003 {
3004 /* Buffer would overflow, signal error. */
3005 return VERR_BUFFER_OVERFLOW;
3006 }
3007
3008 /*
3009 * Append a key=value pair (zero terminated string) to the end of the buffer.
3010 */
3011 pbCurr = pbBuf + cbBufTmp;
3012 memcpy(pbCurr, pcszKey, cbKey);
3013 pbCurr += cbKey;
3014 *pbCurr++ = '=';
3015 if (cbValue == 0)
3016 {
3017 memcpy(pbCurr, pcszValue, cbValueEnc);
3018 pbCurr += cbValueEnc;
3019 }
3020 else
3021 {
3022 *pbCurr++ = '0';
3023 *pbCurr++ = 'x';
3024 for (uint32_t i = 0; i < cbValue; i++)
3025 {
3026 uint8_t b;
3027 b = pcszValue[i];
3028 *pbCurr++ = NUM_2_HEX(b >> 4);
3029 *pbCurr++ = NUM_2_HEX(b & 0xf);
3030 }
3031 }
3032 *pbCurr = '\0';
3033 *pcbBufCurr = cbBufTmp + cbKey + 1 + cbValueEnc + 1;
3034
3035 return VINF_SUCCESS;
3036}
3037
3038
3039/**
3040 * Retrieve the value for a given key from the key=value buffer.
3041 *
[58170]3042 * @returns VBox status code.
[26959]3043 * @param pbBuf Buffer containing key=value pairs.
3044 * @param cbBuf Length of buffer with key=value pairs.
[64272]3045 * @param pcszKey Pointer to key for which to retrieve the value.
3046 * @param ppcszValue Pointer to value string pointer.
[17796]3047 */
3048static int iscsiTextGetKeyValue(const uint8_t *pbBuf, size_t cbBuf, const char *pcszKey, const char **ppcszValue)
3049{
3050 size_t cbKey = strlen(pcszKey);
3051
3052 while (cbBuf != 0)
3053 {
3054 size_t cbKeyValNull = strlen((const char *)pbBuf) + 1;
3055
3056 if (strncmp(pcszKey, (const char *)pbBuf, cbKey) == 0 && pbBuf[cbKey] == '=')
3057 {
3058 *ppcszValue = (const char *)(pbBuf + cbKey + 1);
3059 return VINF_SUCCESS;
3060 }
3061 pbBuf += cbKeyValNull;
3062 cbBuf -= cbKeyValNull;
3063 }
3064 return VERR_INVALID_NAME;
3065}
3066
3067
3068/**
3069 * Convert a long-binary value from a value string to the binary representation.
3070 *
3071 * @returns VBOX status
[64272]3072 * @param pcszValue Pointer to a string containing the textual value representation.
[17796]3073 * @param pbValue Pointer to the value buffer for the binary value.
3074 * @param pcbValue In: length of value buffer, out: actual length of binary value.
3075 */
3076static int iscsiStrToBinary(const char *pcszValue, uint8_t *pbValue, size_t *pcbValue)
3077{
3078 size_t cbValue = *pcbValue;
3079 char c1, c2, c3, c4;
3080 Assert(cbValue >= 1);
3081
3082 if (strlen(pcszValue) < 3)
3083 return VERR_PARSE_ERROR;
3084 if (*pcszValue++ != '0')
3085 return VERR_PARSE_ERROR;
3086 switch (*pcszValue++)
3087 {
3088 case 'x':
3089 case 'X':
3090 if (strlen(pcszValue) & 1)
3091 {
3092 c1 = *pcszValue++;
3093 *pbValue++ = HEX_2_NUM(c1);
3094 cbValue--;
3095 }
3096 while (*pcszValue != '\0')
3097 {
3098 if (cbValue == 0)
3099 return VERR_BUFFER_OVERFLOW;
3100 c1 = *pcszValue++;
3101 if ((c1 < '0' || c1 > '9') && (c1 < 'a' || c1 > 'f') && (c1 < 'A' || c1 > 'F'))
3102 return VERR_PARSE_ERROR;
3103 c2 = *pcszValue++;
3104 if ((c2 < '0' || c2 > '9') && (c2 < 'a' || c2 > 'f') && (c2 < 'A' || c2 > 'F'))
3105 return VERR_PARSE_ERROR;
3106 *pbValue++ = (HEX_2_NUM(c1) << 4) | HEX_2_NUM(c2);
3107 cbValue--;
3108 }
3109 *pcbValue -= cbValue;
3110 break;
3111 case 'b':
3112 case 'B':
3113 if ((strlen(pcszValue) & 3) != 0)
3114 return VERR_PARSE_ERROR;
3115 while (*pcszValue != '\0')
3116 {
3117 uint32_t temp;
3118 if (cbValue == 0)
3119 return VERR_BUFFER_OVERFLOW;
3120 c1 = *pcszValue++;
3121 if ((c1 < 'A' || c1 > 'Z') && (c1 < 'a' || c1 >'z') && (c1 < '0' || c1 > '9') && (c1 != '+') && (c1 != '/'))
3122 return VERR_PARSE_ERROR;
3123 c2 = *pcszValue++;
3124 if ((c2 < 'A' || c2 > 'Z') && (c2 < 'a' || c2 >'z') && (c2 < '0' || c2 > '9') && (c2 != '+') && (c2 != '/'))
3125 return VERR_PARSE_ERROR;
3126 c3 = *pcszValue++;
3127 if ((c3 < 'A' || c3 > 'Z') && (c3 < 'a' || c3 >'z') && (c3 < '0' || c3 > '9') && (c3 != '+') && (c3 != '/') && (c3 != '='))
3128 return VERR_PARSE_ERROR;
3129 c4 = *pcszValue++;
3130 if ( (c3 == '=' && c4 != '=')
3131 || ((c4 < 'A' || c4 > 'Z') && (c4 < 'a' || c4 >'z') && (c4 < '0' || c4 > '9') && (c4 != '+') && (c4 != '/') && (c4 != '=')))
3132 return VERR_PARSE_ERROR;
3133 temp = (B64_2_NUM(c1) << 18) | (B64_2_NUM(c2) << 12);
3134 if (c3 == '=') {
3135 if (*pcszValue != '\0')
3136 return VERR_PARSE_ERROR;
3137 *pbValue++ = temp >> 16;
3138 cbValue--;
3139 } else {
3140 temp |= B64_2_NUM(c3) << 6;
3141 if (c4 == '=') {
3142 if (*pcszValue != '\0')
3143 return VERR_PARSE_ERROR;
3144 if (cbValue < 2)
3145 return VERR_BUFFER_OVERFLOW;
3146 *pbValue++ = temp >> 16;
3147 *pbValue++ = (temp >> 8) & 0xff;
3148 cbValue -= 2;
3149 }
3150 else
3151 {
3152 temp |= B64_2_NUM(c4);
3153 if (cbValue < 3)
3154 return VERR_BUFFER_OVERFLOW;
3155 *pbValue++ = temp >> 16;
3156 *pbValue++ = (temp >> 8) & 0xff;
3157 *pbValue++ = temp & 0xff;
3158 cbValue -= 3;
3159 }
3160 }
3161 }
3162 *pcbValue -= cbValue;
3163 break;
3164 default:
3165 return VERR_PARSE_ERROR;
3166 }
3167 return VINF_SUCCESS;
3168}
3169
3170
[22258]3171/**
3172 * Retrieve the relevant parameter values and update the initiator state.
3173 *
[58170]3174 * @returns VBox status code.
[26959]3175 * @param pImage Current iSCSI initiator state.
3176 * @param pbBuf Buffer containing key=value pairs.
3177 * @param cbBuf Length of buffer with key=value pairs.
[22258]3178 */
3179static int iscsiUpdateParameters(PISCSIIMAGE pImage, const uint8_t *pbBuf, size_t cbBuf)
3180{
3181 int rc;
3182 const char *pcszMaxRecvDataSegmentLength = NULL;
3183 const char *pcszMaxBurstLength = NULL;
3184 const char *pcszFirstBurstLength = NULL;
3185 rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "MaxRecvDataSegmentLength", &pcszMaxRecvDataSegmentLength);
3186 if (rc == VERR_INVALID_NAME)
3187 rc = VINF_SUCCESS;
3188 if (RT_FAILURE(rc))
3189 return VERR_PARSE_ERROR;
3190 rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "MaxBurstLength", &pcszMaxBurstLength);
3191 if (rc == VERR_INVALID_NAME)
3192 rc = VINF_SUCCESS;
3193 if (RT_FAILURE(rc))
3194 return VERR_PARSE_ERROR;
3195 rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "FirstBurstLength", &pcszFirstBurstLength);
3196 if (rc == VERR_INVALID_NAME)
3197 rc = VINF_SUCCESS;
3198 if (RT_FAILURE(rc))
3199 return VERR_PARSE_ERROR;
3200 if (pcszMaxRecvDataSegmentLength)
3201 {
3202 uint32_t cb = pImage->cbSendDataLength;
3203 rc = RTStrToUInt32Full(pcszMaxRecvDataSegmentLength, 0, &cb);
3204 AssertRC(rc);
3205 pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
3206 }
3207 if (pcszMaxBurstLength)
3208 {
3209 uint32_t cb = pImage->cbSendDataLength;
3210 rc = RTStrToUInt32Full(pcszMaxBurstLength, 0, &cb);
3211 AssertRC(rc);
3212 pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
3213 }
3214 if (pcszFirstBurstLength)
3215 {
3216 uint32_t cb = pImage->cbSendDataLength;
3217 rc = RTStrToUInt32Full(pcszFirstBurstLength, 0, &cb);
3218 AssertRC(rc);
3219 pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
3220 }
3221 return VINF_SUCCESS;
3222}
3223
3224
[17796]3225static bool serial_number_less(uint32_t s1, uint32_t s2)
3226{
3227 return (s1 < s2 && s2 - s1 < 0x80000000) || (s1 > s2 && s1 - s2 > 0x80000000);
3228}
3229
[32225]3230static bool serial_number_greater(uint32_t s1, uint32_t s2)
3231{
3232 return (s1 < s2 && s2 - s1 > 0x80000000) || (s1 > s2 && s1 - s2 < 0x80000000);
3233}
[17796]3234
[32225]3235
[17796]3236#ifdef IMPLEMENT_TARGET_AUTH
3237static void chap_md5_generate_challenge(uint8_t *pbChallenge, size_t *pcbChallenge)
3238{
3239 uint8_t cbChallenge;
3240
3241 cbChallenge = RTrand_U8(CHAP_MD5_CHALLENGE_MIN, CHAP_MD5_CHALLENGE_MAX);
3242 RTrand_bytes(pbChallenge, cbChallenge);
3243 *pcbChallenge = cbChallenge;
3244}
3245#endif
3246
3247
3248static void chap_md5_compute_response(uint8_t *pbResponse, uint8_t id, const uint8_t *pbChallenge, size_t cbChallenge,
3249 const uint8_t *pbSecret, size_t cbSecret)
3250{
3251 RTMD5CONTEXT ctx;
3252 uint8_t bId;
3253
3254 bId = id;
3255 RTMd5Init(&ctx);
3256 RTMd5Update(&ctx, &bId, 1);
3257 RTMd5Update(&ctx, pbSecret, cbSecret);
3258 RTMd5Update(&ctx, pbChallenge, cbChallenge);
3259 RTMd5Final(pbResponse, &ctx);
3260}
3261
3262/**
[31098]3263 * Internal. - Wrapper around the extended select callback of the net interface.
3264 */
[31456]3265DECLINLINE(int) iscsiIoThreadWait(PISCSIIMAGE pImage, RTMSINTERVAL cMillies, uint32_t fEvents, uint32_t *pfEvents)
[31098]3266{
[38469]3267 return pImage->pIfNet->pfnSelectOneEx(pImage->Socket, fEvents, pfEvents, cMillies);
[31098]3268}
3269
3270/**
3271 * Internal. - Pokes a thread waiting for I/O.
3272 */
3273DECLINLINE(int) iscsiIoThreadPoke(PISCSIIMAGE pImage)
3274{
[38469]3275 return pImage->pIfNet->pfnPoke(pImage->Socket);
[31098]3276}
3277
3278/**
3279 * Internal. - Get the next request from the queue.
3280 */