ISCSI.cpp revision e4f367251aede667a6de69baa54ef9eb5f150871
2N/A * available from http://www.virtualbox.org. This file is free software;
2N/A#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)
2N/Atypedef enum SCSIDEVTYPE
2N/A SCSI_DEVTYPE_DISK = 0,
} SCSIDEVTYPE;
#define ISCSI_MY_VERSION 0
typedef enum ISCSIOPCODE
} ISCSIOPCODE;
typedef enum ISCSILOGINSTATUSCLASS
typedef enum ISCSISTATE
/** Not having a connection/session at all. */
} ISCSISTATE;
typedef enum ISCSIPDUFLAGS
ISCSIPDU_DEFAULT = 0,
typedef struct ISCSIPARAMETER
const char *pszParamName;
const char *pszParamValue;
typedef struct ISCSIRES
void *pvSeg;
} ISCSIRES;
typedef struct ISCSIREQ
const void *pcvSeg;
} ISCSIREQ;
typedef enum SCSIXFER
SCSIXFER_NONE = 0,
typedef struct SCSIREQ
void *pvCDB;
void *pvSense;
unsigned cI2TSegs;
unsigned cT2ISegs;
typedef struct SCSIREQASYNC
int rcSense;
unsigned cSenseRetries;
unsigned cI2TSegs;
unsigned cT2ISegs;
typedef enum ISCSICMDTYPE
ISCSICMDTYPE_REQ = 0,
} ISCSICMDTYPE;
typedef struct ISCSICMDSYNC
int rcCmd;
typedef struct ISCSICMD
void *pvUser;
} ScsiReq;
void *pvUser;
} Exec;
} CmdType;
typedef struct ISCSIPDUTX
unsigned cISCSIReq;
typedef struct ISCSIIMAGE
const char *pszFilename;
char *pszInitiatorName;
char *pszTargetName;
char *pszTargetAddress;
char *pszInitiatorUsername;
char *pszTargetUsername;
unsigned uOpenFlags;
/** Current state of the connection/session. */
bool FirstRecvPDU;
void *pvRecvPDUBuf;
char *pszHostname;
bool fAutomaticInitiatorName;
bool fHostIP;
volatile bool fRunning;
bool fCmdQueuingSupported;
bool fExtendedSelectSupported;
bool fRecvPDUBHS;
unsigned cCmdsWaiting;
unsigned cLogRelErrors;
} ISCSIIMAGE;
static int iscsiTextAddKeyValue(uint8_t *pbBuf, size_t cbBuf, size_t *pcbBufCurr, const char *pcszKey, const char *pcszValue, size_t cbValue);
static int iscsiTextGetKeyValue(const uint8_t *pbBuf, size_t cbBuf, const char *pcszKey, const char **ppcszValue);
#ifdef IMPLEMENT_TARGET_AUTH
static void chap_md5_compute_response(uint8_t *pbResponse, uint8_t id, const uint8_t *pbChallenge, size_t cbChallenge,
while ( pIScsiCmd
return pIScsiCmd;
while ( pIScsiCmd
if (pIScsiCmd)
if (pIScsiCmdPrev)
return pIScsiCmd;
if (pHead)
return pIScsiCmdHead;
int rc;
return VERR_NET_DEST_ADDRESS_REQUIRED;
return rc;
return rc;
return VERR_NO_MEMORY;
LogRel(("iSCSI: connect from initiator %s with source port %u\n", pImage->pszInitiatorName, pImage->ISID & 65535));
return VINF_SUCCESS;
int rc;
LogRel(("iSCSI: disconnect from initiator %s with source port %u\n", pImage->pszInitiatorName, pImage->ISID & 65535));
return rc;
char *pDst;
cbToRead = 0;
residual = ISCSI_BHS_SIZE; /* Do not read more than the BHS length before the true PDU length is known. */
if (cMilliesRemaining <= 0)
if (cbActuallyRead == 0)
if (cbToRead == 0)
if (cbToRead == 0)
if (cbToRead == 0)
if (residual == 0)
if (i >= cnResponse)
for (i++; i < cnResponse; i++)
return rc;
unsigned cBuf = 0;
for (i = 0; i < cnRequest; i++)
cBuf++;
cBuf++;
unsigned iBuf = 0;
for (i = 0; i < cnRequest; i++)
iBuf++;
iBuf++;
return rc;
char *pszPortEnd;
pcszPort++;
pcszPort++;
pcszPort++;
return rc;
int rc;
bool transit;
char *pszNext;
bool fParameterNeg = true;;
goto out;
csg = 0;
nsg = 0;
substate = 0;
transit = false;
cbBuf = 0;
goto out;
rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "InitiatorName", pImage->pszInitiatorName, 0);
goto out;
goto out;
goto out;
transit = true;
goto out;
goto out;
goto out;
rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_R", (const char *)aResponse, RTMD5HASHSIZE);
goto out;
transit = true;
if (fParameterNeg)
goto out;
fParameterNeg = false;
transit = true;
cnISCSIReq = 0;
cnISCSIReq++;
cnISCSIReq++;
cnISCSIRes = 0;
cnISCSIRes++;
cnISCSIRes++;
switch (loginStatusClass)
bool targetTransit;
const char *pcszAuthMethod;
transit = true;
substate = 0;
substate++;
const char *pcszChapAuthMethod;
const char *pcszChapIdxTarget;
const char *pcszChapChallengeStr;
substate++;
transit = true;
substate = 0;
substate = 0;
substate = 0;
const char *pcszTargetRedir;
goto restart;
goto out;
AssertMsgFailed(("%s: ignoring unexpected PDU with first word = %#08x\n", __FUNCTION__, RT_N2H_U32(aResBHS[0])));
out:
LogRel(("iSCSI: login to target %s %s\n", pImage->pszTargetName, RT_SUCCESS(rc) ? "successful" : "failed"));
return rc;
int rc;
cnISCSIReq++;
return VINF_SUCCESS;
int rc;
bool final = false;
Assert(pRequest->enmXfer != SCSIXFER_TO_FROM_TARGET); /**< @todo not yet supported, would require AHS. */
goto out;
cbData = 0;
aReqBHS[1] = RT_H2N_U32(0x00000000 | ((uint32_t)pRequest->cbI2TData & 0xffffff)); /* TotalAHSLength=0 */
cnISCSIReq++;
cnISCSIReq++;
goto out_release;
cbBufLength = 0;
cnISCSIRes++;
if (cbBufLength != 0 &&
cnISCSIRes++;
cnISCSIRes++;
if (!final || ((RT_N2H_U32(aResBHS[0]) & 0x0000ff00) != 0) || (RT_N2H_U32(aResBHS[6]) != pImage->ExpStatSN - 1))
ExpDataSN++;
out:
return rc;
* needs cleaning up of timeout/disconnect handling a bit, as otherwise
return rc;
* the iSCSI connection/session. */
switch (cmd)
case ISCSIOP_SCSI_RES:
case ISCSIOP_SCSI_DATA_IN:
case ISCSIOP_R2T:
case ISCSIOP_ASYN_MSG:
case ISCSIOP_TEXT_RES:
case ISCSIOP_LOGIN_RES:
case ISCSIOP_LOGOUT_RES:
case ISCSIOP_REJECT:
case ISCSIOP_NOP_IN:
uint32_t j;
for (j = 0; j < cnRes; j++)
cbSeg = 0;
if (cbSeg != 0)
for (j++; j < cnRes; j++)
cnISCSIReq = 0;
cnISCSIReq++;
return rc;
if (!fFront)
return rc;
return rc;
switch (cmd)
case ISCSIOP_SCSI_RES:
case ISCSIOP_SCSI_DATA_IN:
case ISCSIOP_R2T:
case ISCSIOP_ASYN_MSG:
case ISCSIOP_TEXT_RES:
case ISCSIOP_LOGIN_RES:
case ISCSIOP_LOGOUT_RES:
case ISCSIOP_REJECT:
case ISCSIOP_NOP_IN:
if (!pIScsiPDUTx)
cnISCSIReq = 0;
cnISCSIReq++;
return rc;
* Check the static (not dependent on the connection/session state) validity of an iSCSI response PDU.
case ISCSIOP_NOP_IN:
return VERR_PARSE_ERROR;
case ISCSIOP_SCSI_RES:
| ISCSI_RESIDUAL_OVFL_BIT))))
return VERR_PARSE_ERROR;
case ISCSIOP_LOGIN_RES:
return VERR_PARSE_ERROR;
case ISCSIOP_TEXT_RES:
return VERR_PARSE_ERROR;
case ISCSIOP_SCSI_DATA_IN:
return VERR_PARSE_ERROR;
case ISCSIOP_LOGOUT_RES:
return VERR_PARSE_ERROR;
case ISCSIOP_ASYN_MSG:
return VERR_PARSE_ERROR;
case ISCSIOP_R2T:
case ISCSIOP_REJECT:
return VERR_PARSE_ERROR;
return VERR_PARSE_ERROR;
return VINF_SUCCESS;
if (!pIScsiPDU)
return VERR_NO_MEMORY;
paReqBHS[1] = RT_H2N_U32(0x00000000 | ((uint32_t)pScsiReq->cbI2TData & 0xffffff)); /* TotalAHSLength=0 */
cnISCSIReq++;
cnISCSIReq++;
cnISCSIReq++;
return rc;
if (pIScsiCmd)
bool final = false;
if (!final || ((RT_N2H_U32(paResBHS[0]) & 0x0000ff00) != 0) || (RT_N2H_U32(paResBHS[6]) != pImage->ExpStatSN - 1))
LogRel(("iSCSI: Received malformed PDU from target %s (rc=%Rrc), ignoring\n", pImage->pszTargetName, rc));
return rc;
* Appends a key-value pair to the buffer. Normal ASCII strings (cbValue == 0) and large binary values
* of a given length (cbValue > 0) are directly supported. Other value types must be converted to ASCII
static int iscsiTextAddKeyValue(uint8_t *pbBuf, size_t cbBuf, size_t *pcbBufCurr, const char *pcszKey,
if (cbValue == 0)
return VERR_BUFFER_OVERFLOW;
if (cbValue == 0)
uint8_t b;
b = pcszValue[i];
return VINF_SUCCESS;
static int iscsiTextGetKeyValue(const uint8_t *pbBuf, size_t cbBuf, const char *pcszKey, const char **ppcszValue)
while (cbBuf != 0)
return VINF_SUCCESS;
return VERR_INVALID_NAME;
return VERR_PARSE_ERROR;
return VERR_PARSE_ERROR;
switch (*pcszValue++)
cbValue--;
if (cbValue == 0)
return VERR_BUFFER_OVERFLOW;
return VERR_PARSE_ERROR;
return VERR_PARSE_ERROR;
cbValue--;
return VERR_PARSE_ERROR;
if (cbValue == 0)
return VERR_BUFFER_OVERFLOW;
if ((c1 < 'A' || c1 > 'Z') && (c1 < 'a' || c1 >'z') && (c1 < '0' || c1 > '9') && (c1 != '+') && (c1 != '/'))
return VERR_PARSE_ERROR;
if ((c2 < 'A' || c2 > 'Z') && (c2 < 'a' || c2 >'z') && (c2 < '0' || c2 > '9') && (c2 != '+') && (c2 != '/'))
return VERR_PARSE_ERROR;
if ((c3 < 'A' || c3 > 'Z') && (c3 < 'a' || c3 >'z') && (c3 < '0' || c3 > '9') && (c3 != '+') && (c3 != '/') && (c3 != '='))
return VERR_PARSE_ERROR;
|| ((c4 < 'A' || c4 > 'Z') && (c4 < 'a' || c4 >'z') && (c4 < '0' || c4 > '9') && (c4 != '+') && (c4 != '/') && (c4 != '=')))
return VERR_PARSE_ERROR;
return VERR_PARSE_ERROR;
cbValue--;
return VERR_PARSE_ERROR;
return VERR_BUFFER_OVERFLOW;
return VERR_BUFFER_OVERFLOW;
return VERR_PARSE_ERROR;
return VINF_SUCCESS;
int rc;
return VERR_PARSE_ERROR;
return VERR_PARSE_ERROR;
return VERR_PARSE_ERROR;
if (pcszMaxBurstLength)
if (pcszFirstBurstLength)
return VINF_SUCCESS;
#ifdef IMPLEMENT_TARGET_AUTH
static void chap_md5_compute_response(uint8_t *pbResponse, uint8_t id, const uint8_t *pbChallenge, size_t cbChallenge,
DECLINLINE(int) iscsiIoThreadWait(PISCSIIMAGE pImage, RTMSINTERVAL cMillies, uint32_t fEvents, uint32_t *pfEvents)
int rc;
if (pIScsiCmd)
return pIScsiCmd;
return rc;
#ifdef DEBUG
if (pIScsiCmd)
if (pIScsiCmd)
if (pIScsiCmd)
while (pIScsiCmdHead)
while (pIScsiCmdHead)
int rc;
fEvents = 0;
while (pIScsiCmd)
case ISCSICMDTYPE_REQ:
case ISCSICMDTYPE_EXEC:
return VINF_SUCCESS;
int rc;
if (!pIScsiCmd)
return VERR_NO_MEMORY;
return rc;
int rc;
return rc;
if (fRetry)
if (fRetry)
return rc;
int rc;
if (!pIScsiCmd)
return VERR_NO_MEMORY;
return rc;
return rc;
bool fComplete = true;
fComplete = false;
if (fComplete)
if (pImage)
return rc;
int rc;
bool fLunEncoded = false;
bool fHostIPDef = 0;
goto out;
goto out;
goto out;
goto out;
goto out;
goto out;
"TargetName\0InitiatorName\0LUN\0TargetAddress\0InitiatorUsername\0InitiatorSecret\0TargetUsername\0TargetSecret\0WriteSplit\0Timeout\0HostIPStack\0"))
rc = vdIfError(pImage->pIfError, VERR_VD_ISCSI_UNKNOWN_CFG_VALUES, RT_SRC_POS, N_("iSCSI: configuration error: unknown configuration keys present"));
goto out;
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetName as string"));
goto out;
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorName as string"));
goto out;
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read LUN as string"));
goto out;
fLunEncoded = true;
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to convert LUN to integer"));
goto out;
if (!fLunEncoded)
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: LUN number out of range (0-16383)"));
goto out;
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetAddress as string"));
goto out;
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorUsername as string"));
goto out;
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorSecret as byte string"));
goto out;
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetUsername as string"));
goto out;
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetSecret as byte string"));
goto out;
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read WriteSplit as U32"));
goto out;
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read Timeout as U32"));
goto out;
rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read HostIPStack as boolean"));
goto out;
LogFunc(("Not opening the transport connection as IntNet IP stack is not available. Will return dummies\n"));
goto out;
goto out;
LogFunc(("Extended select is not supported by the interface but async I/O is requested -> %Rrc\n", rc));
goto out;
goto out;
goto out;
goto out;
return rc;
LogRel(("iSCSI: Unsupported SCSI peripheral device type %d for target %s\n", devType & SCSI_DEVTYPE_MASK, pImage->pszTargetName));
goto out;
if (uCmdQueue > 0)
goto out;
goto out;
goto out;
LogRel(("iSCSI: Could not get MODE SENSE info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
goto out;
RT_SRC_POS, N_("iSCSI: target address %s, target name %s, SCSI LUN %lld reports media sector count=%llu sector size=%u"),
RT_SRC_POS, N_("iSCSI: fallback capacity detectio for target address %s, target name %s, SCSI LUN %lld reports media sector count=%llu sector size=%u"),
goto out;
LogRel(("iSCSI: Could not check write cache of target %s, rc=%Rrc, got mode page %#x\n", pImage->pszTargetName, rc,aCachingModePage[0] & 0x3f));
out:
return rc;
return rc;
LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
int rc;
goto out;
|| !*pszFilename
goto out;
if (!pImage)
goto out;
LogFlowFunc(("target %s cVolume %d, cbSector %d\n", pImage->pszTargetName, pImage->cVolume, pImage->cbSector));
LogRel(("iSCSI: target address %s, target name %s, SCSI LUN %lld\n", pImage->pszTargetAddress, pImage->pszTargetName, pImage->LUN));
out:
return rc;
LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p ppBackendData=%#p", pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, ppBackendData));
return rc;
int rc;
return rc;
LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
int rc;
|| cbToRead == 0)
goto out;
*pcbActuallyRead = 0;
out:
return rc;
LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
int rc;
goto out;
*pcbPreRead = 0;
*pcbPostRead = 0;
*pcbWriteProcess = 0;
out:
return rc;
int rc;
return rc;
if (pImage)
if (pImage)
int rc;
if (pImage)
LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
return rc;
LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
int rc;
if (pImage)
goto out;
out:
return rc;
int rc;
if (pImage)
LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
return rc;
LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
int rc;
if (pImage)
goto out;
out:
return rc;
unsigned uImageFlags;
return uImageFlags;
unsigned uOpenFlags;
if (pImage)
uOpenFlags = 0;
return uOpenFlags;
int rc;
if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)))
goto out;
out:
return rc;
LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
int rc;
if (pImage)
return rc;
int rc;
if (pImage)
return rc;
int rc;
if (pImage)
return rc;
int rc;
if (pImage)
return rc;
int rc;
if (pImage)
return rc;
int rc;
if (pImage)
return rc;
int rc;
if (pImage)
return rc;
int rc;
if (pImage)
return rc;
int rc;
if (pImage)
return rc;
int rc;
if (pImage)
return rc;
if (pImage)
return VERR_INVALID_PARAMETER;
unsigned cT2ISegs = 0;
if (pReq)
return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
return rc;
LogFlowFunc(("pBackendData=%p uOffset=%llu pIoCtx=%#p cbToWrite=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
return VERR_INVALID_PARAMETER;
unsigned cI2TSegs = 0;
if (pReq)
return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
return rc;
if (pReq)
return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
return rc;
return rc;
return rc;
sizeof(VBOXHDDBACKEND),
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,