diff --git a/Distribution/package.sh b/Distribution/package.sh index ab498fb7..92825be4 100755 --- a/Distribution/package.sh +++ b/Distribution/package.sh @@ -1,7 +1,7 @@ # Package parameters NAME="iSCSI Initiator for OS X" BUNDLE_ID="com.github.iscsi-osx.iSCSIInitiator" -VERSION="1.0.0-beta3" +VERSION="1.0.0-beta4" # Output of final DMG RELEASE="../Release" diff --git a/Source/Kernel/Info.plist b/Source/Kernel/Info.plist index 4a560c89..a1385813 100644 --- a/Source/Kernel/Info.plist +++ b/Source/Kernel/Info.plist @@ -56,7 +56,7 @@ IOProviderClass ${NAME_PREFIX_U}_iSCSIInitiator IOUserClientClass - ${NAME_PREFIX_U}_iSCSIInitiatorClient + ${NAME_PREFIX_U}_iSCSIHBAUserClient Protocol Characteristics Physical Interconnect diff --git a/Source/Kernel/iSCSIHBATypes.h b/Source/Kernel/iSCSIHBATypes.h new file mode 100644 index 00000000..6a62efa3 --- /dev/null +++ b/Source/Kernel/iSCSIHBATypes.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2016, Nareg Sinenian + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __ISCSI_HBA_TYPES_H__ +#define __ISCSI_HBA_TYPES_H__ + +// If used in user-space, this header will need to include additional +// headers that define primitive fixed-size types. If used with the kernel, +// IOLib must be included for kernel memory allocation +#ifdef KERNEL +#include +#else +#include +#include +#endif + +// Kernel and user iSCSI types (shared) +#include "iSCSITypesShared.h" +#include "iSCSIKernelClasses.h" + +#include + +/*! Notification types send from the kernel to the user-space daemon. */ +enum iSCSIHBANotificationTypes { + + /*! An asynchronous iSCSI message. */ + kiSCSIHBANotificationAsyncMessage, + + /*! Notifies clients that the kernel extension or controller is going + * shut down. Clients should release all resources. */ + kiSCSIHBANotificationTerminate, + + /*! Notifies clients that a network connnectivity issue has + * caused the specified connection and session to be dropped. */ + kiSCSIHBANotificationTimeout, + + /*! Invalid notification message. */ + kiSCSIHBANotificationInvalid +}; + + +/*! Used to pass notifications from the kernel to the user-space daemon. + * The notification type is one of the notification types listed in + * the enumerated type iSCSINotificationTypes. */ +typedef struct { + + /*! Message haeder. */ + mach_msg_header_t header; + + /*! The notification type. */ + UInt8 notificationType; + + /*! Parameter associated with the notification (notification-specific). */ + UInt64 parameter1; + + /*! Parameter associated with the notification (notification-specific). */ + UInt64 parameter2; + + /*! Session identifier. */ + SessionIdentifier sessionId; + + /*! Connection identifier. */ + ConnectionIdentifier connectionId; + +} iSCSIHBANotificationMessage; + + +/*! Used to pass notifications from the kernel to the user-space daemon. + * The notification type is one of the notification types listed in + * the enumerated type iSCSINotificationTypes. */ +typedef struct { + + /*! The notification type. */ + UInt8 notificationType; + + /*! An asynchronous event code, see iSCSIPDUAsyncEvent. */ + UInt64 asyncEvent; + + /*! The logical unit identifier associated with the notification (this + * field is only populated for SCSI async messages and ignored for all + * other types of asyncEvents). */ + UInt64 LUN; + + /*! Session identifier. */ + SessionIdentifier sessionId; + + /*! Connection identifier. */ + ConnectionIdentifier connectionId; + +} iSCSIHBANotificationAsyncMessage; + + +/*! Function pointer indices. These are the functions that can be called + * indirectly by calling IOCallScalarMethod(). */ +enum functionNames { + kiSCSIOpenInitiator, + kiSCSICloseInitiator, + kiSCSICreateSession, + kiSCSIReleaseSession, + kiSCSISetSessionParameter, + kiSCSIGetSessionParameter, + kiSCSICreateConnection, + kiSCSIReleaseConnection, + kiSCSIActivateConnection, + kiSCSIActivateAllConnections, + kiSCSIDeactivateConnection, + kiSCSIDeactivateAllConnections, + kiSCSISendBHS, + kiSCSISendData, + kiSCSIRecvBHS, + kiSCSIRecvData, + kiSCSISetConnectionParameter, + kiSCSIGetConnectionParameter, + kiSCSIGetConnection, + kiSCSIGetNumConnections, + kiSCSIGetSessionIdForTargetIQN, + kiSCSIGetConnectionIdForPortalAddress, + kiSCSIGetSessionIds, + kiSCSIGetConnectionIds, + kiSCSICreateTargetIQNForSessionId, + kiSCSIGetPortalAddressForConnectionId, + kiSCSIGetPortalPortForConnectionId, + kiSCSIGetHostInterfaceForConnectionId, + kiSCSIInitiatorNumMethods +}; + +#endif /* defined(__ISCSI_HBA_TYPES_H__) */ diff --git a/Source/Kernel/iSCSIHBAUserClient.cpp b/Source/Kernel/iSCSIHBAUserClient.cpp new file mode 100644 index 00000000..a010a356 --- /dev/null +++ b/Source/Kernel/iSCSIHBAUserClient.cpp @@ -0,0 +1,1521 @@ +/* + * Copyright (c) 2016, Nareg Sinenian + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "iSCSIVirtualHBA.h" +#include "iSCSIHBAUserClient.h" +#include "iSCSITypesShared.h" +#include "iSCSITypesKernel.h" +#include + +/*! Required IOKit macro that defines the constructors, destructors, etc. */ +OSDefineMetaClassAndStructors(iSCSIHBAUserClient,IOUserClient); + +/*! The superclass is defined as a macro to follow IOKit conventions. */ +#define super IOUserClient + +/*! Array of methods that can be called by user-space. */ +const IOExternalMethodDispatch iSCSIHBAUserClient::methods[kiSCSIInitiatorNumMethods] = { + { + (IOExternalMethodAction) &iSCSIHBAUserClient::OpenInitiator, + 0, // Scalar input count + 0, // Structure input size + 0, // Scalar output count + 0 // Structure output size + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::CloseInitiator, + 0, + 0, + 0, + 0 + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::CreateSession, + 1, // Number of parameters in struct + kIOUCVariableStructureSize, // Packed parameters for session + 3, // Returned identifiers, error code + 0 + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::ReleaseSession, + 1, // Session ID + 0, + 0, + 0 + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::SetSessionParameter, + 3, // Session ID, param ID, param value + 0, + 0, + 0 + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::GetSessionParameter, + 2, // Session ID, param ID + 0, + 1, // param to get + 0 + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::CreateConnection, + 2, // Session ID, number of params + kIOUCVariableStructureSize, // Packed parameters for connection + 2, // Returned connection identifier, error code + 0 + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::ReleaseConnection, + 2, // Session ID, connection ID + 0, + 0, + 0 + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::ActivateConnection, + 2, // Session ID, connection ID + 0, + 0, + 0 + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::ActivateAllConnections, + 1, // Session ID + 0, + 0, // Return value + 0 + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::DeactivateConnection, + 2, // Session ID, connection ID + 0, + 0, // Return value + 0 + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::DeactivateAllConnections, + 1, // Session ID + 0, + 0, // Return value + 0 + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::SendBHS, + 0, // Session ID, connection ID + sizeof(struct __iSCSIPDUCommonBHS), // Buffer to send + 0, // Return value + 0 + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::SendData, + 2, // Session ID, connection ID + kIOUCVariableStructureSize, // Data is a variable size block + 0, + 0 + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::RecvBHS, + 2, // Session ID, connection ID + 0, + 0, + sizeof(struct __iSCSIPDUCommonBHS), // Receive buffer + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::RecvData, + 2, // Session ID, connection ID + 0, + 0, + kIOUCVariableStructureSize, // Receive buffer + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::SetConnectionParameter, + 4, // Session ID, connection ID, param ID, param value + 0, + 0, + 0 + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::GetConnectionParameter, + 3, // Session ID, connection ID, param ID + 0, + 1, // param to get + 0 + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::GetConnection, + 1, // Session ID + 0, + 1, // Returned connection identifier + 0 + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::GetNumConnections, + 1, // Session ID + 0, + 1, // Returned number of connections + 0 + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::GetSessionIdForTargetIQN, + 0, + kIOUCVariableStructureSize, // Target name + 1, // Returned session identifier + 0 + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::GetConnectionIdForPortalAddress, + 1, // Session ID + kIOUCVariableStructureSize, // Connection address structure + 1, // Returned connection identifier + 0 + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::GetSessionIds, + 0, + 0, + 1, // Returned session count + kIOUCVariableStructureSize // List of session identifiers + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::GetConnectionIds, + 1, // Session ID + 0, + 1, // Returned connection count + kIOUCVariableStructureSize // List of connection ids + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::GetTargetIQNForSessionId, + 1, // Session ID + 0, + 0, + kIOUCVariableStructureSize // Target name + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::GetPortalAddressForConnectionId, + 2, // Session ID, connection ID + 0, + 0, + kIOUCVariableStructureSize // Portal address (C string) + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::GetPortalPortForConnectionId, + 2, // Session ID, connection ID + 0, + 0, // Returned connection count + kIOUCVariableStructureSize // connection address structures + }, + { + (IOExternalMethodAction) &iSCSIHBAUserClient::GetHostInterfaceForConnectionId, + 2, // Session ID, connection ID + 0, + 0, // Returned connection count + kIOUCVariableStructureSize // connection address structures + } +}; + +IOReturn iSCSIHBAUserClient::externalMethod(uint32_t selector, + IOExternalMethodArguments * args, + IOExternalMethodDispatch * dispatch, + OSObject * target, + void * ref) +{ + // Sanity check the selector + if(selector >= kiSCSIInitiatorNumMethods) + return kIOReturnUnsupported; + + + // Call the appropriate function for the current instance of the class + return super::externalMethod(selector, + args, + (IOExternalMethodDispatch *)&iSCSIHBAUserClient::methods[selector], + this, + ref); +} + + +// Called as a result of user-space call to IOServiceOpen() +bool iSCSIHBAUserClient::initWithTask(task_t owningTask, + void * securityToken, + UInt32 type, + OSDictionary * properties) +{ + // Save owning task, securty token and type so that we can validate user + // as a root user (UID 0) for secure operations (e.g., adding an iSCSI + // target requires privileges). + this->owningTask = owningTask; + this->securityToken = securityToken; + this->type = type; + this->accessLock = IOLockAlloc(); + this->notificationPort = MACH_PORT_NULL; + + // Perform any initialization tasks here + return super::initWithTask(owningTask,securityToken,type,properties); +} + +//Called after initWithTask as a result of call to IOServiceOpen() +bool iSCSIHBAUserClient::start(IOService * provider) +{ + // Check to ensure that the provider is actually an iSCSI initiator + if((this->provider = OSDynamicCast(iSCSIVirtualHBA,provider)) == NULL) + return false; + + return super::start(provider); +} + +void iSCSIHBAUserClient::stop(IOService * provider) +{ + super::stop(provider); +} + +// Called as a result of user-space call to IOServiceClose() +IOReturn iSCSIHBAUserClient::clientClose() +{ + // Tell HBA to release any resources that aren't active (e.g., + // connections we started to establish but didn't activate) + + // Ensure that the connection has been closed (in case the user calls + // IOServiceClose() before calling our close() method + close(); + + if(accessLock) { + IOLockFree(accessLock); + accessLock = NULL; + } + + // Terminate ourselves + terminate(); + + return kIOReturnSuccess; +} + +// Called if the user-space client is terminated without calling +// IOServiceClose() or close() +IOReturn iSCSIHBAUserClient::clientDied() +{ + // Tell HBA to release any resources that aren't active (e.g., + // connections we started to establish but didn't activate) + + // Close the provider (decrease retain count) + close(); + + return super::clientDied(); +} + +/*! Invoked when a user-space application registers a notification port + * with this user client. + * @param port the port associated with the client connection. + * @param type the type. + * @param refCon a user reference value. + * @return an error code indicating the result of the operation. */ +IOReturn iSCSIHBAUserClient::registerNotificationPort(mach_port_t port, + UInt32 type, + io_user_reference_t refCon) +{ + notificationPort = port; + return kIOReturnSuccess; +} + +/*! Send a notification message to the user-space application. + * @param message details regarding the notification message. + * @return an error code indicating the result of the operation. */ +IOReturn iSCSIHBAUserClient::sendNotification(iSCSIHBANotificationMessage * message) +{ + if(notificationPort == MACH_PORT_NULL) + return kIOReturnNotOpen; + + if(isInactive() || provider == NULL) + return kIOReturnNotAttached; + + message->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,0); + message->header.msgh_size = sizeof(iSCSIHBANotificationMessage); + message->header.msgh_remote_port = notificationPort; + message->header.msgh_local_port = MACH_PORT_NULL; + message->header.msgh_reserved = 0; + message->header.msgh_id = 0; + + mach_msg_send_from_kernel_proper(&message->header,message->header.msgh_size); + return kIOReturnSuccess; +} + +/*! Sends a notification message to the user indicating that an + * iSCSI asynchronous event has occured. + * @param sessionId the session identifier. + * @param connectionId the connection identifier. + * @param event the asynchronsou event. + * @return an error code indicating the result of the operation. */ +IOReturn iSCSIHBAUserClient::sendAsyncMessageNotification(SessionIdentifier sessionId, + ConnectionIdentifier connectionId, + enum iSCSIPDUAsyncMsgEvent event) +{ + iSCSIHBANotificationAsyncMessage message; + message.notificationType = kiSCSIHBANotificationAsyncMessage; + message.asyncEvent = event; + message.sessionId = sessionId; + message.connectionId = connectionId; + + return sendNotification((iSCSIHBANotificationMessage*)&message); +} + +/*! Notifies clients that a network connnectivity issue has + * caused the specified connection and session to be dropped. + * @param sessionId session identifier. + * @param connectionId conncetion identifier. + * @return an error code indicating the result of the operation. */ +IOReturn iSCSIHBAUserClient::sendTimeoutMessageNotification(SessionIdentifier sessionId, + ConnectionIdentifier connectionId) +{ + iSCSIHBANotificationMessage message; + message.notificationType = kiSCSIHBANotificationTimeout; + message.sessionId = sessionId; + message.connectionId = connectionId; + + return sendNotification((iSCSIHBANotificationMessage*)&message); +} + +/*! Sends a notification message to the user indicating that the kernel + * extension will be terminating. + * @return an error code indicating the result of the operation. */ +IOReturn iSCSIHBAUserClient::sendTerminateMessageNotification() +{ + iSCSIHBANotificationMessage message; + message.notificationType = kiSCSIHBANotificationTerminate; + + return sendNotification(&message); +} + +// Invoked from user space remotely by calling iSCSIInitiatorOpen() +IOReturn iSCSIHBAUserClient::open() +{ + // Ensure that we are attached to a provider + if(isInactive() || provider == NULL) + return kIOReturnNotAttached; + + // Open the provider (iSCSIInitiator) for this client + if(provider->open(this)) + return kIOReturnSuccess; + + // At this point we couldn't open the client for the provider for some + // other reason + return kIOReturnNotOpen; +} + +// Invoked from user space remotely by calling iSCSIInitiatorClose() +IOReturn iSCSIHBAUserClient::close() +{ + // If we're not active or have no provider we're not attached + if(isInactive() || provider == NULL) + return kIOReturnNotAttached; + + // If the provider isn't open for us then return not open + else if(!provider->isOpen(this)) + return kIOReturnNotOpen; + + // At this point we're attached & open, close the connection + provider->close(this); + + return kIOReturnSuccess; +} + +/*! Dispatched function called from the device interface to this user + * client .*/ +IOReturn iSCSIHBAUserClient::OpenInitiator(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + return target->open(); +} + +/*! Dispatched function called from the device interface to this user + * client .*/ +IOReturn iSCSIHBAUserClient::CloseInitiator(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + return target->close(); +} + +/*! Dispatched function invoked from user-space to create new session. */ +IOReturn iSCSIHBAUserClient::CreateSession(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + // Create a new session and return session ID + SessionIdentifier sessionId; + ConnectionIdentifier connectionId; + + // Unpack the struct to get targetIQN, portalAddress, etc. + UInt64 kNumParams = *args->scalarInput; + + // We expect six input arguments to CreateSession... + if(kNumParams < 6) + return kIOReturnBadArgument; + + void * params[kNumParams]; + size_t paramSize[kNumParams]; + size_t header = sizeof(UInt64*)*kNumParams; + UInt8 * inputPos = ((UInt8*)args->structureInput)+header; + + for(int idx = 0; idx < kNumParams; idx++) { + paramSize[idx] = ((UInt64*)args->structureInput)[idx]; + params[idx] = inputPos; + inputPos += paramSize[idx]; + } + + OSString * targetIQN = OSString::withCString((const char *)params[0]); + OSString * portalAddress = OSString::withCString((const char *)params[1]); + OSString * portalPort = OSString::withCString((const char *)params[2]); + OSString * hostInterface = OSString::withCString((const char *)params[3]); + const sockaddr_storage * remoteAddress = (struct sockaddr_storage*)params[4]; + const sockaddr_storage * localAddress = (struct sockaddr_storage*)params[5]; + + IOLockLock(target->accessLock); + + // Create a connection + errno_t error = target->provider->CreateSession( + targetIQN,portalAddress,portalPort,hostInterface,remoteAddress, + localAddress,&sessionId,&connectionId); + + IOLockUnlock(target->accessLock); + + args->scalarOutput[0] = sessionId; + args->scalarOutput[1] = connectionId; + args->scalarOutput[2] = error; + args->scalarOutputCount = 3; + + targetIQN->release(); + portalAddress->release(); + portalPort->release(); + hostInterface->release(); + + return kIOReturnSuccess; +} + +/*! Dispatched function invoked from user-space to release session. */ +IOReturn iSCSIHBAUserClient::ReleaseSession(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + // Release the session with the specified ID + IOLockLock(target->accessLock); + target->provider->ReleaseSession(*args->scalarInput); + IOLockUnlock(target->accessLock); + + return kIOReturnSuccess; +} + +// TODO: Set session options only once, when session is still inactive... +IOReturn iSCSIHBAUserClient::SetSessionParameter(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + iSCSIVirtualHBA * hba = OSDynamicCast(iSCSIVirtualHBA,target->provider); + + if(args->scalarInputCount != 3) + return kIOReturnBadArgument; + + SessionIdentifier sessionId = (SessionIdentifier)args->scalarInput[0]; + enum iSCSIHBASessionParameters paramType = (enum iSCSIHBASessionParameters)args->scalarInput[1]; + UInt64 paramVal = args->scalarInput[2]; + + // Range-check input + if(sessionId >= kiSCSIMaxSessions) + return kIOReturnBadArgument; + + IOLockLock(target->accessLock); + + // Do nothing if session doesn't exist + iSCSISession * session = hba->sessionList[sessionId]; + + IOReturn retVal = kIOReturnSuccess; + + if(session) + { + switch(paramType) + { + case kiSCSIHBASODataPDUInOrder: + session->dataPDUInOrder = paramVal; + break; + case kiSCSIHBASODataSequenceInOrder: + session->dataSequenceInOrder = paramVal; + break; + case kiSCSIHBASODefaultTime2Retain: + session->defaultTime2Retain = paramVal; + break; + case kiSCSIHBASODefaultTime2Wait: + session->defaultTime2Wait = paramVal; + break; + case kiSCSIHBASOErrorRecoveryLevel: + session->errorRecoveryLevel = paramVal; + break; + case kiSCSIHBASOFirstBurstLength: + session->firstBurstLength = (UInt32)paramVal; + break; + case kiSCSIHBASOImmediateData: + session->immediateData = paramVal; + break; + case kiSCSIHBASOMaxConnections: + session->maxConnections = (ConnectionIdentifier)paramVal; + break; + case kiSCSIHBASOMaxOutstandingR2T: + session->maxOutStandingR2T = paramVal; + break; + case kiSCSIHBASOMaxBurstLength: + session->maxBurstLength = (UInt32)paramVal; + break; + case kiSCSIHBASOInitialR2T: + session->initialR2T = paramVal; + break; + case kiSCSIHBASOTargetPortalGroupTag: + session->targetPortalGroupTag = paramVal; + break; + case kiSCSIHBASOTargetSessionId: + session->targetSessionId = paramVal; + break; + + default: + retVal = kIOReturnBadArgument; + }; + } + else { + retVal = kIOReturnBadArgument; + } + + IOLockUnlock(target->accessLock); + return retVal; +} + +IOReturn iSCSIHBAUserClient::GetSessionParameter(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + iSCSIVirtualHBA * hba = OSDynamicCast(iSCSIVirtualHBA,target->provider); + + if(args->scalarInputCount != 2) + return kIOReturnBadArgument; + + SessionIdentifier sessionId = (SessionIdentifier)args->scalarInput[0]; + enum iSCSIHBASessionParameters paramType = (enum iSCSIHBASessionParameters)args->scalarInput[1]; + + // Range-check input + if(sessionId >= kiSCSIMaxSessions) + return kIOReturnBadArgument; + + IOLockLock(target->accessLock); + + // Do nothing if session doesn't exist + iSCSISession * session = hba->sessionList[sessionId]; + + IOReturn retVal = kIOReturnSuccess; + UInt64 * paramVal = args->scalarOutput; + + if(session) + { + switch(paramType) + { + case kiSCSIHBASODataPDUInOrder: + *paramVal = session->dataPDUInOrder; + break; + case kiSCSIHBASODataSequenceInOrder: + *paramVal = session->dataSequenceInOrder; + break; + case kiSCSIHBASODefaultTime2Retain: + *paramVal = session->defaultTime2Retain; + break; + case kiSCSIHBASODefaultTime2Wait: + *paramVal = session->defaultTime2Wait; + break; + case kiSCSIHBASOErrorRecoveryLevel: + *paramVal = session->errorRecoveryLevel; + break; + case kiSCSIHBASOFirstBurstLength: + *paramVal = session->firstBurstLength; + break; + case kiSCSIHBASOImmediateData: + *paramVal = session->immediateData; + break; + case kiSCSIHBASOMaxConnections: + *paramVal = session->maxConnections; + break; + case kiSCSIHBASOMaxOutstandingR2T: + *paramVal = session->maxOutStandingR2T; + break; + case kiSCSIHBASOMaxBurstLength: + *paramVal = session->maxBurstLength; + break; + case kiSCSIHBASOInitialR2T: + *paramVal = session->initialR2T; + break; + case kiSCSIHBASOTargetPortalGroupTag: + *paramVal = session->targetPortalGroupTag; + break; + case kiSCSIHBASOTargetSessionId: + *paramVal = session->targetSessionId; + break; + default: + retVal = kIOReturnBadArgument; + }; + } + else { + retVal = kIOReturnNotFound; + } + + IOLockUnlock(target->accessLock); + return retVal; +} + +/*! Dispatched function invoked from user-space to create new connection. */ +IOReturn iSCSIHBAUserClient::CreateConnection(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + SessionIdentifier sessionId = (SessionIdentifier)args->scalarInput[0]; + ConnectionIdentifier connectionId; + + // Unpack the struct to get targetIQN, portalAddress, etc. + UInt64 kNumParams = args->scalarInput[1]; + + // We expect six input arguments to CreateSession... + if(kNumParams < 5) + return kIOReturnBadArgument; + + void * params[kNumParams]; + size_t paramSize[kNumParams]; + size_t header = sizeof(UInt64*)*kNumParams; + UInt8 * inputPos = ((UInt8*)args->structureInput)+header; + + for(int idx = 0; idx < kNumParams; idx++) { + paramSize[idx] = ((UInt64*)args->structureInput)[idx]; + params[idx] = inputPos; + inputPos += paramSize[idx]; + } + + OSString * portalAddress = OSString::withCString((const char *)params[0]); + OSString * portalPort = OSString::withCString((const char *)params[1]); + OSString * hostInterface = OSString::withCString((const char *)params[2]); + const sockaddr_storage * remoteAddress = (struct sockaddr_storage*)params[3]; + const sockaddr_storage * localAddress = (struct sockaddr_storage*)params[4]; + + IOLockLock(target->accessLock); + + // Create a connection + errno_t error = target->provider->CreateConnection( + sessionId,portalAddress,portalPort,hostInterface,remoteAddress, + localAddress,&connectionId); + + IOLockUnlock(target->accessLock); + + args->scalarOutput[0] = connectionId; + args->scalarOutput[1] = error; + args->scalarOutputCount = 2; + + return kIOReturnSuccess; +} + +/*! Dispatched function invoked from user-space to release connection. */ +IOReturn iSCSIHBAUserClient::ReleaseConnection(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + iSCSIVirtualHBA * hba = OSDynamicCast(iSCSIVirtualHBA,target->provider); + + SessionIdentifier sessionId = (SessionIdentifier)args->scalarInput[0]; + ConnectionIdentifier connectionId = (ConnectionIdentifier)args->scalarInput[1]; + + // Range-check input + if(sessionId >= kiSCSIMaxSessions || connectionId >= kiSCSIMaxConnectionsPerSession) + return kIOReturnBadArgument; + + IOLockLock(target->accessLock); + + iSCSISession * session = hba->sessionList[sessionId]; + + // If this is the only connection, releasing the connection should + // release the session as well... + ConnectionIdentifier connectionCount = 0; + + if(session) { + // Iterate over list of connections to see how many are valid + for(ConnectionIdentifier connectionId = 0; connectionId < kiSCSIMaxConnectionsPerSession; connectionId++) + if(session->connections[connectionId]) + connectionCount++; + } + + if(connectionCount == 1) + target->provider->ReleaseSession(sessionId); + else + target->provider->ReleaseConnection(sessionId,connectionId); + + IOLockUnlock(target->accessLock); + return kIOReturnSuccess; +} + +IOReturn iSCSIHBAUserClient::ActivateConnection(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + IOLockLock(target->accessLock); + + *args->scalarOutput = + target->provider->ActivateConnection((SessionIdentifier)args->scalarInput[0], + (ConnectionIdentifier)args->scalarInput[1]); + IOLockUnlock(target->accessLock); + return kIOReturnSuccess; +} + +IOReturn iSCSIHBAUserClient::ActivateAllConnections(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + IOLockLock(target->accessLock); + + *args->scalarOutput = + target->provider->ActivateAllConnections((SessionIdentifier)args->scalarInput[0]); + + IOLockUnlock(target->accessLock); + return kIOReturnSuccess; +} + +IOReturn iSCSIHBAUserClient::DeactivateConnection(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + IOLockLock(target->accessLock); + + *args->scalarOutput = + target->provider->DeactivateConnection((SessionIdentifier)args->scalarInput[0], + (ConnectionIdentifier)args->scalarInput[1]); + + IOLockUnlock(target->accessLock); + return kIOReturnSuccess; +} + +IOReturn iSCSIHBAUserClient::DeactivateAllConnections(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + IOLockLock(target->accessLock); + + *args->scalarOutput = + target->provider->DeactivateAllConnections((SessionIdentifier)args->scalarInput[0]); + + IOLockUnlock(target->accessLock); + return kIOReturnSuccess; +} + +/*! Dispatched function invoked from user-space to send data + * over an existing, active connection. */ +IOReturn iSCSIHBAUserClient::SendBHS(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + // Validate input + if(args->structureInputSize != kiSCSIPDUBasicHeaderSegmentSize) + return kIOReturnNoSpace; + + IOLockLock(target->accessLock); + memcpy(&target->bhsBuffer,args->structureInput,kiSCSIPDUBasicHeaderSegmentSize); + IOLockUnlock(target->accessLock); + + return kIOReturnSuccess; +} + +/*! Dispatched function invoked from user-space to send data + * over an existing, active connection. */ +IOReturn iSCSIHBAUserClient::SendData(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + iSCSIVirtualHBA * hba = OSDynamicCast(iSCSIVirtualHBA,target->provider); + + SessionIdentifier sessionId = (SessionIdentifier)args->scalarInput[0]; + ConnectionIdentifier connectionId = (ConnectionIdentifier)args->scalarInput[1]; + + // Range-check input + if(sessionId >= kiSCSIMaxSessions || connectionId >= kiSCSIMaxConnectionsPerSession) + return kIOReturnBadArgument; + + IOLockLock(target->accessLock); + + // Do nothing if session doesn't exist + iSCSISession * session = hba->sessionList[sessionId]; + iSCSIConnection * connection = NULL; + + if(session) + connection = session->connections[connectionId]; + + const void * data = args->structureInput; + size_t length = args->structureInputSize; + + // Send data and return the result + IOReturn retVal = kIOReturnNotFound; + + if(connection) { + if(hba->SendPDU(session,connection,&(target->bhsBuffer),nullptr,data,length)) + retVal = kIOReturnError; + else + retVal = kIOReturnSuccess; + } + + IOLockUnlock(target->accessLock); + + return retVal; +} + +/*! Dispatched function invoked from user-space to receive data + * over an existing, active connection, and to retrieve the size of + * a user-space buffer that is required to hold the data. */ +IOReturn iSCSIHBAUserClient::RecvBHS(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + // Verify user-supplied buffer is large enough to hold BHS + if(args->structureOutputSize != kiSCSIPDUBasicHeaderSegmentSize) + return kIOReturnNoSpace; + + iSCSIVirtualHBA * hba = OSDynamicCast(iSCSIVirtualHBA,target->provider); + + SessionIdentifier sessionId = (SessionIdentifier)args->scalarInput[0]; + ConnectionIdentifier connectionId = (ConnectionIdentifier)args->scalarInput[1]; + + // Range-check input + if(sessionId >= kiSCSIMaxSessions || connectionId >= kiSCSIMaxConnectionsPerSession) + return kIOReturnBadArgument; + + IOLockLock(target->accessLock); + + // Do nothing if session doesn't exist + iSCSISession * session = hba->sessionList[sessionId]; + iSCSIConnection * connection = NULL; + + if(session) + connection = session->connections[connectionId]; + + // Receive data and return the result + IOReturn retVal = kIOReturnNotFound; + + iSCSIPDUTargetBHS * bhs = (iSCSIPDUTargetBHS*)args->structureOutput; + + if(connection) { + if(hba->RecvPDUHeader(session,connection,bhs,MSG_WAITALL)) + retVal = kIOReturnIOError; + else + retVal = kIOReturnSuccess; + } + + IOLockUnlock(target->accessLock); + + return retVal; +} + +/*! Dispatched function invoked from user-space to receive data + * over an existing, active connection, and to retrieve the size of + * a user-space buffer that is required to hold the data. */ +IOReturn iSCSIHBAUserClient::RecvData(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + iSCSIVirtualHBA * hba = OSDynamicCast(iSCSIVirtualHBA,target->provider); + + SessionIdentifier sessionId = (SessionIdentifier)args->scalarInput[0]; + ConnectionIdentifier connectionId = (ConnectionIdentifier)args->scalarInput[1]; + + // Range-check input + if(sessionId >= kiSCSIMaxSessions || connectionId >= kiSCSIMaxConnectionsPerSession) + return kIOReturnBadArgument; + + IOLockLock(target->accessLock); + + // Do nothing if session doesn't exist + iSCSISession * session = hba->sessionList[sessionId]; + iSCSIConnection * connection = NULL; + + if(session) + connection = session->connections[connectionId]; + + // Receive data and return the result + IOReturn retVal = kIOReturnNotFound; + + void * data = (void *)args->structureOutput; + size_t length = args->structureOutputSize; + + if(hba->RecvPDUData(session,connection,data,length,MSG_WAITALL)) + retVal = kIOReturnIOError; + else + retVal = kIOReturnSuccess; + + IOLockUnlock(target->accessLock); + + return retVal; +} + +// TODO: Only allow user to set options when connection is inactive +// TODO: optimize socket parameters based on connection options +IOReturn iSCSIHBAUserClient::SetConnectionParameter(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + iSCSIVirtualHBA * hba = OSDynamicCast(iSCSIVirtualHBA,target->provider); + + if(args->scalarInputCount != 4) + return kIOReturnBadArgument; + + SessionIdentifier sessionId = (SessionIdentifier)args->scalarInput[0]; + ConnectionIdentifier connectionId = (ConnectionIdentifier)args->scalarInput[1]; + enum iSCSIHBAConnectionParameters paramType = (enum iSCSIHBAConnectionParameters)args->scalarInput[2]; + UInt64 paramVal = args->scalarInput[3]; + + // Range-check input + if(sessionId >= kiSCSIMaxSessions || connectionId >= kiSCSIMaxConnectionsPerSession) + return kIOReturnBadArgument; + + IOLockLock(target->accessLock); + + // Do nothing if session doesn't exist + iSCSISession * session = hba->sessionList[sessionId]; + iSCSIConnection * connection = NULL; + + if(session) + connection = session->connections[connectionId]; + + // Receive data and return the result + IOReturn retVal = kIOReturnNotFound; + + if(connection) + { + retVal = kIOReturnSuccess; + + switch(paramType) + { + case kiSCSIHBACOIFMarkInt: + connection->IFMarkInt = paramVal; + break; + case kiSCSIHBACOOFMarkInt: + connection->OFMarkInt = paramVal; + break; + case kiSCSIHBACOUseIFMarker: + connection->useIFMarker = paramVal; + break; + case kiSCSIHBACOUseOFMarker: + connection->useOFMarker = paramVal; + break; + case kiSCSIHBACOUseDataDigest: + connection->useDataDigest = paramVal; + break; + case kiSCSIHBACOUseHeaderDigest: + connection->useHeaderDigest = paramVal; + break; + case kiSCSIHBACOMaxRecvDataSegmentLength: + connection->maxRecvDataSegmentLength = (UInt32)paramVal; + break; + case kiSCSIHBACOMaxSendDataSegmentLength: + connection->maxSendDataSegmentLength = (UInt32)paramVal; + break; + case kiSCSIHBACOInitialExpStatSN: + connection->expStatSN = (UInt32)paramVal; + break; + + default: + retVal = kIOReturnBadArgument; + }; + } + + IOLockUnlock(target->accessLock); + + return retVal; +} + +IOReturn iSCSIHBAUserClient::GetConnectionParameter(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + iSCSIVirtualHBA * hba = OSDynamicCast(iSCSIVirtualHBA,target->provider); + + if(args->scalarInputCount != 3) + return kIOReturnBadArgument; + + SessionIdentifier sessionId = (SessionIdentifier)args->scalarInput[0]; + ConnectionIdentifier connectionId = (ConnectionIdentifier)args->scalarInput[1]; + enum iSCSIHBAConnectionParameters paramType = (enum iSCSIHBAConnectionParameters)args->scalarInput[2]; + UInt64 * paramVal = args->scalarOutput; + + // Range-check input + if(sessionId >= kiSCSIMaxSessions || connectionId >= kiSCSIMaxConnectionsPerSession) + return kIOReturnBadArgument; + + IOLockLock(target->accessLock); + + // Do nothing if session doesn't exist + iSCSISession * session = hba->sessionList[sessionId]; + iSCSIConnection * connection = NULL; + + if(session) + connection = session->connections[connectionId]; + + // Receive data and return the result + IOReturn retVal = kIOReturnNotFound; + + if(connection) { + retVal = kIOReturnSuccess; + + switch(paramType) + { + case kiSCSIHBACOIFMarkInt: + *paramVal = connection->IFMarkInt; + break; + case kiSCSIHBACOOFMarkInt: + *paramVal = connection->OFMarkInt; + break; + case kiSCSIHBACOUseIFMarker: + *paramVal = connection->useIFMarker; + break; + case kiSCSIHBACOUseOFMarker: + *paramVal = connection->useOFMarker; + break; + case kiSCSIHBACOUseDataDigest: + *paramVal = connection->useDataDigest; + break; + case kiSCSIHBACOUseHeaderDigest: + *paramVal = connection->useHeaderDigest; + break; + case kiSCSIHBACOMaxRecvDataSegmentLength: + *paramVal = connection->maxRecvDataSegmentLength; + break; + case kiSCSIHBACOMaxSendDataSegmentLength: + *paramVal = connection->maxSendDataSegmentLength; + break; + case kiSCSIHBACOInitialExpStatSN: + *paramVal = connection->expStatSN; + break; + + default: + return kIOReturnBadArgument; + }; + } + + IOLockUnlock(target->accessLock); + + return retVal; +} + +IOReturn iSCSIHBAUserClient::GetConnection(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + iSCSIVirtualHBA * hba = OSDynamicCast(iSCSIVirtualHBA,target->provider); + + SessionIdentifier sessionId = (SessionIdentifier)args->scalarInput[0]; + + // Range-check input + if(sessionId >= kiSCSIMaxSessions) + return kIOReturnBadArgument; + + IOLockLock(target->accessLock); + + iSCSISession * session = hba->sessionList[sessionId]; + IOReturn retVal = kIOReturnNotFound; + + if(session) { + + retVal = kIOReturnSuccess; + + args->scalarOutputCount = 1; + ConnectionIdentifier * connectionId = (ConnectionIdentifier *)args->scalarOutput; + + *connectionId = kiSCSIInvalidConnectionId; + + for(ConnectionIdentifier connectionIdx = 0; connectionIdx < kiSCSIMaxConnectionsPerSession; connectionIdx++) + { + if(session->connections[connectionIdx]) + { + *connectionId = connectionIdx; + break; + } + } + } + + IOLockUnlock(target->accessLock); + + return retVal; +} + +IOReturn iSCSIHBAUserClient::GetNumConnections(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + iSCSIVirtualHBA * hba = OSDynamicCast(iSCSIVirtualHBA,target->provider); + + SessionIdentifier sessionId = (SessionIdentifier)args->scalarInput[0]; + + // Range-check input + if(sessionId >= kiSCSIMaxSessions) + return kIOReturnBadArgument; + + IOLockLock(target->accessLock); + + iSCSISession * session = hba->sessionList[sessionId]; + IOReturn retVal = kIOReturnNotFound; + ConnectionIdentifier connectionCount = 0; + + if(session) { + // Iterate over list of connections to see how many are valid + for(ConnectionIdentifier connectionId = 0; connectionId < kiSCSIMaxConnectionsPerSession; connectionId++) + if(session->connections[connectionId]) + connectionCount++; + } + + *args->scalarOutput = connectionCount; + args->scalarOutputCount = 1; + + IOLockUnlock(target->accessLock); + + return retVal; +} + +IOReturn iSCSIHBAUserClient::GetSessionIdForTargetIQN(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + iSCSIVirtualHBA * hba = OSDynamicCast(iSCSIVirtualHBA,target->provider); + + const char * targetIQN = (const char *)args->structureInput; + + IOLockLock(target->accessLock); + OSNumber * identifier = (OSNumber*)(hba->targetList->getObject(targetIQN)); + + IOReturn retVal = kIOReturnNotFound; + + if(identifier) { + retVal = kIOReturnSuccess; + *args->scalarOutput = identifier->unsigned16BitValue();; + args->scalarOutputCount = 1; + } + + IOLockUnlock(target->accessLock); + + return retVal; +} + +IOReturn iSCSIHBAUserClient::GetConnectionIdForPortalAddress(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + iSCSIVirtualHBA * hba = OSDynamicCast(iSCSIVirtualHBA,target->provider); + + SessionIdentifier sessionId = (SessionIdentifier)args->scalarInput[0]; + + // Range-check input + if(sessionId == kiSCSIInvalidSessionId) + return kIOReturnBadArgument; + + IOLockLock(target->accessLock); + + iSCSISession * session = hba->sessionList[sessionId]; + IOReturn retVal = kIOReturnNotFound; + + if(session) { + + retVal = kIOReturnBadArgument; + + OSString * portalAddress = OSString::withCString((const char *)args->structureInput); + + if(portalAddress) + { + retVal = kIOReturnNotFound; + + iSCSIConnection * connection = NULL; + + *args->scalarOutput = kiSCSIInvalidConnectionId; + args->scalarOutputCount = 1; + + // Iterate over connections to find a matching address structure + for(ConnectionIdentifier connectionId = 0; connectionId < kiSCSIMaxConnectionsPerSession; connectionId++) + { + if(!(connection = session->connections[connectionId])) + continue; + + if(!connection->portalAddress->isEqualTo(portalAddress)) + continue; + + *args->scalarOutput = connectionId; + args->scalarOutputCount = 1; + + retVal = kIOReturnSuccess; + break; + } + } + } + + IOLockUnlock(target->accessLock); + + return retVal; +} + +IOReturn iSCSIHBAUserClient::GetSessionIds(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + if(args->structureOutputSize < sizeof(SessionIdentifier)*kiSCSIMaxSessions) + return kIOReturnBadArgument; + + iSCSIVirtualHBA * hba = OSDynamicCast(iSCSIVirtualHBA,target->provider); + + SessionIdentifier sessionCount = 0; + SessionIdentifier * sessionIds = (SessionIdentifier *)args->structureOutput; + + IOLockLock(target->accessLock); + + for(SessionIdentifier sessionIdx = 0; sessionIdx < kiSCSIMaxSessions; sessionIdx++) + { + if(hba->sessionList[sessionIdx]) + { + sessionIds[sessionCount] = sessionIdx; + sessionCount++; + } + } + + args->scalarOutputCount = 1; + *args->scalarOutput = sessionCount; + + IOLockUnlock(target->accessLock); + + return kIOReturnSuccess; +} + +IOReturn iSCSIHBAUserClient::GetConnectionIds(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + if(args->structureOutputSize < sizeof(ConnectionIdentifier)*kiSCSIMaxConnectionsPerSession) + return kIOReturnBadArgument; + + iSCSIVirtualHBA * hba = OSDynamicCast(iSCSIVirtualHBA,target->provider); + + SessionIdentifier sessionId = (SessionIdentifier)args->scalarInput[0]; + + // Range-check input + if(sessionId >= kiSCSIMaxSessions) + return kIOReturnBadArgument; + + IOLockLock(target->accessLock); + + iSCSISession * session = hba->sessionList[sessionId]; + IOReturn retVal = kIOReturnNotFound; + + if(session) + { + retVal = kIOReturnSuccess; + + ConnectionIdentifier connectionCount = 0; + ConnectionIdentifier * connectionIds = (ConnectionIdentifier *)args->structureOutput; + + // Find an empty connection slot to use for a new connection + for(ConnectionIdentifier index = 0; index < kiSCSIMaxConnectionsPerSession; index++) + { + if(session->connections[index]) + { + connectionIds[connectionCount] = index; + connectionCount++; + } + } + + args->scalarOutputCount = 1; + *args->scalarOutput = connectionCount; + } + + IOLockUnlock(target->accessLock); + + return retVal; +} + +IOReturn iSCSIHBAUserClient::GetTargetIQNForSessionId(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + iSCSIVirtualHBA * hba = OSDynamicCast(iSCSIVirtualHBA,target->provider); + + SessionIdentifier sessionId = (SessionIdentifier)args->scalarInput[0]; + + // Range-check input + if(sessionId >= kiSCSIMaxSessions) + return kIOReturnBadArgument; + + IOLockLock(target->accessLock); + + iSCSISession * session = hba->sessionList[sessionId]; + IOReturn retVal = kIOReturnNotFound; + + // Iterate over list of target name and find a matching session identifier + OSCollectionIterator * iterator = OSCollectionIterator::withCollection(hba->targetList); + + if(session && iterator) + { + OSObject * object; + + while((object = iterator->getNextObject())) + { + OSString * targetIQN = OSDynamicCast(OSString,object); + OSNumber * sessionIdNumber = OSDynamicCast(OSNumber,hba->targetList->getObject(targetIQN)); + + if(sessionIdNumber->unsigned16BitValue() == sessionId) + { + // Minimum length (either buffer size or size of + // target name, whichever is shorter) + size_t size = min(targetIQN->getLength(),args->structureOutputSize); + memcpy(args->structureOutput,targetIQN->getCStringNoCopy(),size); + + retVal = kIOReturnSuccess; + break; + } + } + } + + IOLockUnlock(target->accessLock); + + OSSafeRelease(iterator); + return retVal; +} + +IOReturn iSCSIHBAUserClient::GetPortalAddressForConnectionId(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + iSCSIVirtualHBA * hba = OSDynamicCast(iSCSIVirtualHBA,target->provider); + + SessionIdentifier sessionId = (SessionIdentifier)args->scalarInput[0]; + ConnectionIdentifier connectionId = (ConnectionIdentifier)args->scalarInput[1]; + + // Range-check input + if(sessionId >= kiSCSIMaxSessions || connectionId >= kiSCSIMaxConnectionsPerSession) + return kIOReturnBadArgument; + + IOLockLock(target->accessLock); + + // Do nothing if session doesn't exist + iSCSISession * session = hba->sessionList[sessionId]; + iSCSIConnection * connection = NULL; + + if(session) + connection = session->connections[connectionId]; + + // Receive data and return the result + IOReturn retVal = kIOReturnNotFound; + + if(connection) { + retVal = kIOReturnSuccess; + + const char * portalAddress = connection->portalAddress->getCStringNoCopy(); + size_t portalAddressLength = connection->portalAddress->getLength(); + + memset(args->structureOutput,0,args->structureOutputSize); + memcpy(args->structureOutput,portalAddress,min(args->structureOutputSize,portalAddressLength)); + } + + IOLockUnlock(target->accessLock); + return retVal; +} + +IOReturn iSCSIHBAUserClient::GetPortalPortForConnectionId(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + iSCSIVirtualHBA * hba = OSDynamicCast(iSCSIVirtualHBA,target->provider); + + SessionIdentifier sessionId = (SessionIdentifier)args->scalarInput[0]; + ConnectionIdentifier connectionId = (ConnectionIdentifier)args->scalarInput[1]; + + // Range-check input + if(sessionId >= kiSCSIMaxSessions || connectionId >= kiSCSIMaxConnectionsPerSession) + return kIOReturnBadArgument; + + IOLockLock(target->accessLock); + + // Do nothing if session doesn't exist + iSCSISession * session = hba->sessionList[sessionId]; + iSCSIConnection * connection = NULL; + + if(session) + connection = session->connections[connectionId]; + + // Receive data and return the result + IOReturn retVal = kIOReturnNotFound; + + if(connection) { + retVal = kIOReturnSuccess; + + const char * portalPort = connection->portalPort->getCStringNoCopy(); + size_t portalPortLength = connection->portalPort->getLength(); + + memset(args->structureOutput,0,args->structureOutputSize); + memcpy(args->structureOutput,portalPort,min(args->structureOutputSize,portalPortLength)); + } + + IOLockUnlock(target->accessLock); + return retVal; +} + +IOReturn iSCSIHBAUserClient::GetHostInterfaceForConnectionId(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args) +{ + iSCSIVirtualHBA * hba = OSDynamicCast(iSCSIVirtualHBA,target->provider); + + SessionIdentifier sessionId = (SessionIdentifier)args->scalarInput[0]; + ConnectionIdentifier connectionId = (ConnectionIdentifier)args->scalarInput[1]; + + // Range-check input + if(sessionId >= kiSCSIMaxSessions || connectionId >= kiSCSIMaxConnectionsPerSession) + return kIOReturnBadArgument; + + IOLockLock(target->accessLock); + + // Do nothing if session doesn't exist + iSCSISession * session = hba->sessionList[sessionId]; + iSCSIConnection * connection = NULL; + + if(session) + connection = session->connections[connectionId]; + + // Receive data and return the result + IOReturn retVal = kIOReturnNotFound; + + if(connection) { + retVal = kIOReturnSuccess; + + const char * hostInterface = connection->hostInteface->getCStringNoCopy(); + size_t hostInterfaceLength = connection->hostInteface->getLength(); + + memcpy(args->structureOutput,hostInterface,min(args->structureOutputSize,hostInterfaceLength+1)); + } + + IOLockUnlock(target->accessLock); + + return retVal; +} + + + diff --git a/Source/Kernel/iSCSIHBAUserClient.h b/Source/Kernel/iSCSIHBAUserClient.h new file mode 100644 index 00000000..55dd5eaf --- /dev/null +++ b/Source/Kernel/iSCSIHBAUserClient.h @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2016, Nareg Sinenian + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __ISCSI_USER_CLIENT_H__ +#define __ISCSI_USER_CLIENT_H__ + +#include + +#include "iSCSIKernelClasses.h" +#include "iSCSIHBATypes.h" +#include "iSCSIPDUShared.h" +#include "iSCSIVirtualHBA.h" +#include "iSCSITypesShared.h" + +class iSCSIHBAUserClient : public IOUserClient +{ + OSDeclareDefaultStructors(iSCSIHBAUserClient); + +public: + + /*! Invoked after initWithTask() as a result of the user-space application + * calling IOServiceOpen(). */ + virtual bool start(IOService * provider); + + /*! Called to stop this service. */ + virtual void stop(IOService * provider); + + /*! Invoked as a result of the user-space application calling + * IOServiceOpen(). */ + virtual bool initWithTask(task_t owningTask, + void * securityToken, + UInt32 type, + OSDictionary * properties); + + /*! Dispatched function called from the device interface to this user + * client .*/ + static IOReturn OpenInitiator(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + /*! Dispatched function called from the device interface to this user + * client .*/ + static IOReturn CloseInitiator(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + /*! Dispatched function invoked from user-space to create new session. */ + static IOReturn CreateSession(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + /*! Dispatched function invoked from user-space to release session. */ + static IOReturn ReleaseSession(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + static IOReturn SetSessionParameter(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + static IOReturn GetSessionParameter(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + /*! Dispatched function invoked from user-space to create new connection. */ + static IOReturn CreateConnection(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + /*! Dispatched function invoked from user-space to release connection. */ + static IOReturn ReleaseConnection(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + static IOReturn ActivateConnection(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + static IOReturn ActivateAllConnections(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + static IOReturn DeactivateConnection(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + static IOReturn DeactivateAllConnections(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + static IOReturn GetConnection(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + static IOReturn GetNumConnections(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + static IOReturn GetSessionIdForTargetIQN(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + static IOReturn GetConnectionIdForPortalAddress(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + static IOReturn GetSessionIds(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + static IOReturn GetConnectionIds(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + static IOReturn GetTargetIQNForSessionId(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + + static IOReturn GetPortalAddressForConnectionId(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + + static IOReturn GetPortalPortForConnectionId(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + static IOReturn GetHostInterfaceForConnectionId(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + /*! Dispatched function invoked from user-space to send data + * over an existing, active connection. */ + static IOReturn SendBHS(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + /*! Dispatched function invoked from user-space to send data + * over an existing, active connection. */ + static IOReturn SendData(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + /*! Dispatched function invoked from user-space to receive data + * over an existing, active connection, and to retrieve the size of + * a user-space buffer that is required to hold the data. */ + static IOReturn RecvBHS(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + /*! Dispatched function invoked from user-space to receive data + * over an existing, active connection, and to retrieve the size of + * a user-space buffer that is required to hold the data. */ + static IOReturn RecvData(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + static IOReturn SetConnectionParameter(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + static IOReturn GetConnectionParameter(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + static IOReturn GetActiveConnection(iSCSIHBAUserClient * target, + void * reference, + IOExternalMethodArguments * args); + + /*! Overrides IOUserClient's externalMethod to allow users to call + * dispatched functions defined by this subclass. */ + virtual IOReturn externalMethod(uint32_t selector, + IOExternalMethodArguments * args, + IOExternalMethodDispatch * dispatch, + OSObject * target, + void * ref); + + /*! Opens an exclusive connection to the iSCSI initiator device driver. The + * driver can handle multiple iSCSI targets with multiple LUNs. This + * function is remotely invoked by the user-space application. */ + virtual IOReturn open(); + + /*! Closes the connection to the iSCSI initiator device driver. Leaves + * iSCSI target connections intact. This function is remotely invoked + * by the user-space application. */ + virtual IOReturn close(); + + /*! Invoked when the user-space application calls IOServiceClose. */ + virtual IOReturn clientClose(); + + /*! Invoked when the user-space application is terminated without calling + * IOServiceClose or remotely invoking close(). */ + virtual IOReturn clientDied(); + + /*! Invoked when a user-space application registers a notification port + * with this user client. + * @param port the port associated with the client connection. + * @param type the type. + * @param refCon a user reference value. + * @return an error code indicating the result of the operation. */ + virtual IOReturn registerNotificationPort(mach_port_t port, + UInt32 type, + io_user_reference_t refCon); + + /*! Send a notification message to the user-space application. + * @param message details regarding the notification message. + * @return an error code indicating the result of the operation. */ + IOReturn sendNotification(iSCSIHBANotificationMessage * message); + + /*! Sends a notification message to the user indicating that an + * iSCSI asynchronous event has occured. + * @param sessionId the session identifier. + * @param connectionId the connection identifier. + * @param event the asynchronous event. + * @return an error code indicating the result of the operation. */ + IOReturn sendAsyncMessageNotification(SessionIdentifier sessionId, + ConnectionIdentifier connectionId, + enum iSCSIPDUAsyncMsgEvent event); + + /*! Notifies clients that a network connnectivity issue has + * caused the specified connection and session to be dropped. + * @param sessionId session identifier. + * @param connectionId conncetion identifier. + * @return an error code indicating the result of the operation. */ + IOReturn sendTimeoutMessageNotification(SessionIdentifier sessionId,ConnectionIdentifier connectionId); + + /*! Sends a notification message to the user indicating that the kernel + * extension will be terminating. + * @return an error code indicating the result of the operation. */ + IOReturn sendTerminateMessageNotification(); + + /*! Array of methods that can be called by user-space. */ + static const IOExternalMethodDispatch methods[kiSCSIInitiatorNumMethods]; + +private: + + /*! Points to the provider object (driver). The pointer is assigned + * when the start() function is called by the I/O Kit. */ + iSCSIVirtualHBA * provider; + + /*! Holds a basic header segment (buffer). Used when sending and + * receiving PDUs to and from the target. */ + iSCSIPDUInitiatorBHS bhsBuffer; + + /*! Identifies the Mach task (user-space) that opened a connection to this + * client. */ + task_t owningTask; + + /*! A security token that identifies user privileges. */ + void * securityToken; + + /*! A security type that identifies user privileges. */ + UInt32 type; + + /*! The notification port associated with a user-space connection. */ + mach_port_t notificationPort; + + /*! Access lock for kernel functions. */ + IOLock * accessLock; +}; + +#endif /* defined(__ISCSI_USER_CLIENT_H__) */ diff --git a/Source/Kernel/iSCSIInitiator.cpp b/Source/Kernel/iSCSIInitiator.cpp index 7fc120b5..7e683103 100644 --- a/Source/Kernel/iSCSIInitiator.cpp +++ b/Source/Kernel/iSCSIInitiator.cpp @@ -28,7 +28,7 @@ #include #include "iSCSIInitiator.h" -#include "iSCSIKernelInterfaceShared.h" +#include "iSCSIHBATypes.h" /*! Required IOKit macro that defines the constructors, destructors, etc. */ OSDefineMetaClassAndStructors(iSCSIInitiator,IOService); diff --git a/Source/Kernel/iSCSIKernelClasses.h b/Source/Kernel/iSCSIKernelClasses.h index 283f92eb..496ab791 100644 --- a/Source/Kernel/iSCSIKernelClasses.h +++ b/Source/Kernel/iSCSIKernelClasses.h @@ -39,7 +39,7 @@ #define iSCSIVirtualHBA ADD_PREFIX(iSCSIVirtualHBA) #define iSCSITaskQueue ADD_PREFIX(iSCSITaskQueue) #define iSCSIIOEventSource ADD_PREFIX(iSCSIIOEventSource) -#define iSCSIInitiatorClient ADD_PREFIX(iSCSIInitiatorClient) +#define iSCSIHBAUserClient ADD_PREFIX(iSCSIHBAUserClient) #define iSCSIInitiator ADD_PREFIX(iSCSIInitiator) #define kiSCSIVirtualHBA_IOClassName STRINGIFY_EVAL(iSCSIVirtualHBA) diff --git a/Source/Kernel/iSCSITypesKernel.h b/Source/Kernel/iSCSITypesKernel.h index 104824a1..edd9d162 100644 --- a/Source/Kernel/iSCSITypesKernel.h +++ b/Source/Kernel/iSCSITypesKernel.h @@ -46,7 +46,7 @@ typedef struct iSCSIConnection { UInt32 expStatSN; /*! Connection ID. */ - CID cid; // Might need this for ErrorRecovery (otherwise have to search through list for it) + ConnectionIdentifier cid; /*! Portal address (IPv4/IPv6/DNS address). */ OSString * portalAddress; @@ -146,7 +146,7 @@ typedef struct iSCSISession { /*! The initiator session ID, which is also used as the target ID within * this kernel extension since there is a 1-1 mapping. */ - SID sessionId; + SessionIdentifier sessionId; /*! Command sequence number to be used for the next initiator command. */ UInt32 cmdSN; @@ -203,10 +203,10 @@ typedef struct iSCSISession { UInt32 firstBurstLength; /*! Target session identifying handle. */ - TSIH targetSessionId; + TargetSessionIdentifier targetSessionId; /*! Target portal group tag. */ - TPGT targetPortalGroupTag; + TargetPortalGroupTag targetPortalGroupTag; } iSCSISession; diff --git a/Source/Kernel/iSCSIVirtualHBA.cpp b/Source/Kernel/iSCSIVirtualHBA.cpp index 3e1f0919..c21ef868 100644 --- a/Source/Kernel/iSCSIVirtualHBA.cpp +++ b/Source/Kernel/iSCSIVirtualHBA.cpp @@ -31,7 +31,7 @@ #include "iSCSITaskQueue.h" #include "iSCSITypesKernel.h" #include "iSCSIRFC3720Defaults.h" -#include "iSCSIInitiatorClient.h" +#include "iSCSIHBAUserClient.h" #include "crc32c.h" #include @@ -78,7 +78,7 @@ const UInt32 iSCSIVirtualHBA::kMaxTaskCount = 10; const UInt32 iSCSIVirtualHBA::kNumBytesPerAvgBW = 1048576; /*! Default task timeout for new tasks (milliseconds). */ -const UInt32 iSCSIVirtualHBA::kiSCSITaskTimeoutMs = 60000; +const UInt32 iSCSIVirtualHBA::kiSCSITaskTimeoutMs = 20000; /*! Default TCP timeout for new connections (seconds). */ const UInt32 iSCSIVirtualHBA::kiSCSITCPTimeoutSec = 1; @@ -397,8 +397,8 @@ void iSCSIVirtualHBA::HandleTimeout(SCSIParallelTaskIdentifier task) { // Determine the target identifier (session identifier) and connection // associated with this task and remove the task from the task queue. - SID sessionId = (UInt16)GetTargetIdentifier(task); - CID connectionId = *((UInt32*)GetHBADataPointer(task)); + SessionIdentifier sessionId = (UInt16)GetTargetIdentifier(task); + ConnectionIdentifier connectionId = *((UInt32*)GetHBADataPointer(task)); if(connectionId >= kMaxConnectionsPerSession) return; @@ -438,7 +438,7 @@ void iSCSIVirtualHBA::HandleTimeout(SCSIParallelTaskIdentifier task) /*! Handles connection timeouts. * @param sessionId the session associated with the timed-out connection. * @param connectionId the connection that timed out. */ -void iSCSIVirtualHBA::HandleConnectionTimeout(SID sessionId,CID connectionId) +void iSCSIVirtualHBA::HandleConnectionTimeout(SessionIdentifier sessionId,ConnectionIdentifier connectionId) { // If this is the last connection, release the session... iSCSISession * session; @@ -448,16 +448,29 @@ void iSCSIVirtualHBA::HandleConnectionTimeout(SID sessionId,CID connectionId) DBLog("iscsi: Connection timeout (sid: %d, cid: %d)\n",sessionId,connectionId); - CID connectionCount = 0; - for(CID connectionId = 0; connectionId < kiSCSIMaxConnectionsPerSession; connectionId++) + ConnectionIdentifier connectionCount = 0; + for(ConnectionIdentifier connectionId = 0; connectionId < kiSCSIMaxConnectionsPerSession; connectionId++) if(session->connections[connectionId]) connectionCount++; + iSCSIHBAUserClient * client = (iSCSIHBAUserClient*)getClient(); + // In the future add recovery here... if(connectionCount > 1) - ReleaseConnection(sessionId,connectionId); + DeactivateConnection(sessionId,connectionId); else - ReleaseSession(sessionId); + DeactivateAllConnections(sessionId); + + // Send a notification to the daemon; if the daemon does not respond then + // release the session or connection as appropriate + if(client->sendTimeoutMessageNotification(sessionId,connectionId) != kIOReturnSuccess) + { + if(connectionCount > 1) + ReleaseConnection(sessionId,connectionId); + else + ReleaseSession(sessionId); + } + } SCSIServiceResponse iSCSIVirtualHBA::ProcessParallelTask(SCSIParallelTaskIdentifier parallelTask) @@ -468,7 +481,7 @@ SCSIServiceResponse iSCSIVirtualHBA::ProcessParallelTask(SCSIParallelTaskIdentif SCSILogicalUnitNumber LUN = GetLogicalUnitNumber(parallelTask); SCSITaggedTaskIdentifier taskId = GetTaggedTaskIdentifier(parallelTask); - iSCSISession * session = sessionList[(SID)targetId]; + iSCSISession * session = sessionList[(SessionIdentifier)targetId]; if(!session) return kSCSIServiceResponse_FUNCTION_REJECTED; @@ -676,6 +689,7 @@ bool iSCSIVirtualHBA::ProcessTaskOnWorkloopThread(iSCSIVirtualHBA * owner, // Grab incoming bhs (we are guaranteed to have a basic header at this // point (iSCSIIOEventSource ensures that this is the case) iSCSIPDUTargetBHS bhs; + if(owner->RecvPDUHeader(session,connection,&bhs,0)) { DBLog("iscsi: Failed to get PDU header (sid: %d, cid: %d)\n", @@ -1058,7 +1072,7 @@ void iSCSIVirtualHBA::ProcessAsyncMsg(iSCSISession * session, DBLog("iscsi: Async Message (code %#x) received (sid: %d, cid: %d)\n", asyncEvent,session->sessionId,connection->cid); - iSCSIInitiatorClient * client = (iSCSIInitiatorClient*)getClient(); + iSCSIHBAUserClient * client = (iSCSIHBAUserClient*)getClient(); if(!client) { DBLog("iscsi: client is not available; async message may not be processed correctly\n"); } @@ -1281,8 +1295,8 @@ errno_t iSCSIVirtualHBA::CreateSession(OSString * targetIQN, OSString * hostInterface, const struct sockaddr_storage * portalSockaddr, const struct sockaddr_storage * hostSockaddr, - SID * sessionId, - CID * connectionId) + SessionIdentifier * sessionId, + ConnectionIdentifier * connectionId) { // Validate inputs if(!portalSockaddr || !hostSockaddr || !sessionId || !connectionId) @@ -1296,7 +1310,7 @@ errno_t iSCSIVirtualHBA::CreateSession(OSString * targetIQN, errno_t error = EAGAIN; // Find an open session slot - SID sessionIdx; + SessionIdentifier sessionIdx; for(sessionIdx = 0; sessionIdx < kMaxSessions; sessionIdx++) if(!sessionList[sessionIdx]) break; @@ -1381,7 +1395,7 @@ void iSCSIVirtualHBA::ReleaseAllSessions() { // Go through every connection for each session, and close sockets, // remove event sources, etc - for(SID index = 0; index < kMaxSessions; index++) + for(SessionIdentifier index = 0; index < kMaxSessions; index++) { if(!sessionList[index]) continue; @@ -1393,7 +1407,7 @@ void iSCSIVirtualHBA::ReleaseAllSessions() /*! Releases an iSCSI session, including all connections associated with that * session. * @param sessionId the session qualifier part of the ISID. */ -void iSCSIVirtualHBA::ReleaseSession(SID sessionId) +void iSCSIVirtualHBA::ReleaseSession(SessionIdentifier sessionId) { // Range-check inputs if(sessionId >= kMaxSessions) @@ -1408,7 +1422,7 @@ void iSCSIVirtualHBA::ReleaseSession(SID sessionId) DBLog("iscsi: Releasing session (sid %d)\n",sessionId); // Disconnect all connections - for(CID connectionId = 0; connectionId < kMaxConnectionsPerSession; connectionId++) + for(ConnectionIdentifier connectionId = 0; connectionId < kMaxConnectionsPerSession; connectionId++) { if(theSession->connections[connectionId]) ReleaseConnection(sessionId,connectionId); @@ -1445,13 +1459,13 @@ void iSCSIVirtualHBA::ReleaseSession(SID sessionId) * @param hostSockaddr the BSD socket structure used to identify the host adapter. * @param connectionId identifier for the new connection. * @return error code indicating result of operation. */ -errno_t iSCSIVirtualHBA::CreateConnection(SID sessionId, +errno_t iSCSIVirtualHBA::CreateConnection(SessionIdentifier sessionId, OSString * portalAddress, OSString * portalPort, OSString * hostInterface, const struct sockaddr_storage * portalSockaddr, const struct sockaddr_storage * hostSockaddr, - CID * connectionId) + ConnectionIdentifier * connectionId) { // Range-check inputs if(sessionId >= kMaxSessions || !portalSockaddr || !hostSockaddr || !connectionId) @@ -1463,7 +1477,7 @@ errno_t iSCSIVirtualHBA::CreateConnection(SID sessionId, return EINVAL; // Find an empty connection slot to use for a new connection - CID index; + ConnectionIdentifier index; for(index = 0; index < kMaxConnectionsPerSession; index++) if(!session->connections[index]) break; @@ -1595,8 +1609,8 @@ errno_t iSCSIVirtualHBA::CreateConnection(SID sessionId, /*! Frees a given iSCSI connection associated with a given session. * The session should be logged out using the appropriate PDUs. */ -void iSCSIVirtualHBA::ReleaseConnection(SID sessionId, - CID connectionId) +void iSCSIVirtualHBA::ReleaseConnection(SessionIdentifier sessionId, + ConnectionIdentifier connectionId) { // Range-check inputs if(sessionId >= kMaxSessions || connectionId >= kMaxConnectionsPerSession) @@ -1642,7 +1656,7 @@ void iSCSIVirtualHBA::ReleaseConnection(SID sessionId, * @param sessionId the session to deactivate. * @param connectionId the connection to deactivate. * @return error code indicating result of operation. */ -errno_t iSCSIVirtualHBA::ActivateConnection(SID sessionId,CID connectionId) +errno_t iSCSIVirtualHBA::ActivateConnection(SessionIdentifier sessionId,ConnectionIdentifier connectionId) { if(sessionId == kiSCSIInvalidSessionId || connectionId == kiSCSIInvalidConnectionId) return EINVAL; @@ -1687,7 +1701,7 @@ errno_t iSCSIVirtualHBA::ActivateConnection(SID sessionId,CID connectionId) * @param sessionId the session to deactivate. * @param connectionId the connection to deactivate. * @return error code indicating result of operation. */ -errno_t iSCSIVirtualHBA::ActivateAllConnections(SID sessionId) +errno_t iSCSIVirtualHBA::ActivateAllConnections(SessionIdentifier sessionId) { if(sessionId == kiSCSIInvalidSessionId) return EINVAL; @@ -1699,7 +1713,7 @@ errno_t iSCSIVirtualHBA::ActivateAllConnections(SID sessionId) return EINVAL; errno_t error = 0; - for(CID connectionId = 0; connectionId < kMaxConnectionsPerSession; connectionId++) + for(ConnectionIdentifier connectionId = 0; connectionId < kMaxConnectionsPerSession; connectionId++) if((error = ActivateConnection(sessionId,connectionId))) return error; @@ -1711,7 +1725,7 @@ errno_t iSCSIVirtualHBA::ActivateAllConnections(SID sessionId) * @param sessionId the session to deactivate. * @param connectionId the connection to deactivate. * @return error code indicating result of operation. */ -errno_t iSCSIVirtualHBA::DeactivateConnection(SID sessionId,CID connectionId) +errno_t iSCSIVirtualHBA::DeactivateConnection(SessionIdentifier sessionId,ConnectionIdentifier connectionId) { if(sessionId >= kMaxSessions || connectionId >= kMaxConnectionsPerSession) return EINVAL; @@ -1766,7 +1780,7 @@ errno_t iSCSIVirtualHBA::DeactivateConnection(SID sessionId,CID connectionId) * negotiated by the iSCSI daemon. * @param sessionId the session to deactivate. * @return error code indicating result of operation. */ -errno_t iSCSIVirtualHBA::DeactivateAllConnections(SID sessionId) +errno_t iSCSIVirtualHBA::DeactivateAllConnections(SessionIdentifier sessionId) { if(sessionId >= kMaxSessions) return EINVAL; @@ -1778,7 +1792,7 @@ errno_t iSCSIVirtualHBA::DeactivateAllConnections(SID sessionId) return EINVAL; errno_t error = 0; - for(CID connectionId = 0; connectionId < kMaxConnectionsPerSession; connectionId++) + for(ConnectionIdentifier connectionId = 0; connectionId < kMaxConnectionsPerSession; connectionId++) { if(session->connections[connectionId]) { @@ -1897,9 +1911,16 @@ errno_t iSCSIVirtualHBA::SendPDU(iSCSISession * session, // Update io vector count, send data msg.msg_iovlen = iovecCnt; size_t bytesSent = 0; - int result = sock_send(connection->socket,&msg,0,&bytesSent); + errno_t error; - return result; + if((error = sock_send(connection->socket,&msg,0,&bytesSent))) + { + DBLog("iscsi: sock_send error returned with code %d (sid: %d, cid: %d)\n",error,session->sessionId,connection->cid); + HandleConnectionTimeout(session->sessionId,connection->cid); + return error; + } + + return error; } @@ -1959,13 +1980,22 @@ errno_t iSCSIVirtualHBA::RecvPDUHeader(iSCSISession * session, // Bytes received from sock_receive call size_t bytesRecv; - errno_t result = sock_receive(connection->socket,&msg,MSG_WAITALL,&bytesRecv); - - if(result != 0) - DBLog("iscsi: sock_receive error returned with code %d (sid: %d, cid: %d)\n",result,session->sessionId,connection->cid); + errno_t error; + // Handle connection problems + if((error = sock_receive(connection->socket,&msg,MSG_WAITALL,&bytesRecv))) + { + if(error != EWOULDBLOCK) { + DBLog("iscsi: sock_receive error returned with code %d (sid: %d, cid: %d)\n",error,session->sessionId,connection->cid); + HandleConnectionTimeout(session->sessionId,connection->cid); + return error; + } + else + error = 0; + } + // Verify length; incoming PDUS from a target should have no AHS, verify. - if(bytesRecv < kiSCSIPDUBasicHeaderSegmentSize || bhs->totalAHSLength != 0) + if(bytesRecv < kiSCSIPDUBasicHeaderSegmentSize)// || bhs->totalAHSLength != 0) { DBLog("iscsi: Received incomplete PDU header: %zu bytes (sid: %d, cid: %d)\n",bytesRecv,session->sessionId,connection->cid); @@ -1990,12 +2020,13 @@ errno_t iSCSIVirtualHBA::RecvPDUHeader(iSCSISession * session, // Update command sequence numbers only if the PDU was not a data PDU // (unless the data PDU contains a SCSI service response) + if(bhs->opCode == kiSCSIPDUOpCodeDataIn) { iSCSIPDUDataInBHS * bhsDataIn = (iSCSIPDUDataInBHS *)bhs; if((bhsDataIn->flags & kiSCSIPDUDataInStatusFlag) == 0) - return result; + return error; } - + // Read and update the command sequence numbers bhs->maxCmdSN = OSSwapBigToHostInt32(bhs->maxCmdSN); bhs->expCmdSN = OSSwapBigToHostInt32(bhs->expCmdSN); @@ -2009,7 +2040,7 @@ errno_t iSCSIVirtualHBA::RecvPDUHeader(iSCSISession * session, if(bhs->opCode != kiSCSIPDUOpCodeR2T && bhs->statSN != 0xffffffff && bhs->initiatorTaskTag != 0xffffffff) OSIncrementAtomic(&connection->expStatSN); - return result; + return error; } /*! Receives a data segment over a kernel socket. If the specified length is @@ -2066,7 +2097,19 @@ errno_t iSCSIVirtualHBA::RecvPDUData(iSCSISession * session, msg.msg_iovlen = iovecCnt; size_t bytesRecv; - errno_t result = sock_receive(connection->socket,&msg,MSG_WAITALL,&bytesRecv); + errno_t error = 0; + + // Handle connection problems + if((error = sock_receive(connection->socket,&msg,MSG_WAITALL,&bytesRecv))) + { + if(error != EWOULDBLOCK) { + DBLog("iscsi: sock_receive error returned with code %d (sid: %d, cid: %d)\n",error,session->sessionId,connection->cid); + HandleConnectionTimeout(session->sessionId,connection->cid); + return error; + } + else + error = 0; + } // Verify digest if present if(connection->useDataDigest) @@ -2084,5 +2127,5 @@ errno_t iSCSIVirtualHBA::RecvPDUData(iSCSISession * session, } } - return result; + return error; } diff --git a/Source/Kernel/iSCSIVirtualHBA.h b/Source/Kernel/iSCSIVirtualHBA.h index 98fe6a04..223db542 100644 --- a/Source/Kernel/iSCSIVirtualHBA.h +++ b/Source/Kernel/iSCSIVirtualHBA.h @@ -41,7 +41,7 @@ #include "iSCSIKernelClasses.h" #include "iSCSITypesKernel.h" #include "iSCSITypesShared.h" -#include "iSCSIKernelInterfaceShared.h" +#include "iSCSIHBATypes.h" #include "iSCSIPDUKernel.h" // BSD socket includes @@ -63,7 +63,7 @@ * and then returned to the OS. */ class iSCSIVirtualHBA : public IOSCSIParallelInterfaceController { - friend class iSCSIInitiatorClient; + friend class iSCSIHBAUserClient; OSDeclareDefaultStructors(iSCSIVirtualHBA); @@ -150,7 +150,7 @@ class iSCSIVirtualHBA : public IOSCSIParallelInterfaceController /*! Handles connection timeouts. * @param sessionId the session associated with the timed-out connection. * @param connectionId the connection that timed out. */ - void HandleConnectionTimeout(SID sessionId,CID connectionId); + void HandleConnectionTimeout(SessionIdentifier sessionId,ConnectionIdentifier connectionId); /*! Processes a task passed down by SCSI target devices in driver stack. * @param parallelTask the task to process. @@ -208,8 +208,8 @@ class iSCSIVirtualHBA : public IOSCSIParallelInterfaceController OSString * hostInterface, const struct sockaddr_storage * portalSockaddr, const struct sockaddr_storage * hostSockaddr, - SID * sessionId, - CID * connectionId); + SessionIdentifier * sessionId, + ConnectionIdentifier * connectionId); /*! Releases all iSCSI sessions. */ void ReleaseAllSessions(); @@ -218,7 +218,7 @@ class iSCSIVirtualHBA : public IOSCSIParallelInterfaceController * session. Connections may be active or inactive when this function is * called. * @param sessionId the session qualifier part of the ISID. */ - void ReleaseSession(SID sessionId); + void ReleaseSession(SessionIdentifier sessionId); /*! Allocates a new iSCSI connection associated with the particular session. * @param sessionId the session to create a new connection for. @@ -229,17 +229,17 @@ class iSCSIVirtualHBA : public IOSCSIParallelInterfaceController * @param hostSockaddr the BSD socket structure used to identify the host adapter. * @param connectionId identifier for the new connection. * @return error code indicating result of operation. */ - errno_t CreateConnection(SID sessionId, + errno_t CreateConnection(SessionIdentifier sessionId, OSString * portalAddress, OSString * portalPort, OSString * hostInterface, const struct sockaddr_storage * portalSockaddr, const struct sockaddr_storage * hostSockaddr, - CID * connectionId); + ConnectionIdentifier * connectionId); /*! Frees a given iSCSI connection associated with a given session. * The session should be logged out using the appropriate PDUs. */ - void ReleaseConnection(SID sessionId,CID connectionId); + void ReleaseConnection(SessionIdentifier sessionId,ConnectionIdentifier connectionId); /*! Activates an iSCSI connection, indicating to the kernel that the iSCSI * daemon has negotiated security and operational parameters and that the @@ -247,7 +247,7 @@ class iSCSIVirtualHBA : public IOSCSIParallelInterfaceController * @param sessionId the session to deactivate. * @param connectionId the connection to deactivate. * @return error code indicating result of operation. */ - errno_t ActivateConnection(SID sessionId,CID connectionId); + errno_t ActivateConnection(SessionIdentifier sessionId,ConnectionIdentifier connectionId); /*! Activates all iSCSI connections for the session, indicating to the * kernel that the iSCSI daemon has negotiated security and operational @@ -255,19 +255,19 @@ class iSCSIVirtualHBA : public IOSCSIParallelInterfaceController * @param sessionId the session to deactivate. * @param connectionId the connection to deactivate. * @return error code indicating result of operation. */ - errno_t ActivateAllConnections(SID sessionId); + errno_t ActivateAllConnections(SessionIdentifier sessionId); /*! Deactivates an iSCSI connection so that parameters can be adjusted or * negotiated by the iSCSI daemon. * @param sessionId the session to deactivate. * @return error code indicating result of operation. */ - errno_t DeactivateConnection(SID sessionId,CID connectionId); + errno_t DeactivateConnection(SessionIdentifier sessionId,ConnectionIdentifier connectionId); /*! Deactivates all iSCSI connections so that parameters can be adjusted or * negotiated by the iSCSI daemon. * @param sessionId the session to deactivate. * @return error code indicating result of operation. */ - errno_t DeactivateAllConnections(SID sessionId); + errno_t DeactivateAllConnections(SessionIdentifier sessionId); /*! Sends data over a kernel socket associated with iSCSI. If the specified * data segment length is not a multiple of 4-bytes, padding bytes will be diff --git a/Source/User/iSCSI Framework/iSCSIDA.c b/Source/User/iSCSI Framework/iSCSIDA.c index be1f8873..d5e91dd8 100644 --- a/Source/User/iSCSI Framework/iSCSIDA.c +++ b/Source/User/iSCSI Framework/iSCSIDA.c @@ -93,11 +93,13 @@ void iSCSIDADiskMountComplete(DADiskRef disk,DADissenterRef dissenter,void * con void iSCSIDAUnmountApplierFunc(io_object_t entry, void * context) { iSCSIDiskOperationContext * opContext = (iSCSIDiskOperationContext*)context; - opContext->diskCount++; - DADiskRef disk = DADiskCreateFromIOMedia(kCFAllocatorDefault,opContext->session,entry); - DADiskUnmount(disk,opContext->options,iSCSIDADiskUnmountComplete,context); - CFRelease(disk); + + if(disk) { + DADiskUnmount(disk,opContext->options,iSCSIDADiskUnmountComplete,context); + CFRelease(disk); + opContext->diskCount++; + } } @@ -134,11 +136,14 @@ void iSCSIDAUnmountForTarget(DASessionRef session, void iSCSIDAMountApplierFunc(io_object_t entry, void * context) { iSCSIDiskOperationContext * opContext = (iSCSIDiskOperationContext*)context; - opContext->diskCount++; DADiskRef disk = DADiskCreateFromIOMedia(kCFAllocatorDefault,opContext->session,entry); - DADiskMount(disk,NULL,opContext->options,iSCSIDADiskMountComplete,context); - CFRelease(disk); + + if(disk) { + DADiskMount(disk,NULL,opContext->options,iSCSIDADiskMountComplete,context); + CFRelease(disk); + opContext->diskCount++; + } } /*! Mounts all IOMedia associated with a particular iSCSI session, and diff --git a/Source/User/iSCSI Framework/iSCSIDaemonInterface.c b/Source/User/iSCSI Framework/iSCSIDaemonInterface.c index aeef8120..23aa0728 100644 --- a/Source/User/iSCSI Framework/iSCSIDaemonInterface.c +++ b/Source/User/iSCSI Framework/iSCSIDaemonInterface.c @@ -724,13 +724,13 @@ errno_t iSCSIDaemonSetSharedSecret(iSCSIDaemonHandle handle, kAuthorizationExternalFormLength, kCFAllocatorDefault); - CFDataRef nodeIQNData = CFStringCreateExternalRepresentation(kCFAllocatorDefault,nodeIQN,kCFStringEncodingASCII,NULL); - CFDataRef sharedSecretData = CFStringCreateExternalRepresentation(kCFAllocatorDefault,sharedSecret,kCFStringEncodingASCII,NULL); + CFDataRef nodeIQNData = CFStringCreateExternalRepresentation(kCFAllocatorDefault,nodeIQN,kCFStringEncodingASCII,0); + CFDataRef sharedSecretData = CFStringCreateExternalRepresentation(kCFAllocatorDefault,sharedSecret,kCFStringEncodingASCII,0); iSCSIDMsgSetSharedSecretCmd cmd = iSCSIDMsgSetSharedSecretCmdInit; cmd.authorizationLength = (UInt32)CFDataGetLength(authData); - cmd.nodeIQNLength = CFDataGetLength(nodeIQNData); - cmd.secretLength = CFDataGetLength(sharedSecretData); + cmd.nodeIQNLength = (UInt32)CFDataGetLength(nodeIQNData); + cmd.secretLength = (UInt32)CFDataGetLength(sharedSecretData); errno_t error = iSCSIDaemonSendMsg(handle,(iSCSIDMsgGeneric *)&cmd, authData,nodeIQNData,sharedSecretData,NULL); @@ -780,7 +780,7 @@ errno_t iSCSIDaemonRemoveSharedSecret(iSCSIDaemonHandle handle, iSCSIDMsgRemoveSharedSecretCmd cmd = iSCSIDMsgRemoveSharedSecretCmdInit; cmd.authorizationLength = (UInt32)CFDataGetLength(authData); - cmd.nodeIQNLength = CFDataGetLength(nodeIQNData); + cmd.nodeIQNLength = (UInt32)CFDataGetLength(nodeIQNData); errno_t error = iSCSIDaemonSendMsg(handle,(iSCSIDMsgGeneric *)&cmd, authData,nodeIQNData,NULL); diff --git a/Source/User/iSCSI Framework/iSCSIIORegistry.c b/Source/User/iSCSI Framework/iSCSIIORegistry.c index 6a9f5f0a..31cfb3b2 100644 --- a/Source/User/iSCSI Framework/iSCSIIORegistry.c +++ b/Source/User/iSCSI Framework/iSCSIIORegistry.c @@ -38,7 +38,7 @@ #include #include -#include "iSCSIKernelInterfaceShared.h" +#include "iSCSIHBATypes.h" /*! Gets the iSCSIVirtualHBA object in the IO registry.*/ diff --git a/Source/User/iSCSI Framework/iSCSIPreferences.c b/Source/User/iSCSI Framework/iSCSIPreferences.c index c681309b..021c1463 100644 --- a/Source/User/iSCSI Framework/iSCSIPreferences.c +++ b/Source/User/iSCSI Framework/iSCSIPreferences.c @@ -60,6 +60,9 @@ CFStringRef kiSCSIPVTargetConfigTypeDiscovery = CFSTR("SendTargets"); /*! Preference key name for auto-login of target. */ CFStringRef kiSCSIPKAutoLogin = CFSTR("Automatic Login"); +/*! Preference key name for target persistence. */ +CFStringRef kiSCSIPKPersistent = CFSTR("Persistent"); + /*! Preference key name for error recovery level. */ CFStringRef kiSCSIPKErrorRecoveryLevel = CFSTR("Error Recovery Level"); @@ -213,6 +216,7 @@ CFMutableDictionaryRef iSCSIPreferencesCreateTargetDict() CFDictionaryAddValue(targetDict,kiSCSIPKAuthCHAPName,(void *)CFSTR("")); CFDictionaryAddValue(targetDict,kiSCSIPKAuth,kiSCSIPVAuthNone); CFDictionaryAddValue(targetDict,kiSCSIPKAutoLogin,kCFBooleanFalse); + CFDictionaryAddValue(targetDict,kiSCSIPKPersistent,kCFBooleanTrue); CFDictionaryAddValue(targetDict,kiSCSIPKMaxConnections,maxConnections); CFDictionaryAddValue(targetDict,kiSCSIPKErrorRecoveryLevel,errorRecoveryLevel); CFDictionaryAddValue(targetDict,kiSCSIPKHeaderDigest,kiSCSIPVDigestNone); @@ -410,8 +414,8 @@ enum iSCSIErrorRecoveryLevels iSCSIPreferencesGetErrorRecoveryLevelForTarget(iSC } iSCSIPortalRef iSCSIPreferencesCopyPortalForTarget(iSCSIPreferencesRef preferences, - CFStringRef targetIQN, - CFStringRef portalAddress) + CFStringRef targetIQN, + CFStringRef portalAddress) { // Get list of portals for this target CFMutableDictionaryRef portalsList = iSCSIPreferencesGetPortalsList(preferences,targetIQN,false); @@ -672,6 +676,43 @@ Boolean iSCSIPreferencesGetAutoLoginForTarget(iSCSIPreferencesRef preferences, return autoLogin; } +/*! Sets whether the a connection to the target should be re-established + * in the event of an interruption. + * @param preferences an iSCSI preferences object. + * @param targetIQN the target iSCSI qualified name (IQN). + * @param persistent true to make target persistent, false otherwise. */ +void iSCSIPreferencesSetPersistenceForTarget(iSCSIPreferencesRef preferences, + CFStringRef targetIQN, + Boolean persistent) +{ + CFMutableDictionaryRef targetDict = iSCSIPreferencesGetTargetDict(preferences,targetIQN,true); + + if(targetDict) { + if(persistent) + CFDictionarySetValue(targetDict,kiSCSIPKPersistent,kCFBooleanTrue); + else + CFDictionarySetValue(targetDict,kiSCSIPKPersistent,kCFBooleanFalse); + } +} + +/*! Gets whether the a connection to the target should be re-established + * in the event of an interruption. + * @param preferences an iSCSI preferences object. + * @param targetIQN the target iSCSI qualified name (IQN). */ +Boolean iSCSIPreferencesGetPersistenceForTarget(iSCSIPreferencesRef preferences, + CFStringRef targetIQN) +{ + CFMutableDictionaryRef targetDict = iSCSIPreferencesGetTargetDict(preferences,targetIQN,true); + Boolean persistent = false; + + if(targetDict) { + if(CFDictionaryGetValue(targetDict,kiSCSIPKPersistent) == kCFBooleanTrue) + persistent = true; + } + + return persistent; +} + /*! Adds a target object with a specified portal. * @param targetIQN the target iSCSI qualified name (IQN). * @param portal the portal object to set. */ @@ -935,6 +976,10 @@ void iSCSIPreferencesSetTargetIQN(iSCSIPreferencesRef preferences, CFStringRef existingIQN, CFStringRef newIQN) { + // Do not allow a change in IQN for dynamically configured targets + if(iSCSIPreferencesGetTargetConfigType(preferences,existingIQN) != kiSCSITargetConfigStatic) + return; + CFMutableDictionaryRef targetNodes = iSCSIPreferencesGetTargets(preferences,false); CFMutableDictionaryRef target = iSCSIPreferencesGetTargetDict(preferences,existingIQN,false); diff --git a/Source/User/iSCSI Framework/iSCSIPreferences.h b/Source/User/iSCSI Framework/iSCSIPreferences.h index 6ffaa26e..5fa41796 100644 --- a/Source/User/iSCSI Framework/iSCSIPreferences.h +++ b/Source/User/iSCSI Framework/iSCSIPreferences.h @@ -152,6 +152,22 @@ void iSCSIPreferencesSetAutoLoginForTarget(iSCSIPreferencesRef preferences, Boolean iSCSIPreferencesGetAutoLoginForTarget(iSCSIPreferencesRef preferences, CFStringRef targetIQN); +/*! Sets whether the a connection to the target should be re-established + * in the event of an interruption. + * @param preferences an iSCSI preferences object. + * @param targetIQN the target iSCSI qualified name (IQN). + * @param persistent true to make target persistent, false otherwise. */ +void iSCSIPreferencesSetPersistenceForTarget(iSCSIPreferencesRef preferences, + CFStringRef targetIQN, + Boolean persistent); + +/*! Gets whether the a connection to the target should be re-established + * in the event of an interruption. + * @param preferences an iSCSI preferences object. + * @param targetIQN the target iSCSI qualified name (IQN). */ +Boolean iSCSIPreferencesGetPersistenceForTarget(iSCSIPreferencesRef preferences, + CFStringRef targetIQN); + /*! Sets the maximum number of connections for the specified target. * @param preferences an iSCSI preferences object. * @param targetIQN the target iSCSI qualified name (IQN). diff --git a/Source/User/iSCSI Framework/iSCSIRFC3720Keys.h b/Source/User/iSCSI Framework/iSCSIRFC3720Keys.h index 203db8ed..583ba461 100644 --- a/Source/User/iSCSI Framework/iSCSIRFC3720Keys.h +++ b/Source/User/iSCSI Framework/iSCSIRFC3720Keys.h @@ -103,6 +103,10 @@ static CFStringRef kRFC3720_Value_SendTargetsAll = CFSTR("All"); static CFStringRef kRFC3720_Value_Yes = CFSTR("Yes"); static CFStringRef kRFC3720_Value_No = CFSTR("No"); +static CFStringRef kRFC3720_Value_Reject = CFSTR("Reject"); +static CFStringRef kRFC3720_Value_NotUnderstood = CFSTR("NotUnderstood"); +static CFStringRef kRFC3720_Value_Irrelevant = CFSTR("Irrelevant"); + // Other keys associated with sessions static CFStringRef kRFC3720_Key_TargetSessionId = CFSTR("TSIH"); diff --git a/Source/User/iSCSI Framework/iSCSITypes.c b/Source/User/iSCSI Framework/iSCSITypes.c index c8ec5b03..d3640cb3 100644 --- a/Source/User/iSCSI Framework/iSCSITypes.c +++ b/Source/User/iSCSI Framework/iSCSITypes.c @@ -625,17 +625,17 @@ void iSCSISessionConfigSetErrorRecoveryLevel(iSCSIMutableSessionConfigRef target } /*! Gets the target portal group tag. */ -TPGT iSCSISessionConfigGetTargetPortalGroupTag(iSCSISessionConfigRef target) +TargetPortalGroupTag iSCSISessionConfigGetTargetPortalGroupTag(iSCSISessionConfigRef target) { - TPGT targetPortalGroupTag = 0; + TargetPortalGroupTag targetPortalGroupTag = 0; CFNumberRef targetPortalGroupTagNum = CFDictionaryGetValue(target,kiSCSISessionConfigPortalGroupTagKey); CFNumberGetValue(targetPortalGroupTagNum,kCFNumberIntType,&targetPortalGroupTag); - return (TPGT)targetPortalGroupTag; + return (TargetPortalGroupTag)targetPortalGroupTag; } /*! Sets the target portal group tag. */ void iSCSISessionConfigSetTargetPortalGroupTag(iSCSIMutableSessionConfigRef target, - TPGT targetPortalGroupTag) + TargetPortalGroupTag targetPortalGroupTag) { CFNumberRef targetPortalGroupTagNum = CFNumberCreate(kCFAllocatorDefault,kCFNumberIntType,&targetPortalGroupTag); CFDictionarySetValue(target,kiSCSISessionConfigPortalGroupTagKey,targetPortalGroupTagNum); diff --git a/Source/User/iSCSI Framework/iSCSITypes.h b/Source/User/iSCSI Framework/iSCSITypes.h index 8daa0168..45e1123b 100644 --- a/Source/User/iSCSI Framework/iSCSITypes.h +++ b/Source/User/iSCSI Framework/iSCSITypes.h @@ -84,6 +84,8 @@ typedef CFMutableDictionaryRef iSCSIMutableConnectionConfigRef; #define kiSCSITypeDictionaryKeyCallbacks kCFTypeDictionaryKeyCallBacks #define kiSCSITypeDictionaryValueCallbacks kCFTypeDictionaryValueCallBacks +typedef CFTypeRef iSCSITypeRef; + /*! Error recovery levels. */ enum iSCSIErrorRecoveryLevels { @@ -497,11 +499,11 @@ void iSCSISessionConfigSetErrorRecoveryLevel(iSCSIMutableSessionConfigRef config enum iSCSIErrorRecoveryLevels errorRecoveryLevel); /*! Gets the target portal group tag for the session. */ -TPGT iSCSISessionConfigGetTargetPortalGroupTag(iSCSISessionConfigRef config); +TargetPortalGroupTag iSCSISessionConfigGetTargetPortalGroupTag(iSCSISessionConfigRef config); /*! Sets the target portal group tag for the session. */ void iSCSISessionConfigSetTargetPortalGroupTag(iSCSIMutableSessionConfigRef config, - TPGT targetPortalGroupTag); + TargetPortalGroupTag targetPortalGroupTag); /*! Gets the maximum number of connections. */ UInt32 iSCSISessionConfigGetMaxConnections(iSCSISessionConfigRef config); diff --git a/Source/User/iSCSI Framework/iSCSITypesShared.h b/Source/User/iSCSI Framework/iSCSITypesShared.h index dc1ebb5a..8eacdb18 100644 --- a/Source/User/iSCSI Framework/iSCSITypesShared.h +++ b/Source/User/iSCSI Framework/iSCSITypesShared.h @@ -34,16 +34,16 @@ typedef int errno_t; #endif /*! Session identifier. */ -typedef UInt16 SID; +typedef UInt16 SessionIdentifier; /*! Connection identifier. */ -typedef UInt32 CID; +typedef UInt32 ConnectionIdentifier; /*! Target portal group tag. */ -typedef UInt16 TPGT; +typedef UInt16 TargetPortalGroupTag; /*! Target session identifier. */ -typedef UInt16 TSIH; +typedef UInt16 TargetSessionIdentifier; /*! Session qualifier value for an invalid session. */ static const UInt16 kiSCSIInvalidSessionId = 0xFFFF; @@ -58,79 +58,79 @@ static const UInt16 kiSCSIMaxSessions = 16; static const UInt32 kiSCSIMaxConnectionsPerSession = 2; /*! An enumeration of configurable session parameters. */ -enum iSCSIKernelSessionOptTypes { +enum iSCSIHBASessionParameters { /*! Time to retain (UInt16). */ - kiSCSIKernelSODefaultTime2Retain, + kiSCSIHBASODefaultTime2Retain, /*! Time to wait (UInt16). */ - kiSCSIKernelSODefaultTime2Wait, + kiSCSIHBASODefaultTime2Wait, /*! Error recovery level (UInt8). */ - kiSCSIKernelSOErrorRecoveryLevel, + kiSCSIHBASOErrorRecoveryLevel, /*! Max connections supported by target (UInt32). */ - kiSCSIKernelSOMaxConnections, + kiSCSIHBASOMaxConnections, /*! Send data immediately (bool). */ - kiSCSIKernelSOImmediateData, + kiSCSIHBASOImmediateData, /*! Expect an initial R2T from target (bool). */ - kiSCSIKernelSOInitialR2T, + kiSCSIHBASOInitialR2T, /*! Data PDUs in order (bool). */ - kiSCSIKernelSODataPDUInOrder, + kiSCSIHBASODataPDUInOrder, /*! Data sequence in order (bool). */ - kiSCSIKernelSODataSequenceInOrder, + kiSCSIHBASODataSequenceInOrder, /*! Number of outstanding R2Ts allowed (UInt16). */ - kiSCSIKernelSOMaxOutstandingR2T, + kiSCSIHBASOMaxOutstandingR2T, /*! Maximum data burst length in bytes (UInt32). */ - kiSCSIKernelSOMaxBurstLength, + kiSCSIHBASOMaxBurstLength, /*! First data burst length in bytes (UInt32). */ - kiSCSIKernelSOFirstBurstLength, + kiSCSIHBASOFirstBurstLength, /*! Target session identifying handle (TSIH). */ - kiSCSIKernelSOTargetSessionId, + kiSCSIHBASOTargetSessionId, /*! Target portal group tag (TPGT). */ - kiSCSIKernelSOTargetPortalGroupTag, + kiSCSIHBASOTargetPortalGroupTag, }; /*! An enumeration of configurable connection parameters. */ -enum iSCSIKernelConnectionOptTypes { +enum iSCSIHBAConnectionParameters { /*! Flag that indicates if this connection uses header digests (bool). */ - kiSCSIKernelCOUseHeaderDigest, + kiSCSIHBACOUseHeaderDigest, /*! Flag that indicates if this connection uses data digests (bool). */ - kiSCSIKernelCOUseDataDigest, + kiSCSIHBACOUseDataDigest, /*! Flag that indicates if this connection uses IF markers (bool). */ - kiSCSIKernelCOUseIFMarker, + kiSCSIHBACOUseIFMarker, /*! Flag that indicates if this connection uses OF markers (bool). */ - kiSCSIKernelCOUseOFMarker, + kiSCSIHBACOUseOFMarker, /*! Interval for OF marker (UInt16). */ - kiSCSIKernelCOOFMarkInt, + kiSCSIHBACOOFMarkInt, /*! Interval for IF marker (UInt16). */ - kiSCSIKernelCOIFMarkInt, + kiSCSIHBACOIFMarkInt, /*! Maximum data segment length allowed by the target (UInt32). */ - kiSCSIKernelCOMaxSendDataSegmentLength, + kiSCSIHBACOMaxSendDataSegmentLength, /*! Maximum data segment length initiator can receive (UInt32). */ - kiSCSIKernelCOMaxRecvDataSegmentLength, + kiSCSIHBACOMaxRecvDataSegmentLength, /*! Initial expStatSN. */ - kiSCSIKernelCOInitialExpStatSN + kiSCSIHBACOInitialExpStatSN }; diff --git a/Source/User/iSCSI Framework/iSCSIUtils.c b/Source/User/iSCSI Framework/iSCSIUtils.c index 5e2bc868..d548e6de 100644 --- a/Source/User/iSCSI Framework/iSCSIUtils.c +++ b/Source/User/iSCSI Framework/iSCSIUtils.c @@ -50,7 +50,11 @@ Boolean iSCSIUtilsValidateIQN(CFStringRef IQN) regex_t preg; regcomp(&preg,pattern,REG_EXTENDED | REG_NOSUB); - if(regexec(&preg,CFStringGetCStringPtr(IQN,kCFStringEncodingASCII),0,NULL,0) == 0) + CFIndex IQNLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(IQN),kCFStringEncodingASCII) + sizeof('\0'); + char IQNBuffer[IQNLength]; + CFStringGetCString(IQN,IQNBuffer,IQNLength,kCFStringEncodingASCII); + + if(regexec(&preg,IQNBuffer,0,NULL,0) == 0) validName = true; regfree(&preg); @@ -101,9 +105,13 @@ CFArrayRef iSCSIUtilsCreateArrayByParsingPortalParts(CFStringRef portal) regmatch_t matches[maxMatches[index]]; memset(matches,0,sizeof(regmatch_t)*maxMatches[index]); - + + CFIndex portalLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(portal),kCFStringEncodingASCII) + sizeof('\0'); + char portalBuffer[portalLength]; + CFStringGetCString(portal,portalBuffer,portalLength,kCFStringEncodingASCII); + // Match against pattern[index] - if(regexec(&preg,CFStringGetCStringPtr(portal,kCFStringEncodingASCII),maxMatches[index],matches,0)) + if(regexec(&preg,portalBuffer,maxMatches[index],matches,0)) { regfree(&preg); index++; @@ -263,4 +271,106 @@ CFStringRef iSCSIUtilsGetStringForLogoutStatus(enum iSCSILogoutStatusCode status return CFSTR(""); }; return CFSTR(""); +} + +/*! Creates address structures for an iSCSI target and the host (initiator) + * given an iSCSI portal reference. This function may be helpful when + * interfacing to low-level C networking APIs or other foundation libraries. + * @param portal an iSCSI portal. + * @param the target address structure (returned by this function). + * @param the host address structure (returned by this function). */ +errno_t iSCSIUtilsGetAddressForPortal(iSCSIPortalRef portal, + struct sockaddr_storage * remoteAddress, + struct sockaddr_storage * localAddress) +{ + if (!portal || !remoteAddress || !localAddress) + return EINVAL; + + errno_t error = 0; + + // Resolve the target node first and get a sockaddr info for it + const char * targetAddr, * targetPort; + + targetAddr = CFStringGetCStringPtr(iSCSIPortalGetAddress(portal),kCFStringEncodingUTF8); + targetPort = CFStringGetCStringPtr(iSCSIPortalGetPort(portal),kCFStringEncodingUTF8); + + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, + .ai_protocol = IPPROTO_TCP, + }; + + struct addrinfo * aiTarget = NULL; + if((error = getaddrinfo(targetAddr,targetPort,&hints,&aiTarget))) + return error; + + // Copy the sock_addr structure into a sockaddr_storage structure (this + // may be either an IPv4 or IPv6 sockaddr structure) + memcpy(remoteAddress,aiTarget->ai_addr,aiTarget->ai_addrlen); + + freeaddrinfo(aiTarget); + + // If the default interface is to be used, prepare a structure for it + CFStringRef hostIface = iSCSIPortalGetHostInterface(portal); + + if(CFStringCompare(hostIface,kiSCSIDefaultHostInterface,0) == kCFCompareEqualTo) + { + localAddress->ss_family = remoteAddress->ss_family; + + // For completeness, setup the sockaddr_in structure + if(localAddress->ss_family == AF_INET) + { + struct sockaddr_in * sa = (struct sockaddr_in *)localAddress; + sa->sin_port = 0; + sa->sin_addr.s_addr = htonl(INADDR_ANY); + sa->sin_len = sizeof(struct sockaddr_in); + } + + // TODO: test IPv6 functionality + else if(localAddress->ss_family == AF_INET6) + { + struct sockaddr_in6 * sa = (struct sockaddr_in6 *)localAddress; + sa->sin6_addr = in6addr_any; + } + + return error; + } + + // Otherwise we have to search the list of all interfaces for the specified + // interface and copy the corresponding address structure + struct ifaddrs * interfaceList; + + if((error = getifaddrs(&interfaceList))) + return error; + + error = EAFNOSUPPORT; + struct ifaddrs * interface = interfaceList; + + while(interface) + { + // Check if interface supports the targets address family (e.g., IPv4) + if(interface->ifa_addr->sa_family == remoteAddress->ss_family) + { + CFStringRef currIface = CFStringCreateWithCStringNoCopy( + kCFAllocatorDefault, + interface->ifa_name, + kCFStringEncodingUTF8, + kCFAllocatorNull); + + Boolean ifaceNameMatch = + CFStringCompare(currIface,hostIface,kCFCompareCaseInsensitive) == kCFCompareEqualTo; + CFRelease(currIface); + // Check if interface names match... + if(ifaceNameMatch) + { + memcpy(localAddress,interface->ifa_addr,interface->ifa_addr->sa_len); + error = 0; + break; + } + } + interface = interface->ifa_next; + } + + freeifaddrs(interfaceList); + return error; } \ No newline at end of file diff --git a/Source/User/iSCSI Framework/iSCSIUtils.h b/Source/User/iSCSI Framework/iSCSIUtils.h index 013a0c96..c66adcce 100644 --- a/Source/User/iSCSI Framework/iSCSIUtils.h +++ b/Source/User/iSCSI Framework/iSCSIUtils.h @@ -30,9 +30,12 @@ #define __ISCSI_UTILS_H__ #include -#include #include +#include +#include +#include + #include "iSCSITypes.h" /*! Verifies whether specified iSCSI qualified name (IQN) is valid per RFC3720. @@ -73,4 +76,14 @@ CFStringRef iSCSIUtilsGetStringForLoginStatus(enum iSCSILoginStatusCode statusCo * @return a string describing the login status (guaranteed to be a valid string). */ CFStringRef iSCSIUtilsGetStringForLogoutStatus(enum iSCSILogoutStatusCode statusCode); +/*! Creates address structures for an iSCSI target and the host (initiator) + * given an iSCSI portal reference. This function may be helpful when + * interfacing to low-level C networking APIs or other foundation libraries. + * @param portal an iSCSI portal. + * @param the target address structure (returned by this function). + * @param the host address structure (returned by this function). */ +errno_t iSCSIUtilsGetAddressForPortal(iSCSIPortalRef portal, + struct sockaddr_storage * remoteAddress, + struct sockaddr_storage * localAddress); + #endif /* defined(__ISCSI_UTILS_H__) */ diff --git a/Source/User/iscsictl/iSCSICtl.m b/Source/User/iscsictl/iSCSICtl.m index 23734940..58428e2d 100644 --- a/Source/User/iscsictl/iSCSICtl.m +++ b/Source/User/iscsictl/iSCSICtl.m @@ -113,12 +113,21 @@ /*! Auto login command-line option. */ CFStringRef kOptKeyAutoLogin = CFSTR("auto-login"); +/*! Persistent command-line option. */ +CFStringRef kOptKeyPersistent = CFSTR("persistent"); + /*! Auto-login enable/disable command-line value. */ CFStringRef kOptValueAutoLoginEnable = CFSTR("enable"); /*! Auto login enable/disable command-line value. */ CFStringRef kOptValueAutoLoginDisable = CFSTR("disable"); +/*! Persistent enable/disable command-line value. */ +CFStringRef kOptValuePersistentEnable = CFSTR("enable"); + +/*! Persistent enable/disable command-line value. */ +CFStringRef kOptValuePersistentDisable = CFSTR("disable"); + /*! Digest command-line options. */ CFStringRef kOptKeyDigest = CFSTR("digest"); @@ -314,14 +323,8 @@ void iSCSICtlDisplayString(CFStringRef string) CFIndex usedBufLen = 0; UInt8 buffer[maxBufLen]; - CFStringGetBytes(string, - CFRangeMake(0,CFStringGetLength(string)), - kCFStringEncodingASCII, - 0, - false, - buffer, - maxBufLen, - &usedBufLen); + CFStringGetBytes(string,CFRangeMake(0,CFStringGetLength(string)),kCFStringEncodingASCII, + 0,false,buffer,maxBufLen,&usedBufLen); CFWriteStreamWrite(stdoutStream,buffer,usedBufLen); } @@ -375,7 +378,7 @@ CFStringRef iSCSICtlCreateSecretFromInput(CFIndex retries) { CFStringRef secret = NULL; - const int MAX_SECRET_LENGTH = 128; //256 + const int MAX_SECRET_LENGTH = 128; char buffer[MAX_SECRET_LENGTH]; char verify[MAX_SECRET_LENGTH]; @@ -1001,11 +1004,25 @@ errno_t iSCSICtlModifyInitiator(AuthorizationRef authorization,CFDictionaryRef o bool lockAndSync = false; iSCSIPreferencesRef preferences = NULL; errno_t error = 0; + bool validOption = false; // Was there at least one valid option? - // Check for CHAP shared secret + // Get CHAP shared secret if present, otherwise get from input CFStringRef secret = NULL; - if(CFDictionaryContainsKey(options,kOptKeyCHAPSecret)) - secret = iSCSICtlCreateSecretFromInput(MAX_SECRET_RETRY_ATTEMPTS); + if (CFDictionaryGetValueIfPresent(options, kOptKeyCHAPSecret, (const void **)&secret)) + { + if (CFStringCompare(secret, kOptValueEmpty, 0) == kCFCompareEqualTo) + { + if (CFDictionaryContainsKey(options,kOptKeyCHAPSecret)) + { + secret = iSCSICtlCreateSecretFromInput(MAX_SECRET_RETRY_ATTEMPTS); + validOption = true; + } + } + else + { + validOption = true; + } + } error = iSCSICtlConnectToDaemon(&handle); @@ -1024,6 +1041,8 @@ errno_t iSCSICtlModifyInitiator(AuthorizationRef authorization,CFDictionaryRef o } else error = EINVAL; + + validOption = true; } if(!error) { @@ -1040,6 +1059,12 @@ errno_t iSCSICtlModifyInitiator(AuthorizationRef authorization,CFDictionaryRef o { if(CFStringCompare(value,kOptValueEmpty,0) != kCFCompareEqualTo) iSCSIPreferencesSetInitiatorCHAPName(preferences,value); + else { + iSCSICtlDisplayError(CFSTR("A CHAP name was not specified")); + error = EINVAL; + } + + validOption = true; } // Check for authentication method @@ -1049,31 +1074,55 @@ errno_t iSCSICtlModifyInitiator(AuthorizationRef authorization,CFDictionaryRef o iSCSIPreferencesSetInitiatorAuthenticationMethod(preferences,kiSCSIAuthMethodNone); else if(CFStringCompare(value,kOptValueAuthMethodCHAP,kCFCompareCaseInsensitive) == kCFCompareEqualTo) iSCSIPreferencesSetInitiatorAuthenticationMethod(preferences,kiSCSIAuthMethodCHAP); + else if(CFStringCompare(value,kOptValueEmpty,0) == kCFCompareEqualTo) { + iSCSICtlDisplayError(CFSTR("Authentication method not specified")); + error = EINVAL; + } else { iSCSICtlDisplayError(CFSTR("The specified authentication method is invalid")); error = EINVAL; } + + validOption = true; } // Check for initiator alias if(!error && CFDictionaryGetValueIfPresent(options,kOptKeyNodeAlias,(const void**)&value)) - iSCSIPreferencesSetInitiatorAlias(preferences,value); + { + if(CFStringCompare(value,kOptValueEmpty,0) == kCFCompareEqualTo) { + iSCSICtlDisplayError(CFSTR("An alias was not specified")); + error = EINVAL; + } + else + iSCSIPreferencesSetInitiatorAlias(preferences,value); + + validOption = true; + } // Check for initiator IQN if(!error && CFDictionaryGetValueIfPresent(options,kOptKeyNodeName,(const void **)&value)) { - // Validate the chosen initiator IQN - if(iSCSIUtilsValidateIQN(value)) + if(CFStringCompare(value,kOptValueEmpty,0) == kCFCompareEqualTo) { + iSCSICtlDisplayError(CFSTR("An iSCSI Qualified Name (IQN) was not specified")); + error = EINVAL; + } + else if(iSCSIUtilsValidateIQN(value)) iSCSIPreferencesSetInitiatorIQN(preferences,value); else { iSCSICtlDisplayError(CFSTR("The specified name is not a valid IQN or EUI-64 identifier")); error = EINVAL; } + + validOption = true; } if(!error) { iSCSIDaemonPreferencesIOUnlockAndSync(handle,preferences); - iSCSICtlDisplayString(CFSTR("Initiator settings have been updated\n")); + + if(validOption) + iSCSICtlDisplayString(CFSTR("Initiator settings have been updated\n")); + else + iSCSICtlDisplayError(CFSTR("No valid options have been specified.")); } else if(lockAndSync) iSCSIDaemonPreferencesIOUnlockAndSync(handle,NULL); @@ -1089,7 +1138,8 @@ errno_t iSCSICtlModifyTargetFromOptions(AuthorizationRef authorization, iSCSIPreferencesRef preferences, CFDictionaryRef options, iSCSITargetRef target, - iSCSIPortalRef portal) + iSCSIPortalRef portal, + bool validOption) { CFStringRef targetIQN = iSCSITargetGetIQN(target); CFStringRef value = NULL; @@ -1100,38 +1150,54 @@ errno_t iSCSICtlModifyTargetFromOptions(AuthorizationRef authorization, { if(CFStringCompare(value,kOptValueEmpty,0) != kCFCompareEqualTo) iSCSIPreferencesSetTargetCHAPName(preferences,targetIQN,value); + else { + iSCSICtlDisplayError(CFSTR("A CHAP name was not specified")); + error = EINVAL; + } + + validOption = true; } - + // Check for authentication method - enum iSCSIAuthMethods authMethod = kiSCSIAuthMethodInvalid; - if(!error && CFDictionaryGetValueIfPresent(options,kOptKeyAutMethod,(const void**)&value)) { - if(CFStringCompare(value,kOptValueAuthMethodNone,kCFCompareCaseInsensitive) == kCFCompareEqualTo) { - authMethod = kiSCSIAuthMethodNone; - iSCSIPreferencesSetTargetAuthenticationMethod(preferences,targetIQN,authMethod); - } + if(CFStringCompare(value,kOptValueAuthMethodNone,kCFCompareCaseInsensitive) == kCFCompareEqualTo) + iSCSIPreferencesSetTargetAuthenticationMethod(preferences,targetIQN,kiSCSIAuthMethodNone); else if(CFStringCompare(value,kOptValueAuthMethodCHAP,kCFCompareCaseInsensitive) == kCFCompareEqualTo) - { - authMethod = kiSCSIAuthMethodCHAP; - iSCSIPreferencesSetTargetAuthenticationMethod(preferences,targetIQN,authMethod); + iSCSIPreferencesSetTargetAuthenticationMethod(preferences,targetIQN,kiSCSIAuthMethodCHAP); + else if(CFStringCompare(value,kOptValueEmpty,0) == kCFCompareEqualTo) { + iSCSICtlDisplayError(CFSTR("Authentication method not specified")); + error = EINVAL; } else { iSCSICtlDisplayError(CFSTR("The specified authentication method is invalid")); error = EINVAL; } + + validOption = true; } - // Check for target IQN + // Check for target IQN changes (preferences will not allow a change to the IQN for + // static targets, we must therefore catch and display an appropriate error message) if(!error && CFDictionaryGetValueIfPresent(options,kOptKeyNodeName,(const void **)&value)) { - // Validate the chosen target IQN - if(iSCSIUtilsValidateIQN(value)) + if(iSCSIPreferencesGetTargetConfigType(preferences,targetIQN) != kiSCSITargetConfigStatic) + { + iSCSICtlDisplayError(CFSTR("The specified target is dynamically managed using discovery and cannot be renamed")); + error = EINVAL; + } + else if(CFStringCompare(value,kOptValueEmpty,0) == kCFCompareEqualTo) { + iSCSICtlDisplayError(CFSTR("An iSCSI Qualified Name (IQN) was not specified")); + error = EINVAL; + } + else if(iSCSIUtilsValidateIQN(value)) iSCSIPreferencesSetTargetIQN(preferences,targetIQN,value); else { iSCSICtlDisplayError(CFSTR("The specified name is not a valid IQN or EUI-64 identifier")); error = EINVAL; } + + validOption = true; } // Check for auto-login @@ -1149,6 +1215,27 @@ errno_t iSCSICtlModifyTargetFromOptions(AuthorizationRef authorization, CFRelease(errorString); error = EINVAL; } + + validOption = true; + } + + // Check for persistent + if(!error && CFDictionaryGetValueIfPresent(options,kOptKeyPersistent,(const void **)&value)) + { + + if(CFStringCompare(value,kOptValuePersistentEnable,kCFCompareCaseInsensitive) == kCFCompareEqualTo) + iSCSIPreferencesSetPersistenceForTarget(preferences,targetIQN,true); + else if(CFStringCompare(value,kOptValuePersistentDisable,kCFCompareCaseInsensitive) == kCFCompareEqualTo) + iSCSIPreferencesSetPersistenceForTarget(preferences,targetIQN,false); + else { + CFStringRef errorString = CFStringCreateWithFormat( + kCFAllocatorDefault,0,CFSTR("Invalid argument for %@"),kOptKeyPersistent); + iSCSICtlDisplayError(errorString); + CFRelease(errorString); + error = EINVAL; + } + + validOption = true; } // Check for maximum connections @@ -1161,10 +1248,12 @@ errno_t iSCSICtlModifyTargetFromOptions(AuthorizationRef authorization, } else if(maxConnections > kRFC3720_MaxConnections_Max) { iSCSICtlDisplayError(CFSTR("Specified maximum number of connections is not sufficient")); - error = EINVAL; + error = EINVAL; } else iSCSIPreferencesSetMaxConnectionsForTarget(preferences,targetIQN,maxConnections); + + validOption = true; } // Check for error recovery level @@ -1179,6 +1268,8 @@ errno_t iSCSICtlModifyTargetFromOptions(AuthorizationRef authorization, else { iSCSIPreferencesSetErrorRecoveryLevelForTarget(preferences,targetIQN,level); } + + validOption = true; } // Check for header digest @@ -1188,10 +1279,16 @@ errno_t iSCSICtlModifyTargetFromOptions(AuthorizationRef authorization, iSCSIPreferencesSetHeaderDigestForTarget(preferences,targetIQN,kiSCSIDigestNone); else if(CFStringCompare(value,kOptValueDigestCRC32C,kCFCompareCaseInsensitive) == kCFCompareEqualTo) iSCSIPreferencesSetHeaderDigestForTarget(preferences,targetIQN,kiSCSIDigestCRC32C); + if(CFStringCompare(value,kOptValueEmpty,0) == kCFCompareEqualTo) { + iSCSICtlDisplayError(CFSTR("A digest type was not specified")); + error = EINVAL; + } else { iSCSICtlDisplayError(CFSTR("The specified digest type is invalid")); error = EINVAL; } + + validOption = true; } // Check for data digest @@ -1201,12 +1298,23 @@ errno_t iSCSICtlModifyTargetFromOptions(AuthorizationRef authorization, iSCSIPreferencesSetDataDigestForTarget(preferences,targetIQN,kiSCSIDigestNone); else if(CFStringCompare(value,kOptValueDigestCRC32C,kCFCompareCaseInsensitive) == kCFCompareEqualTo) iSCSIPreferencesSetDataDigestForTarget(preferences,targetIQN,kiSCSIDigestCRC32C); + if(CFStringCompare(value,kOptValueEmpty,0) == kCFCompareEqualTo) { + iSCSICtlDisplayError(CFSTR("A digest type was not specified")); + error = EINVAL; + } else { iSCSICtlDisplayError(CFSTR("The specified digest type is invalid")); error = EINVAL; } + + validOption = true; } - + + if(!error && !validOption) { + iSCSICtlDisplayError(CFSTR("No valid options have been specified.")); + error = EINVAL; + } + return error; } @@ -1227,6 +1335,7 @@ errno_t iSCSICtlModifyTarget(AuthorizationRef authorization,CFDictionaryRef opti iSCSIPreferencesRef preferences = NULL; CFStringRef targetIQN = NULL; errno_t error = 0; + bool validOption = false; // Was there at least one valid option? if(!error && !(target = iSCSICtlCreateTargetFromOptions(options))) error = EINVAL; @@ -1237,10 +1346,23 @@ errno_t iSCSICtlModifyTarget(AuthorizationRef authorization,CFDictionaryRef opti if(!(portal = iSCSICtlCreatePortalFromOptions(options))) error = EINVAL; - // Check for CHAP shared secret + // Get CHAP shared secret if present, otherwise get from input CFStringRef secret = NULL; - if(CFDictionaryContainsKey(options,kOptKeyCHAPSecret)) - secret = iSCSICtlCreateSecretFromInput(MAX_SECRET_RETRY_ATTEMPTS); + if (CFDictionaryGetValueIfPresent(options, kOptKeyCHAPSecret, (const void **)&secret)) + { + if (CFStringCompare(secret, kOptValueEmpty, 0) == kCFCompareEqualTo) + { + if (CFDictionaryContainsKey(options,kOptKeyCHAPSecret)) + { + secret = iSCSICtlCreateSecretFromInput(MAX_SECRET_RETRY_ATTEMPTS); + validOption = true; + } + } + else + { + validOption = true; + } + } if(!error) error = iSCSICtlConnectToDaemon(&handle); @@ -1248,16 +1370,18 @@ errno_t iSCSICtlModifyTarget(AuthorizationRef authorization,CFDictionaryRef opti if(!error) preferences = iSCSIPreferencesCreateFromAppValues(); - if(!error && CFDictionaryContainsKey(options,kOptKeyCHAPSecret)) { - if(secret != NULL) { + if(CFDictionaryContainsKey(options,kOptKeyCHAPSecret)) { + if(!error && secret != NULL) { if(iSCSIDaemonSetSharedSecret(handle,authorization,targetIQN,secret)) { iSCSICtlDisplayError(kPermissionsErrorString); error = EAUTH; } - CFRelease(secret); } else error = EINVAL; + + if(secret) + CFRelease(secret); } if(!error) { @@ -1299,8 +1423,10 @@ errno_t iSCSICtlModifyTarget(AuthorizationRef authorization,CFDictionaryRef opti if(iSCSIDaemonIsTargetActive(handle,target)) iSCSICtlDisplayString(CFSTR("The specified target has an active session and cannot be modified\n")); else { - iSCSICtlModifyTargetFromOptions(authorization,preferences,options,target,portal); - iSCSICtlDisplayString(CFSTR("Target settings have been updated\n")); + error = iSCSICtlModifyTargetFromOptions(authorization,preferences,options,target,portal,validOption); + + if(!error) + iSCSICtlDisplayString(CFSTR("Target settings have been updated\n")); } } } @@ -1323,8 +1449,7 @@ errno_t iSCSICtlModifyTarget(AuthorizationRef authorization,CFDictionaryRef opti } /*! Helper function. Displays information about a target/session. */ -void displayTargetInfo(iSCSITargetRef target, - CFDictionaryRef properties) +void displayTargetInfo(iSCSITargetRef target,CFDictionaryRef properties) { CFStringRef targetState = NULL; CFStringRef targetConfig = NULL; @@ -1360,7 +1485,7 @@ void displayTargetInfo(iSCSITargetRef target, CFNumberRef targetSessionId = CFDictionaryGetValue(properties,kRFC3720_Key_TargetSessionId); CFNumberRef sessionId = CFDictionaryGetValue(properties,kRFC3720_Key_SessionId); - TSIH tsih = 0; + TargetSessionIdentifier tsih = 0; CFNumberGetValue(targetSessionId,kCFNumberSInt16Type,&tsih); status = CFStringCreateWithFormat(kCFAllocatorDefault,0, @@ -1521,15 +1646,34 @@ errno_t iSCSICtlListTarget(CFDictionaryRef options) displayTargetInfo(target,properties); + // Retrieve last known target alias (if available) and display it + CFStringRef targetAlias = iSCSIPreferencesGetTargetAlias(preferences,targetIQN); + if(!targetAlias) + targetAlias = CFSTR(""); + + CFStringRef aliasString = CFStringCreateWithFormat(kCFAllocatorDefault,0,CFSTR("\tnode-alias: %@\n"),targetAlias); + iSCSICtlDisplayString(aliasString); + CFRelease(aliasString); + // Get information about automatic login CFStringRef autoLogin = CFSTR("disabled"); if(iSCSIPreferencesGetAutoLoginForTarget(preferences,targetIQN)) autoLogin = CFSTR("enabled"); - CFStringRef targetConfig = CFStringCreateWithFormat(kCFAllocatorDefault,0,CFSTR("\t%@: %@\n"),kOptKeyAutoLogin,autoLogin); - iSCSICtlDisplayString(targetConfig); - CFRelease(targetConfig); + CFStringRef autoLoginString = CFStringCreateWithFormat(kCFAllocatorDefault,0,CFSTR("\t%@: %@\n"),kOptKeyAutoLogin,autoLogin); + iSCSICtlDisplayString(autoLoginString); + CFRelease(autoLoginString); + + // Get information about persistence + CFStringRef persistent = CFSTR("no"); + + if(iSCSIPreferencesGetPersistenceForTarget(preferences,targetIQN)) + persistent = CFSTR("yes"); + + CFStringRef persistentString = CFStringCreateWithFormat(kCFAllocatorDefault,0,CFSTR("\t%@: %@\n"),kOptKeyPersistent,persistent); + iSCSICtlDisplayString(persistentString); + CFRelease(persistentString); if(iSCSIPreferencesGetTargetConfigType(preferences,targetIQN) == kiSCSITargetConfigDynamicSendTargets) { @@ -1843,6 +1987,7 @@ errno_t iSCSICtlModifyDiscovery(AuthorizationRef authorization,CFDictionaryRef o iSCSIPreferencesRef preferences = NULL; bool lockAndSync = false; errno_t error = 0; + bool validOption = false; // Was there at least one valid option? if(!error) error = iSCSICtlConnectToDaemon(&handle); @@ -1873,6 +2018,8 @@ errno_t iSCSICtlModifyDiscovery(AuthorizationRef authorization,CFDictionaryRef o CFRelease(errorString); error = EINVAL; } + + validOption = true; } // Check if user modified the discovery interval if(!error && CFDictionaryGetValueIfPresent(optDictionary,kOptKeyDiscoveryInterval,(const void **)&value)) @@ -1888,11 +2035,17 @@ errno_t iSCSICtlModifyDiscovery(AuthorizationRef authorization,CFDictionaryRef o } else iSCSIPreferencesSetSendTargetsDiscoveryInterval(preferences,interval); + + validOption = true; } if(!error) { iSCSIDaemonPreferencesIOUnlockAndSync(handle,preferences); - iSCSICtlDisplayString(CFSTR("Discovery settings have been updated\n")); + + if(validOption) + iSCSICtlDisplayString(CFSTR("Discovery settings have been updated\n")); + else + iSCSICtlDisplayError(CFSTR("No valid options have been specified.")); } else if(lockAndSync) iSCSIDaemonPreferencesIOUnlockAndSync(handle,NULL); @@ -2277,4 +2430,4 @@ int main(int argc, char * argv[]) AuthorizationFree(authorization,kAuthorizationFlagDefaults); } return error; -} \ No newline at end of file +} diff --git a/Source/User/iscsictl/iscsictl.8 b/Source/User/iscsictl/iscsictl.8 index 86661466..08ec28bc 100644 --- a/Source/User/iscsictl/iscsictl.8 +++ b/Source/User/iscsictl/iscsictl.8 @@ -1,6 +1,6 @@ .\" (c) 2013-2015 Nareg Sinenian. All rights reserved. .\" This file is the UNIX man page for the iscsictl command-line utility." -.Dd November 1, 2015 +.Dd July 23, 2016 .Dt ISCSICTL 8 .Os "Mac OS X" .Sh NAME @@ -113,6 +113,10 @@ The following options can be used to modify target-config: Specifies whether the iSCSI daemon should login to the target upon startup. Possible values for .Ar enable are enable or disable. +.It Fl persistent Ar enable +Specifies whether the iSCSI daemon should reconnect to the target in the event of a network interruption or connection timeout. Possible values for +.Ar enable +are enable or disable. .It Fl MaxConnections Ar max_connections The maximum number of simultaneous connections allowed for this target. .It Fl ErrorRecoveryLevel Ar error_level diff --git a/Source/User/iscsid/iSCSIAuth.c b/Source/User/iscsid/iSCSIAuth.c index ff06de77..cd492acc 100644 --- a/Source/User/iscsid/iSCSIAuth.c +++ b/Source/User/iscsid/iSCSIAuth.c @@ -29,7 +29,7 @@ #include "iSCSIAuth.h" #include "iSCSIPDUUser.h" #include "iSCSISession.h" -#include "iSCSIKernelInterface.h" +#include "iSCSIHBAInterface.h" #include "iSCSIQueryTarget.h" @@ -42,29 +42,30 @@ extern CFStringRef kiSCSIInitiatorAlias; /*! Helper function. Create a byte array (CFDataRef object) that holds the * value represented by the hexidecimal string. Handles strings with or * without a 0x prefix. */ -CFDataRef CFDataCreateWithHexString(CFStringRef hexStr) +CFDataRef CFDataCreateWithHexString(CFStringRef hexString) { - if(!hexStr) + if(!hexString) return NULL; // Get length and pointer to hex string - CFIndex hexStrLen = CFStringGetLength(hexStr); - const char * hexStrPtr = CFStringGetCStringPtr(hexStr,kCFStringEncodingASCII); + CFIndex hexStringLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(hexString),kCFStringEncodingASCII) + sizeof('\0'); + char hexStringBuffer[hexStringLength]; + CFStringGetCString(hexString,hexStringBuffer,hexStringLength,kCFStringEncodingASCII); // Byte length stars off as the number of hex characters, and is adjusted // to reflect the number of bytes depending on the format of the hex string - // (e.g., if the hex string - CFIndex byteLength = hexStrLen; + // (does not include null terminator) + CFIndex byteLength = CFStringGetLength(hexString); // The index we'll start processing the hex string unsigned int startIndex = 0; - // Check for the "0x" prefix, ignore it if present... - if(hexStrLen >= 2 && hexStrPtr[0] == '0' && hexStrPtr[1] == 'x') { + // Check for the "0x" or "x" prefix, ignore it if present... + if(hexStringLength >= 2 && hexStringBuffer[0] == '0' && hexStringBuffer[1] == 'x') { startIndex+=2; byteLength-=2; } - else if(hexStrLen >= 1 && hexStrPtr[0] == 'x') { + else if(hexStringLength >= 1 && hexStringBuffer[0] == 'x') { startIndex++; byteLength--; } @@ -82,21 +83,21 @@ CFDataRef CFDataCreateWithHexString(CFStringRef hexStr) int buffer = 0; // If an odd number of hex characters, process first one differently... - if(hexStrLen % 2 != 0) { + if(hexStringLength % 2 != 0) { // Pick off the first character and convert differently - sscanf(&hexStrPtr[startIndex],"%01x",&buffer); + sscanf(&hexStringBuffer[startIndex],"%01x",&buffer); bytes[byteIdx] = buffer; startIndex++; byteIdx++; } // Process remaining characters in pairs (2 hex characters = 1 byte) - for(unsigned int idx = startIndex; idx < hexStrLen; idx+=2) { - sscanf(&hexStrPtr[idx],"%02x",&buffer); + for(unsigned int idx = startIndex; idx < hexStringLength; idx+=2) { + sscanf(&hexStringBuffer[idx],"%02x",&buffer); bytes[byteIdx] = buffer; byteIdx++; } - + return data; } @@ -107,9 +108,9 @@ CFDataRef CFDataCreateWithHexString(CFStringRef hexStr) CFStringRef CreateHexStringWithBytes(const UInt8 * bytes, size_t length) { // Pad string by 3 bytes to leave room for "0x" prefix and null terminator - const long hexStrLen = length * 2 + 3; + const long hexStrLength = length * 2 + 3; - char hexStr[hexStrLen]; + char hexStr[hexStrLength]; hexStr[0] = '0'; hexStr[1] = 'x'; @@ -118,7 +119,7 @@ CFStringRef CreateHexStringWithBytes(const UInt8 * bytes, size_t length) sprintf(&hexStr[i*2+2], "%02x", bytes[i]); // Null terminate - hexStr[hexStrLen-1] = 0; + hexStr[hexStrLength-1] = 0; // This copies our buffer into a CFString object return CFStringCreateWithCString(kCFAllocatorDefault,hexStr,kCFStringEncodingASCII); @@ -143,8 +144,11 @@ CFStringRef iSCSIAuthNegotiateCHAPCreateResponse(CFStringRef identifier, CC_MD5_Update(&md5,&id,(CC_LONG)sizeof(id)); // Hash in the secret - const UInt8 * byteSecret = (const UInt8*)CFStringGetCStringPtr(secret,kCFStringEncodingASCII); - CC_MD5_Update(&md5,byteSecret,(CC_LONG)CFStringGetLength(secret)); + CFIndex secretLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(secret),kCFStringEncodingASCII) + sizeof('\0'); + char secretBuffer[secretLength]; + CFStringGetCString(secret,secretBuffer,secretLength,kCFStringEncodingASCII); + + CC_MD5_Update(&md5,secretBuffer,(CC_LONG)CFStringGetLength(secret)); // Hash in the challenge CFDataRef challengeData = CFDataCreateWithHexString(challenge); @@ -155,7 +159,7 @@ CFStringRef iSCSIAuthNegotiateCHAPCreateResponse(CFStringRef identifier, CC_MD5_Final(md5Hash,&md5); CFRelease(challengeData); - + return CreateHexStringWithBytes(md5Hash,CC_MD5_DIGEST_LENGTH); } @@ -188,18 +192,15 @@ CFStringRef iSCSIAuthNegotiateCHAPCreateId() /*! Helper function for iSCSIConnectionSecurityNegotiate. Once it has been * determined that a CHAP session is to be used, this function will perform * the CHAP authentication. */ -errno_t iSCSIAuthNegotiateCHAP(iSCSIMutableTargetRef target, +errno_t iSCSIAuthNegotiateCHAP(iSCSISessionManagerRef managerRef, + iSCSIMutableTargetRef target, iSCSIAuthRef initiatorAuth, iSCSIAuthRef targetAuth, - SID sessionId, - CID connectionId, - TSIH targetSessionId, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId, + TargetSessionIdentifier targetSessionId, enum iSCSILoginStatusCode * statusCode) { - if(!target || !initiatorAuth || !targetAuth || - sessionId == kiSCSIInvalidConnectionId || connectionId == kiSCSIInvalidConnectionId) - return EINVAL; - // Setup dictionary CHAP authentication information CFMutableDictionaryRef authCmd = CFDictionaryCreateMutable( kCFAllocatorDefault,kiSCSISessionMaxTextKeyValuePairs, @@ -223,6 +224,7 @@ errno_t iSCSIAuthNegotiateCHAP(iSCSIMutableTargetRef target, CFDictionaryAddValue(authCmd,kRFC3720_Key_AuthCHAPDigest,kRFC3720_Value_AuthCHAPDigestMD5); struct iSCSILoginQueryContext context; + context.interface = iSCSISessionManagerGetHBAInterface(managerRef); context.sessionId = sessionId; context.connectionId = connectionId; context.targetSessionId = targetSessionId; @@ -387,17 +389,16 @@ void iSCSIAuthNegotiateBuildDict(iSCSITargetRef target, * begin authentication between the initiator and a selected target. If the * target name is set to blank (e.g., by a call to iSCSITargetSetIQN()) or * never set at all, a discovery session is assumed for authentication. */ -errno_t iSCSIAuthNegotiate(iSCSIMutableTargetRef target, +errno_t iSCSIAuthNegotiate(iSCSISessionManagerRef managerRef, + iSCSIMutableTargetRef target, iSCSIAuthRef initiatorAuth, iSCSIAuthRef targetAuth, - SID sessionId, - CID connectionId, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId, enum iSCSILoginStatusCode * statusCode) { - if(!target || !initiatorAuth || !targetAuth || - sessionId == kiSCSIInvalidConnectionId || connectionId == kiSCSIInvalidConnectionId) - return EINVAL; - + iSCSIHBAInterfaceRef hbaInterface = iSCSISessionManagerGetHBAInterface(managerRef); + // Setup dictionary with target and initiator info for authentication CFMutableDictionaryRef authCmd = CFDictionaryCreateMutable( kCFAllocatorDefault,kiSCSISessionMaxTextKeyValuePairs, @@ -411,14 +412,16 @@ errno_t iSCSIAuthNegotiate(iSCSIMutableTargetRef target, iSCSIAuthNegotiateBuildDict(target,initiatorAuth,targetAuth,authCmd); struct iSCSILoginQueryContext context; + context.interface = hbaInterface; context.sessionId = sessionId; context.connectionId = connectionId; context.currentStage = kiSCSIPDUSecurityNegotiation; context.nextStage = kiSCSIPDUSecurityNegotiation; // Retrieve the TSIH from the kernel - TSIH targetSessionId = 0; - iSCSIKernelGetSessionOpt(sessionId,kiSCSIKernelSOTargetSessionId,&targetSessionId,sizeof(TSIH)); + TargetSessionIdentifier targetSessionId = 0; + iSCSIHBAInterfaceGetSessionParameter(hbaInterface,sessionId,kiSCSIHBASOTargetSessionId, + &targetSessionId,sizeof(TargetSessionIdentifier)); context.targetSessionId = targetSessionId; enum iSCSIPDURejectCode rejectCode; @@ -440,11 +443,11 @@ errno_t iSCSIAuthNegotiate(iSCSIMutableTargetRef target, // This was the first query of the connection; record the status // sequence number provided by the target UInt32 expStatSN = context.statSN + 1; - iSCSIKernelSetConnectionOpt(sessionId,connectionId,kiSCSIKernelCOInitialExpStatSN, - &expStatSN,sizeof(expStatSN)); + iSCSIHBAInterfaceSetConnectionParameter(hbaInterface,sessionId,connectionId,kiSCSIHBACOInitialExpStatSN, + &expStatSN,sizeof(expStatSN)); - // If this is not a discovery session, we expect to receive a target - // portal group tag (TPGT) and validate it + // If this is not a discovery session (the target is not specified for discovery), + // we expect to receive a target portal group tag (TPGT) and validate it if(CFStringCompare(iSCSITargetGetIQN(target),kiSCSIUnspecifiedTargetIQN,0) != kCFCompareEqualTo) { // Ensure that the target returned a portal group tag (TPGT)... @@ -459,16 +462,16 @@ errno_t iSCSIAuthNegotiate(iSCSIMutableTargetRef target, // If this is leading login (TSIH = 0 for leading login), store TPGT, // else compare it to the TPGT that we have stored for this session... if(targetSessionId == 0) { - TPGT targetPortalGroupTag = CFStringGetIntValue(targetPortalGroupRsp); + TargetPortalGroupTag targetPortalGroupTag = CFStringGetIntValue(targetPortalGroupRsp); - iSCSIKernelSetSessionOpt(sessionId,kiSCSIKernelSOTargetPortalGroupTag, - &targetPortalGroupTag,sizeof(TPGT)); + iSCSIHBAInterfaceSetSessionParameter(hbaInterface,sessionId,kiSCSIHBASOTargetPortalGroupTag, + &targetPortalGroupTag,sizeof(TargetPortalGroupTag)); } else { // Retrieve from kernel - TPGT targetPortalGroupTag = 0; - iSCSIKernelGetSessionOpt(sessionId,kiSCSIKernelSOTargetPortalGroupTag, - &targetPortalGroupTag,sizeof(TPGT)); + TargetPortalGroupTag targetPortalGroupTag = 0; + iSCSIHBAInterfaceGetSessionParameter(hbaInterface,sessionId,kiSCSIHBASOTargetPortalGroupTag, + &targetPortalGroupTag,sizeof(TargetPortalGroupTag)); // Validate existing group against TPGT for this connection if(targetPortalGroupTag != CFStringGetIntValue(targetPortalGroupRsp)) @@ -476,12 +479,14 @@ errno_t iSCSIAuthNegotiate(iSCSIMutableTargetRef target, } } - // Determine if target supports desired authentication method + // Determine if target supports desired authentication method. Desired method could + // be a comma-separated list in authCmd, so we check the target's desired method + // as specified in the response (authRsp) against our list CFRange result = CFStringFind(CFDictionaryGetValue(authCmd,kRFC3720_Key_AuthMethod), CFDictionaryGetValue(authRsp,kRFC3720_Key_AuthMethod), kCFCompareCaseInsensitive); - // If we wanted to use a particular method and the target doesn't support it + // Check if target supported our desired authentication method if(result.location == kCFNotFound) { error = EAUTH; goto ERROR_AUTHENTICATION; @@ -513,7 +518,8 @@ errno_t iSCSIAuthNegotiate(iSCSIMutableTargetRef target, } if(authMethod == kiSCSIAuthMethodCHAP) { - error = iSCSIAuthNegotiateCHAP(target, + error = iSCSIAuthNegotiateCHAP(managerRef, + target, initiatorAuth, targetAuth, sessionId, @@ -552,9 +558,10 @@ errno_t iSCSIAuthNegotiate(iSCSIMutableTargetRef target, /*! Helper function. Called by session or connection creation functions to * determine available authentication options for a given target. */ -errno_t iSCSIAuthInterrogate(iSCSITargetRef target, - SID sessionId, - CID connectionId, +errno_t iSCSIAuthInterrogate(iSCSISessionManagerRef managerRef, + iSCSITargetRef target, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId, enum iSCSIAuthMethods * authMethod, enum iSCSILoginStatusCode * statusCode) { @@ -581,6 +588,7 @@ errno_t iSCSIAuthInterrogate(iSCSITargetRef target, &kCFTypeDictionaryKeyCallBacks,&kCFTypeDictionaryValueCallBacks); struct iSCSILoginQueryContext context; + context.interface = iSCSISessionManagerGetHBAInterface(managerRef); context.sessionId = sessionId; context.connectionId = connectionId; context.currentStage = kiSCSIPDUSecurityNegotiation; diff --git a/Source/User/iscsid/iSCSIAuth.h b/Source/User/iscsid/iSCSIAuth.h index 58257e4f..49c56dc6 100644 --- a/Source/User/iscsid/iSCSIAuth.h +++ b/Source/User/iscsid/iSCSIAuth.h @@ -32,23 +32,26 @@ #include #include +#include "iSCSISessionManager.h" #include "iSCSITypes.h" -#include "iSCSIKernelInterfaceShared.h" +#include "iSCSIHBATypes.h" /*! Authentication function defined in the authentication module * (in the file iSCSIAuth.h). */ -errno_t iSCSIAuthNegotiate(iSCSIMutableTargetRef target, +errno_t iSCSIAuthNegotiate(iSCSISessionManagerRef manager, + iSCSIMutableTargetRef target, iSCSIAuthRef initiatorAuth, iSCSIAuthRef targetAuth, - SID sessionId, - CID connectionId, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId, enum iSCSILoginStatusCode * statusCode); /*! Authentication function defined in the authentication module * (in the file iSCSIAuth.h). */ -errno_t iSCSIAuthInterrogate(iSCSITargetRef target, - SID sessionId, - CID connectionId, +errno_t iSCSIAuthInterrogate(iSCSISessionManagerRef manager, + iSCSITargetRef target, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId, enum iSCSIAuthMethods * authMethod, enum iSCSILoginStatusCode * statusCode); diff --git a/Source/User/iscsid/iSCSIDaemon.c b/Source/User/iscsid/iSCSIDaemon.c index 6e2351f6..678de72e 100644 --- a/Source/User/iscsid/iSCSIDaemon.c +++ b/Source/User/iscsid/iSCSIDaemon.c @@ -48,6 +48,7 @@ // Foundation includes #include #include +#include // Mach kernel includes #include @@ -60,7 +61,6 @@ // iSCSI includes #include "iSCSISession.h" -#include "iSCSIDiscovery.h" #include "iSCSIDaemonInterfaceShared.h" #include "iSCSIPreferences.h" #include "iSCSIDA.h" @@ -94,6 +94,8 @@ iSCSIPreferencesRef preferences = NULL; /*! Incoming request information struct. */ struct iSCSIDIncomingRequestInfo * reqInfo = NULL; +/*! Used to manage iSCSI sessions. */ +iSCSISessionManagerRef sessionManager = NULL; struct iSCSIDIncomingRequestInfo { CFSocketRef socket; @@ -101,6 +103,10 @@ struct iSCSIDIncomingRequestInfo { int fd; }; +struct iSCSIDQueueLoginForTargetPortal { + iSCSITargetRef target; + iSCSIPortalRef portal; +}; const iSCSIDMsgLoginRsp iSCSIDMsgLoginRspInit = { .funcCode = kiSCSIDLogin, @@ -259,7 +265,6 @@ iSCSIAuthRef iSCSIDCreateAuthenticationForTarget(CFStringRef targetIQN) CFRelease(name); if(sharedSecret) CFRelease(sharedSecret); - } else auth = iSCSIAuthCreateNone(); @@ -302,7 +307,7 @@ iSCSIAuthRef iSCSIDCreateAuthenticationForInitiator() return auth; } -errno_t iSCSIDLoginCommon(SID sessionId, +errno_t iSCSIDLoginCommon(SessionIdentifier sessionId, iSCSIMutableTargetRef target, iSCSIPortalRef portal, enum iSCSILoginStatusCode * statusCode) @@ -312,7 +317,7 @@ errno_t iSCSIDLoginCommon(SID sessionId, iSCSIConnectionConfigRef connCfg = NULL; iSCSIAuthRef initiatorAuth = NULL, targetAuth = NULL; - CID connectionId = kiSCSIInvalidConnectionId; + ConnectionIdentifier connectionId = kiSCSIInvalidConnectionId; *statusCode = kiSCSILoginInvalidStatusCode; @@ -336,9 +341,9 @@ errno_t iSCSIDLoginCommon(SID sessionId, // Do either session or connection login if(sessionId == kiSCSIInvalidSessionId) - error = iSCSILoginSession(target,portal,initiatorAuth,targetAuth,sessCfg,connCfg,&sessionId,&connectionId,statusCode); + error = iSCSISessionLogin(sessionManager,target,portal,initiatorAuth,targetAuth,sessCfg,connCfg,&sessionId,&connectionId,statusCode); else - error = iSCSILoginConnection(sessionId,portal,initiatorAuth,targetAuth,connCfg,&connectionId,statusCode); + error = iSCSISessionAddConnection(sessionManager,sessionId,portal,initiatorAuth,targetAuth,connCfg,&connectionId,statusCode); // Log error message if(error) { @@ -350,7 +355,12 @@ errno_t iSCSIDLoginCommon(SID sessionId, iSCSIPortalGetPort(portal), strerror(error)); - asl_log(NULL,NULL,ASL_LEVEL_ERR,"%s",CFStringGetCStringPtr(errorString,kCFStringEncodingASCII)); + CFIndex errorStringLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(errorString),kCFStringEncodingASCII) + sizeof('\0'); + char errorStringBuffer[errorStringLength]; + CFStringGetCString(errorString,errorStringBuffer,errorStringLength,kCFStringEncodingASCII); + + asl_log(NULL, NULL, ASL_LEVEL_ERR, "%s", errorStringBuffer); + CFRelease(errorString); } // Update target alias in preferences (if one was furnished) @@ -389,7 +399,7 @@ errno_t iSCSIDLoginAllPortals(iSCSIMutableTargetRef target, *statusCode = kiSCSILoginInvalidStatusCode; CFStringRef targetIQN = iSCSITargetGetIQN(target); - SID sessionId = iSCSIGetSessionIdForTarget(targetIQN); + SessionIdentifier sessionId = iSCSISessionGetSessionIdForTarget(sessionManager,targetIQN); // Set initial values for maxConnections and activeConnections if(sessionId == kiSCSIInvalidSessionId) @@ -398,7 +408,7 @@ errno_t iSCSIDLoginAllPortals(iSCSIMutableTargetRef target, else { // If session exists, get the max connections and active connections - CFDictionaryRef properties = iSCSICreateCFPropertiesForSession(target); + CFDictionaryRef properties = iSCSISessionCopyCFPropertiesForTarget(sessionManager,target); if(properties) { // Get max connections from property list @@ -406,7 +416,7 @@ errno_t iSCSIDLoginAllPortals(iSCSIMutableTargetRef target, CFNumberGetValue(number,kCFNumberSInt32Type,&maxConnections); CFRelease(properties); - CFArrayRef connections = iSCSICreateArrayOfConnectionsIds(sessionId); + CFArrayRef connections = iSCSISessionCopyArrayOfConnectionIds(sessionManager,sessionId); if(connections) { activeConnections = CFArrayGetCount(connections); @@ -439,12 +449,12 @@ errno_t iSCSIDLoginAllPortals(iSCSIMutableTargetRef target, portalIdx++; // Determine how many connections this session supports - sessionId = iSCSIGetSessionIdForTarget(iSCSITargetGetIQN(target)); + sessionId = iSCSISessionGetSessionIdForTarget(sessionManager,iSCSITargetGetIQN(target)); // If this was the first connection of the session, get the number of // allowed maximum connections if(activeConnections == 1) { - CFDictionaryRef properties = iSCSICreateCFPropertiesForSession(target); + CFDictionaryRef properties = iSCSISessionCopyCFPropertiesForTarget(sessionManager,target); if(properties) { // Get max connections from property list CFNumberRef number = CFDictionaryGetValue(properties,kRFC3720_Key_MaxConnections); @@ -466,25 +476,25 @@ errno_t iSCSIDLoginWithPortal(iSCSIMutableTargetRef target, enum iSCSILoginStatusCode * statusCode) { // Check for active sessions before attempting loginb - SID sessionId = kiSCSIInvalidSessionId; - CID connectionId = kiSCSIInvalidConnectionId; + SessionIdentifier sessionId = kiSCSIInvalidSessionId; + ConnectionIdentifier connectionId = kiSCSIInvalidConnectionId; *statusCode = kiSCSILoginInvalidStatusCode; errno_t errorCode = 0; CFStringRef targetIQN = iSCSITargetGetIQN(target); - sessionId = iSCSIGetSessionIdForTarget(targetIQN); + sessionId = iSCSISessionGetSessionIdForTarget(sessionManager,targetIQN); // Existing session, add a connection if(sessionId != kiSCSIInvalidSessionId) { - connectionId = iSCSIGetConnectionIdForPortal(sessionId,portal); + connectionId = iSCSISessionGetConnectionIdForPortal(sessionManager,sessionId,portal); // If there's an active session display error otherwise login if(connectionId != kiSCSIInvalidConnectionId) {} //iSCSICtlDisplayError("The specified target has an active session over the specified portal."); else { // See if the session can support an additional connection - CFDictionaryRef properties = iSCSICreateCFPropertiesForSession(target); + CFDictionaryRef properties = iSCSISessionCopyCFPropertiesForTarget(sessionManager,target); if(properties) { // Get max connections from property list UInt32 maxConnections; @@ -492,7 +502,7 @@ errno_t iSCSIDLoginWithPortal(iSCSIMutableTargetRef target, CFNumberGetValue(number,kCFNumberSInt32Type,&maxConnections); CFRelease(properties); - CFArrayRef connections = iSCSICreateArrayOfConnectionsIds(sessionId); + CFArrayRef connections = iSCSISessionCopyArrayOfConnectionIds(sessionManager,sessionId); if(connections) { CFIndex activeConnections = CFArrayGetCount(connections); @@ -610,19 +620,19 @@ void iSCSIDLogoutComplete(iSCSITargetRef target,enum iSCSIDAOperationResult resu if(!errorCode) { - SID sessionId = iSCSIGetSessionIdForTarget(iSCSITargetGetIQN(target)); + SessionIdentifier sessionId = iSCSISessionGetSessionIdForTarget(sessionManager,iSCSITargetGetIQN(target)); // For session logout, ensure that disk unmount was successful... if(!portal) { if(result == kiSCSIDAOperationSuccess) - errorCode = iSCSILogoutSession(sessionId,&statusCode); + errorCode = iSCSISessionLogout(sessionManager,sessionId,&statusCode); else errorCode = EBUSY; } else { - CID connectionId = iSCSIGetConnectionIdForPortal(sessionId,portal); - errorCode = iSCSILogoutConnection(sessionId,connectionId,&statusCode); + ConnectionIdentifier connectionId = iSCSISessionGetConnectionIdForPortal(sessionManager,sessionId,portal); + errorCode = iSCSISessionRemoveConnection(sessionManager,sessionId,connectionId,&statusCode); } } @@ -647,7 +657,12 @@ void iSCSIDLogoutComplete(iSCSITargetRef target,enum iSCSIDAOperationResult resu strerror(errorCode)); } - asl_log(NULL,NULL,ASL_LEVEL_ERR,"%s",CFStringGetCStringPtr(errorString,kCFStringEncodingASCII)); + CFIndex errorStringLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(errorString),kCFStringEncodingASCII) + sizeof('\0'); + char errorStringBuffer[errorStringLength]; + CFStringGetCString(errorString,errorStringBuffer,errorStringLength,kCFStringEncodingASCII); + + asl_log(NULL, NULL, ASL_LEVEL_ERR, "%s", errorStringBuffer); + CFRelease(errorString); } @@ -707,9 +722,8 @@ errno_t iSCSIDLogout(int fd,iSCSIDMsgLogoutCmd * cmd) else errorCode = EINVAL; - // See if there exists an active session for this target - SID sessionId = iSCSIGetSessionIdForTarget(iSCSITargetGetIQN(target)); + SessionIdentifier sessionId = iSCSISessionGetSessionIdForTarget(sessionManager,iSCSITargetGetIQN(target)); if(!errorCode && sessionId == kiSCSIInvalidSessionId) { @@ -718,17 +732,22 @@ errno_t iSCSIDLogout(int fd,iSCSIDMsgLogoutCmd * cmd) CFSTR("logout of %@ failed: the target has no active sessions"), iSCSITargetGetIQN(target)); - asl_log(0,0,ASL_LEVEL_CRIT,"%s",CFStringGetCStringPtr(errorString,kCFStringEncodingASCII)); + CFIndex errorStringLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(errorString),kCFStringEncodingASCII) + sizeof('\0'); + char errorStringBuffer[errorStringLength]; + CFStringGetCString(errorString,errorStringBuffer,errorStringLength,kCFStringEncodingASCII); + + asl_log(NULL, NULL, ASL_LEVEL_CRIT, "%s", errorStringBuffer); + CFRelease(errorString); errorCode = EINVAL; } // See if there exists an active connection for this portal - CID connectionId = kiSCSIInvalidConnectionId; + ConnectionIdentifier connectionId = kiSCSIInvalidConnectionId; CFIndex connectionCount = 0; if(!errorCode && portal) { - connectionId = iSCSIGetConnectionIdForPortal(sessionId,portal); + connectionId = iSCSISessionGetConnectionIdForPortal(sessionManager,sessionId,portal); if(connectionId == kiSCSIInvalidConnectionId) { @@ -737,13 +756,18 @@ errno_t iSCSIDLogout(int fd,iSCSIDMsgLogoutCmd * cmd) CFSTR("logout of %@,%@:%@ failed: the portal has no active connections"), iSCSITargetGetIQN(target),iSCSIPortalGetAddress(portal),iSCSIPortalGetPort(portal)); - asl_log(0,0,ASL_LEVEL_CRIT,"%s",CFStringGetCStringPtr(errorString,kCFStringEncodingASCII)); + CFIndex errorStringLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(errorString),kCFStringEncodingASCII) + sizeof('\0'); + char errorStringBuffer[errorStringLength]; + CFStringGetCString(errorString,errorStringBuffer,errorStringLength,kCFStringEncodingASCII); + + asl_log(NULL, NULL, ASL_LEVEL_CRIT, "%s", errorStringBuffer); + CFRelease(errorString); errorCode = EINVAL; } else { // Determine the number of connections - CFArrayRef connectionIds = iSCSICreateArrayOfConnectionsIds(sessionId); + CFArrayRef connectionIds = iSCSISessionCopyArrayOfConnectionIds(sessionManager,sessionId); connectionCount = CFArrayGetCount(connectionIds); CFRelease(connectionIds); } @@ -775,7 +799,7 @@ errno_t iSCSIDLogout(int fd,iSCSIDMsgLogoutCmd * cmd) errno_t iSCSIDCreateArrayOfActiveTargets(int fd,iSCSIDMsgCreateArrayOfActiveTargetsCmd * cmd) { - CFArrayRef sessionIds = iSCSICreateArrayOfSessionIds(); + CFArrayRef sessionIds = iSCSISessionCopyArrayOfSessionIds(sessionManager); CFIndex sessionCount = CFArrayGetCount(sessionIds); // Prepare an array to hold our targets @@ -786,7 +810,7 @@ errno_t iSCSIDCreateArrayOfActiveTargets(int fd,iSCSIDMsgCreateArrayOfActiveTarg // Get target object for each active session and add to array for(CFIndex idx = 0; idx < sessionCount; idx++) { - iSCSITargetRef target = iSCSICreateTargetForSessionId((SID)CFArrayGetValueAtIndex(sessionIds,idx)); + iSCSITargetRef target = iSCSISessionCopyTargetForId(sessionManager,(SessionIdentifier)CFArrayGetValueAtIndex(sessionIds,idx)); CFArrayAppendValue(activeTargets,target); iSCSITargetRelease(target); } @@ -816,7 +840,7 @@ errno_t iSCSIDCreateArrayofActivePortalsForTarget(int fd, iSCSIDMsgCreateArrayOfActivePortalsForTargetCmd * cmd) { // TODO: null check - CFArrayRef sessionIds = iSCSICreateArrayOfSessionIds(); + CFArrayRef sessionIds = iSCSISessionCopyArrayOfSessionIds(sessionManager); CFIndex sessionCount = CFArrayGetCount(sessionIds); // Prepare an array to hold our targets @@ -827,7 +851,7 @@ errno_t iSCSIDCreateArrayofActivePortalsForTarget(int fd, // Get target object for each active session and add to array for(CFIndex idx = 0; idx < sessionCount; idx++) { - iSCSITargetRef target = iSCSICreateTargetForSessionId((SID)CFArrayGetValueAtIndex(sessionIds,idx)); + iSCSITargetRef target = iSCSISessionCopyTargetForId(sessionManager,(SessionIdentifier)CFArrayGetValueAtIndex(sessionIds,idx)); CFArrayAppendValue(activeTargets,target); iSCSITargetRelease(target); } @@ -870,7 +894,7 @@ errno_t iSCSIDIsTargetActive(int fd,iSCSIDMsgIsTargetActiveCmd *cmd) if(target) { iSCSIDMsgIsTargetActiveRsp rsp = iSCSIDMsgIsTargetActiveRspInit; - rsp.active = (iSCSIGetSessionIdForTarget(iSCSITargetGetIQN(target)) != kiSCSIInvalidSessionId); + rsp.active = (iSCSISessionGetSessionIdForTarget(sessionManager,iSCSITargetGetIQN(target)) != kiSCSIInvalidSessionId); iSCSITargetRelease(target); @@ -900,12 +924,12 @@ errno_t iSCSIDIsPortalActive(int fd,iSCSIDMsgIsPortalActiveCmd *cmd) } iSCSIDMsgIsPortalActiveRsp rsp = iSCSIDMsgIsPortalActiveRspInit; - SID sessionId = (iSCSIGetSessionIdForTarget(iSCSITargetGetIQN(target))); + SessionIdentifier sessionId = (iSCSISessionGetSessionIdForTarget(sessionManager,iSCSITargetGetIQN(target))); if(sessionId == kiSCSIInvalidSessionId) rsp.active = false; else - rsp.active = (iSCSIGetConnectionIdForPortal(sessionId,portal) != kiSCSIInvalidConnectionId); + rsp.active = (iSCSISessionGetConnectionIdForPortal(sessionManager,sessionId,portal) != kiSCSIInvalidConnectionId); if(target) iSCSITargetRelease(target); @@ -941,7 +965,7 @@ errno_t iSCSIDQueryTargetForAuthMethod(int fd,iSCSIDMsgQueryTargetForAuthMethodC enum iSCSIAuthMethods authMethod = kiSCSIAuthMethodInvalid; enum iSCSILoginStatusCode statusCode = kiSCSILoginInvalidStatusCode; - errno_t error = iSCSIQueryTargetForAuthMethod(portal,iSCSITargetGetIQN(target),&authMethod,&statusCode); + errno_t error = iSCSIQueryTargetForAuthMethod(sessionManager,portal,iSCSITargetGetIQN(target),&authMethod,&statusCode); // Compose a response to send back to the client iSCSIDMsgQueryTargetForAuthMethodRsp rsp = iSCSIDMsgQueryTargetForAuthMethodRspInit; @@ -975,7 +999,7 @@ errno_t iSCSIDCreateCFPropertiesForSession(int fd, } if(!error) { - CFDictionaryRef properties = iSCSICreateCFPropertiesForSession(target); + CFDictionaryRef properties = iSCSISessionCopyCFPropertiesForTarget(sessionManager,target); // Send back response iSCSIDMsgCreateCFPropertiesForSessionRsp rsp = iSCSIDMsgCreateCFPropertiesForSessionRspInit; @@ -1032,7 +1056,7 @@ errno_t iSCSIDCreateCFPropertiesForConnection(int fd, if(!error) { - CFDictionaryRef properties = iSCSICreateCFPropertiesForConnection(target,portal); + CFDictionaryRef properties = iSCSISessionCopyCFPropertiesForPortal(sessionManager,target,portal); // Send back response iSCSIDMsgCreateCFPropertiesForConnectionRsp rsp = iSCSIDMsgCreateCFPropertiesForConnectionRspInit; @@ -1064,9 +1088,219 @@ errno_t iSCSIDCreateCFPropertiesForConnection(int fd, return error; } + +errno_t iSCSIDAddTargetForSendTargets(iSCSIPreferencesRef preferences, + CFStringRef targetIQN, + iSCSIDiscoveryRecRef discoveryRec, + CFStringRef discoveryPortal) +{ + CFArrayRef portalGroups = iSCSIDiscoveryRecCreateArrayOfPortalGroupTags(discoveryRec,targetIQN); + CFIndex portalGroupCount = CFArrayGetCount(portalGroups); + + // Iterate over portal groups for this target + for(CFIndex portalGroupIdx = 0; portalGroupIdx < portalGroupCount; portalGroupIdx++) + { + CFStringRef portalGroupTag = CFArrayGetValueAtIndex(portalGroups,portalGroupIdx); + CFArrayRef portals = iSCSIDiscoveryRecGetPortals(discoveryRec,targetIQN,portalGroupTag); + CFIndex portalsCount = CFArrayGetCount(portals); + + iSCSIPortalRef portal = NULL; + + // Iterate over portals within this group + for(CFIndex portalIdx = 0; portalIdx < portalsCount; portalIdx++) + { + if(!(portal = CFArrayGetValueAtIndex(portals,portalIdx))) + continue; + + // Add portal to target, or add target as necessary + if(iSCSIPreferencesContainsTarget(preferences,targetIQN)) + iSCSIPreferencesSetPortalForTarget(preferences,targetIQN,portal); + else + iSCSIPreferencesAddDynamicTargetForSendTargets(preferences,targetIQN,portal,discoveryPortal); + } + } + + CFRelease(portalGroups); + + return 0; +} + +/*! Updates an iSCSI preference sobject with information about targets as + * contained in the provided discovery record. + * @param preferences an iSCSI preferences object. + * @param discoveryPortal the portal (address) that was used to perform discovery. + * @param discoveryRec the discovery record resulting from the discovery operation. + * @return an error code indicating the result of the operation. */ +errno_t iSCSIDUpdatePreferencesWithDiscoveredTargets(iSCSISessionManagerRef managerRef, + iSCSIPreferencesRef preferences, + CFStringRef discoveryPortal, + iSCSIDiscoveryRecRef discoveryRec) +{ + CFArrayRef targets = iSCSIDiscoveryRecCreateArrayOfTargets(discoveryRec); + + if(!targets) + return EINVAL; + + CFIndex targetCount = CFArrayGetCount(targets); + + CFMutableDictionaryRef discTargets = CFDictionaryCreateMutable( + kCFAllocatorDefault,0,&kCFTypeDictionaryKeyCallBacks,0); + + for(CFIndex targetIdx = 0; targetIdx < targetCount; targetIdx++) + { + CFStringRef targetIQN = CFArrayGetValueAtIndex(targets,targetIdx); + + // Target exists with static (or other configuration). In + // this case we do nothing, log a message and move on. + if(iSCSIPreferencesContainsTarget(preferences,targetIQN) && + iSCSIPreferencesGetTargetConfigType(preferences,targetIQN) != kiSCSITargetConfigDynamicSendTargets) + { + CFStringRef statusString = CFStringCreateWithFormat( + kCFAllocatorDefault,0, + CFSTR("discovered target %@ already exists with static configuration."), + targetIQN); + + asl_log(NULL,NULL,ASL_LEVEL_INFO,"%s",CFStringGetCStringPtr(statusString,kCFStringEncodingASCII)); + + CFRelease(statusString); + } + // Target doesn't exist, or target exists with SendTargets + // configuration (add or update as necessary) + else { + iSCSIDAddTargetForSendTargets(preferences,targetIQN,discoveryRec,discoveryPortal); + CFStringRef statusString = CFStringCreateWithFormat( + kCFAllocatorDefault,0, + CFSTR("discovered target %@ over discovery portal %@."), + targetIQN,discoveryPortal); + + asl_log(NULL,NULL,ASL_LEVEL_INFO,"%s",CFStringGetCStringPtr(statusString,kCFStringEncodingASCII)); + + CFRelease(statusString); + } + + // As we process each target we'll add it to a temporary dictionary + // for cross-checking against targets that exist in our database + // which have been removed. + CFDictionaryAddValue(discTargets,targetIQN,0); + } + + // Are there any targets that must be removed? Cross-check existing + // list against the list we just built... + CFArrayRef existingTargets = iSCSIPreferencesCreateArrayOfDynamicTargetsForSendTargets(preferences,discoveryPortal); + targetCount = CFArrayGetCount(existingTargets); + + for(CFIndex targetIdx = 0; targetIdx < targetCount; targetIdx++) + { + CFStringRef targetIQN = CFArrayGetValueAtIndex(existingTargets,targetIdx); + + // If we have a target that was not discovered, then we need to remove + // it from our property list... + if(!CFDictionaryContainsKey(discTargets,targetIQN)) { + + // If the target is logged in, logout of the target and remove it + SessionIdentifier sessionId = iSCSISessionGetSessionIdForTarget(managerRef,targetIQN); + enum iSCSILogoutStatusCode statusCode; + if(sessionId != kiSCSIInvalidSessionId) + iSCSISessionLogout(managerRef,sessionId,&statusCode); + + iSCSIPreferencesRemoveTarget(preferences,targetIQN); + } + } + + CFRelease(targets); + CFRelease(discTargets); + CFRelease(existingTargets); + + return 0; +} + +/*! Scans all iSCSI discovery portals found in iSCSI preferences + * for targets (SendTargets). Returns a dictionary of key-value pairs + * with discovery record objects as values and discovery portal names + * as keys. + * @param preferences an iSCSI preferences object. + * @return a dictionary key-value pairs of dicovery portal names (addresses) + * and the discovery records associated with the result of SendTargets + * discovery of those portals. */ +CFDictionaryRef iSCSIDCreateRecordsWithSendTargets(iSCSISessionManagerRef managerRef, + iSCSIPreferencesRef preferences) +{ + if(!preferences) + return NULL; + + CFArrayRef portals = iSCSIPreferencesCreateArrayOfPortalsForSendTargetsDiscovery(preferences); + + // Quit if no discovery portals are defined + if(!portals) + return NULL; + + CFIndex portalCount = CFArrayGetCount(portals); + + CFStringRef discoveryPortal = NULL; + iSCSIPortalRef portal = NULL; + + CFMutableDictionaryRef discoveryRecords = CFDictionaryCreateMutable(kCFAllocatorDefault,0, + &kiSCSITypeDictionaryKeyCallbacks, + &kiSCSITypeDictionaryValueCallbacks); + + for(CFIndex idx = 0; idx < portalCount; idx++) + { + discoveryPortal = CFArrayGetValueAtIndex(portals,idx); + + if(!discoveryPortal) + continue; + + portal = iSCSIPreferencesCopySendTargetsDiscoveryPortal(preferences,discoveryPortal); + + if(!portal) + continue; + + enum iSCSILoginStatusCode statusCode; + iSCSIMutableDiscoveryRecRef discoveryRec; + + // If there was an error, log it and move on to the next portal + errno_t error = 0; + iSCSIAuthRef auth = iSCSIAuthCreateNone(); + if((error = iSCSIQueryPortalForTargets(managerRef,portal,auth,&discoveryRec,&statusCode))) + { + CFStringRef errorString = CFStringCreateWithFormat( + kCFAllocatorDefault,0, + CFSTR("system error (code %d) occurred during SendTargets discovery of %@."), + error,discoveryPortal); + + asl_log(NULL,NULL,ASL_LEVEL_ERR,"%s",CFStringGetCStringPtr(errorString,kCFStringEncodingASCII)); + CFRelease(errorString); + } + else if(statusCode != kiSCSILoginSuccess) { + CFStringRef errorString = CFStringCreateWithFormat( + kCFAllocatorDefault,0, + CFSTR("login failed with (code %d) during SendTargets discovery of %@."), + statusCode,discoveryPortal); + + asl_log(NULL,NULL,ASL_LEVEL_ERR,"%s",CFStringGetCStringPtr(errorString,kCFStringEncodingASCII)); + CFRelease(errorString); + } + else { + // Queue discovery record so that it can be processes later + if(discoveryRec) { + CFDictionarySetValue(discoveryRecords,discoveryPortal,discoveryRec); + iSCSIDiscoveryRecRelease(discoveryRec); + } + } + + iSCSIAuthRelease(auth); + iSCSIPortalRelease(portal); + } + + // Release the array of discovery portals + CFRelease(portals); + + return discoveryRecords; +} + void * iSCSIDRunDiscovery(void * context) { - CFDictionaryRef discoveryRecords = iSCSIDiscoveryCreateRecordsWithSendTargets(preferences); + CFDictionaryRef discoveryRecords = iSCSIDCreateRecordsWithSendTargets(sessionManager,preferences); // Process discovery results if any if(discoveryRecords) { @@ -1080,7 +1314,7 @@ void * iSCSIDRunDiscovery(void * context) CFDictionaryGetKeysAndValues(discoveryRecords,keys,values); for(CFIndex i = 0; i < count; i++) - iSCSIDiscoveryUpdatePreferencesWithDiscoveredTargets(preferences,keys[i],values[i]); + iSCSIDUpdatePreferencesWithDiscoveredTargets(sessionManager,preferences,keys[i],values[i]); iSCSIPreferencesSynchronzeAppValues(preferences); pthread_mutex_unlock(&preferencesMutex); @@ -1367,6 +1601,86 @@ errno_t iSCSIDRemoveSharedSecret(int fd,iSCSIDMsgRemoveSharedSecretCmd *cmd) return 0; } +/*! Callback function used to process a queued login once + * the network becomes available. */ +void iSCSIDProcessQueuedLogin(SCNetworkReachabilityRef reachabilityTarget, + SCNetworkReachabilityFlags flags, + void * info) +{ + struct iSCSIDQueueLoginForTargetPortal * loginRef = info; + + iSCSIMutableTargetRef target = iSCSITargetCreateMutableCopy(loginRef->target); + iSCSIPortalRef portal = loginRef->portal; + + enum iSCSILoginStatusCode statusCode; + iSCSIDLoginWithPortal(target,portal,&statusCode); + + iSCSITargetRelease(target); + iSCSITargetRelease(loginRef->target); + iSCSIPortalRelease(portal); + + free(loginRef); +} + +/*! Helper function used by auto-login, sleep-mode and persistent + * functions to login to the specified target using the specified + * portal when the network becomes available. */ +void iSCSIDQueueLogin(iSCSITargetRef target,iSCSIPortalRef portal) +{ + SCNetworkReachabilityRef reachabilityTarget; + SCNetworkReachabilityContext reachabilityContext; + + struct iSCSIDQueueLoginForTargetPortal * loginRef = malloc(sizeof(struct iSCSIDQueueLoginForTargetPortal)); + loginRef->target = target; + loginRef->portal = portal; + + reachabilityContext.info = loginRef; + reachabilityContext.copyDescription = 0; + reachabilityContext.retain = 0; + reachabilityContext.release = 0; + + char portalAddressBuffer[NI_MAXHOST]; + + if(!CFStringGetCString(iSCSIPortalGetAddress(portal),portalAddressBuffer,NI_MAXHOST,kCFStringEncodingASCII)) + return; + + // If a specific host interface was specified, create with pair... + if(CFStringCompare(iSCSIPortalGetHostInterface(portal),kiSCSIDefaultHostInterface,0) == kCFCompareEqualTo) + reachabilityTarget = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault,portalAddressBuffer); + else { + + struct sockaddr_storage remoteAddress, localAddress; + iSCSIUtilsGetAddressForPortal(portal,&remoteAddress,&localAddress); + + reachabilityTarget = SCNetworkReachabilityCreateWithAddressPair(kCFAllocatorDefault, + (const struct sockaddr *)&localAddress, + (const struct sockaddr *)&remoteAddress); + } + + SCNetworkReachabilitySetCallback(reachabilityTarget,iSCSIDProcessQueuedLogin,&reachabilityContext); + SCNetworkReachabilityScheduleWithRunLoop(reachabilityTarget,CFRunLoopGetMain(),kCFRunLoopDefaultMode); +} + +void iSCSIDSessionTimeoutHandler(iSCSITargetRef target,iSCSIPortalRef portal) +{ + if(!target || !portal) + return; + + // Post message to system log + asl_log(NULL,NULL,ASL_LEVEL_ERR,"TCP timeout for %s over portal %s.", + CFStringGetCStringPtr(iSCSITargetGetIQN(target),kCFStringEncodingASCII), + CFStringGetCStringPtr(iSCSIPortalGetAddress(portal),kCFStringEncodingASCII)); + + // If this was a persistance target, queue another login when the network is + // available + if(iSCSIPreferencesGetPersistenceForTarget(preferences,iSCSITargetGetIQN(target))) + iSCSIDQueueLogin(target,portal); + else { + iSCSITargetRelease(target); + iSCSIPortalRelease(portal); + } +} + /*! Automatically logs in to targets that were specified for auto-login. * Used during startup of the daemon to log in to either static * dynamic targets for which the auto-login option is enabled. */ @@ -1385,14 +1699,27 @@ void iSCSIDAutoLogin() for(CFIndex idx = 0; idx < targetsCount; idx++) { CFStringRef targetIQN = CFArrayGetValueAtIndex(targets,idx); + iSCSITargetRef target = iSCSIPreferencesCopyTarget(preferences,targetIQN); + // See if this target requires auto-login and process it if(iSCSIPreferencesGetAutoLoginForTarget(preferences,targetIQN)) { - iSCSITargetRef targetTemp = iSCSIPreferencesCopyTarget(preferences,targetIQN); - iSCSIMutableTargetRef target = iSCSITargetCreateMutableCopy(targetTemp); - iSCSITargetRelease(targetTemp); - enum iSCSILoginStatusCode statusCode; - iSCSIDLoginAllPortals(target,&statusCode); + + CFArrayRef portals = iSCSIPreferencesCreateArrayOfPortalsForTarget(preferences,targetIQN); + if(!portals) + continue; + + CFIndex portalsCount = CFArrayGetCount(portals); + + // Queue a login operation for a each portal; mind the interface is one is required + for(CFIndex portalIdx = 0; portalIdx < portalsCount; portalIdx++) + { + CFStringRef portalAddress = CFArrayGetValueAtIndex(portals,portalIdx); + iSCSIPortalRef portal = iSCSIPreferencesCopyPortalForTarget(preferences,targetIQN,portalAddress); + iSCSIDQueueLogin(target,portal); + } + iSCSITargetRelease(target); + CFRelease(portals); } } CFRelease(targets); @@ -1439,16 +1766,16 @@ void iSCSIDPrepareForSystemSleepComplete(iSCSITargetRef target, enum iSCSIDAOperationResult result, void * context) { - SID sessionId = (SID)context; + SessionIdentifier sessionId = (SessionIdentifier)context; enum iSCSILogoutStatusCode statusCode; - iSCSILogoutSession(sessionId,&statusCode); + iSCSISessionLogout(sessionManager,sessionId,&statusCode); } /*! Saves a dictionary of active targets and portals that * is used to restore active sessions upon wakeup. */ void iSCSIDPrepareForSystemSleep() { - CFArrayRef sessionIds = iSCSICreateArrayOfSessionIds(); + CFArrayRef sessionIds = iSCSISessionCopyArrayOfSessionIds(sessionManager); if(!sessionIds) return; @@ -1469,22 +1796,22 @@ void iSCSIDPrepareForSystemSleep() for(CFIndex idx = 0; idx < sessionCount; idx++) { - SID sessionId = (SID)CFArrayGetValueAtIndex(sessionIds,idx); - iSCSITargetRef target = iSCSICreateTargetForSessionId(sessionId); + SessionIdentifier sessionId = (SessionIdentifier)CFArrayGetValueAtIndex(sessionIds,idx); + iSCSITargetRef target = iSCSISessionCopyTargetForId(sessionManager,sessionId); if(!target) continue; CFStringRef targetIQN = iSCSITargetGetIQN(target); - CFArrayRef connectionIds = iSCSICreateArrayOfConnectionsIds(sessionId); + CFArrayRef connectionIds = iSCSISessionCopyArrayOfConnectionIds(sessionManager,sessionId); CFIndex connectionCount = CFArrayGetCount(connectionIds); CFMutableArrayRef portals = CFArrayCreateMutable(kCFAllocatorDefault,0,&kCFTypeArrayCallBacks); for(CFIndex connIdx = 0; connIdx < connectionCount; connIdx++) { - CID connectionId = (CID)CFArrayGetValueAtIndex(connectionIds,connIdx); - iSCSIPortalRef portal = iSCSICreatePortalForConnectionId(sessionId,connectionId); + ConnectionIdentifier connectionId = (ConnectionIdentifier)CFArrayGetValueAtIndex(connectionIds,connIdx); + iSCSIPortalRef portal = iSCSISessionCopyPortalForConnectionId(sessionManager,sessionId,connectionId); CFArrayAppendValue(portals,portal); CFRelease(portal); } @@ -1610,7 +1937,7 @@ void iSCSIDProcessIncomingRequest(void * info) } } -/*! Handle an incoming connection from iscsictl. Once a connection is +/*! Handle an incoming connection from a client. Once a connection is * established, main runloop calls this function. This function processes * all incoming commands until a shutdown request is received, at which point * this function terminates and returns control to the run loop. For this @@ -1660,15 +1987,26 @@ int main(void) { // Initialize logging aslclient log = asl_open(NULL,NULL,ASL_OPT_STDERR); + + // Start the iSCSI session manager (which creates a connection to the HBA) + iSCSISessionManagerCallBacks callbacks; + callbacks.timeoutCallback = iSCSIDSessionTimeoutHandler; + sessionManager = iSCSISessionManagerCreate(kCFAllocatorDefault,callbacks); + + // Let launchd call us again once the HBA kext is loaded + if(!sessionManager) + return EAGAIN; + + iSCSISessionManagerScheduleWithRunLoop(sessionManager,CFRunLoopGetMain(),kCFRunLoopDefaultMode); // Read configuration parameters from the iSCSI property list iSCSIDUpdatePreferencesFromAppValues(); // Update initiator name and alias internally CFStringRef initiatorIQN = iSCSIPreferencesCopyInitiatorIQN(preferences); - + if(initiatorIQN) { - iSCSISetInitiatorName(initiatorIQN); + iSCSISessionManagerSetInitiatorName(sessionManager,initiatorIQN); CFRelease(initiatorIQN); } else { @@ -1678,7 +2016,7 @@ int main(void) CFStringRef initiatorAlias = iSCSIPreferencesCopyInitiatorAlias(preferences); if(initiatorAlias) { - iSCSISetInitiatorAlias(initiatorAlias); + iSCSISessionManagerSetInitiatorName(sessionManager,initiatorAlias); CFRelease(initiatorAlias); } else { @@ -1761,10 +2099,6 @@ int main(void) // Ignore SIGPIPE (generated when the client closes the connection) signal(SIGPIPE,sig_pipe_handler); - // Initialize iSCSI connection to kernel (ability to call iSCSI kernel - // functions and receive notifications from the kernel). - iSCSIInitialize(CFRunLoopGetMain()); - // Setup authorization rights if none exist AuthorizationRef authorization; AuthorizationCreate(NULL,NULL,0,&authorization); @@ -1778,7 +2112,10 @@ int main(void) iSCSIDAutoLogin(); CFRunLoopRun(); - iSCSICleanup(); + + iSCSISessionManagerUnscheduleWithRunloop(sessionManager,CFRunLoopGetMain(),kCFRunLoopDefaultMode); + iSCSISessionManagerRelease(sessionManager); + sessionManager = NULL; // Deregister for power iSCSIDDeregisterForPowerEvents(); diff --git a/Source/User/iscsid/iSCSIDiscovery.c b/Source/User/iscsid/iSCSIDiscovery.c index 35aabe49..df2352d0 100644 --- a/Source/User/iscsid/iSCSIDiscovery.c +++ b/Source/User/iscsid/iSCSIDiscovery.c @@ -70,7 +70,8 @@ errno_t iSCSIDiscoveryAddTargetForSendTargets(iSCSIPreferencesRef preferences, * @param discoveryPortal the portal (address) that was used to perform discovery. * @param discoveryRec the discovery record resulting from the discovery operation. * @return an error code indicating the result of the operation. */ -errno_t iSCSIDiscoveryUpdatePreferencesWithDiscoveredTargets(iSCSIPreferencesRef preferences, +errno_t iSCSIDiscoveryUpdatePreferencesWithDiscoveredTargets(iSCSISessionManagerRef managerRef, + iSCSIPreferencesRef preferences, CFStringRef discoveryPortal, iSCSIDiscoveryRecRef discoveryRec) { @@ -98,8 +99,12 @@ errno_t iSCSIDiscoveryUpdatePreferencesWithDiscoveredTargets(iSCSIPreferencesRef CFSTR("discovered target %@ already exists with static configuration."), targetIQN); - asl_log(NULL,NULL,ASL_LEVEL_INFO,"%s",CFStringGetCStringPtr(statusString,kCFStringEncodingASCII)); + CFIndex statusStringLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(statusString),kCFStringEncodingASCII) + sizeof('\0'); + char statusStringBuffer[statusStringLength]; + CFStringGetCString(statusString,statusStringBuffer,statusStringLength,kCFStringEncodingASCII); + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s", statusStringBuffer); + CFRelease(statusString); } // Target doesn't exist, or target exists with SendTargets @@ -111,8 +116,12 @@ errno_t iSCSIDiscoveryUpdatePreferencesWithDiscoveredTargets(iSCSIPreferencesRef CFSTR("discovered target %@ over discovery portal %@."), targetIQN,discoveryPortal); - asl_log(NULL,NULL,ASL_LEVEL_INFO,"%s",CFStringGetCStringPtr(statusString,kCFStringEncodingASCII)); - + CFIndex statusStringLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(statusString),kCFStringEncodingASCII) + sizeof('\0'); + char statusStringBuffer[statusStringLength]; + CFStringGetCString(statusString,statusStringBuffer,statusStringLength,kCFStringEncodingASCII); + + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s", statusStringBuffer); + CFRelease(statusString); } @@ -136,10 +145,10 @@ errno_t iSCSIDiscoveryUpdatePreferencesWithDiscoveredTargets(iSCSIPreferencesRef if(!CFDictionaryContainsKey(discTargets,targetIQN)) { // If the target is logged in, logout of the target and remove it - SID sessionId = iSCSIGetSessionIdForTarget(targetIQN); + SessionIdentifier sessionId = iSCSISessionGetSessionIdForTarget(managerRef,targetIQN); enum iSCSILogoutStatusCode statusCode; if(sessionId != kiSCSIInvalidSessionId) - iSCSILogoutSession(sessionId,&statusCode); + iSCSISessionLogout(managerRef,sessionId,&statusCode); iSCSIPreferencesRemoveTarget(preferences,targetIQN); } @@ -160,7 +169,8 @@ errno_t iSCSIDiscoveryUpdatePreferencesWithDiscoveredTargets(iSCSIPreferencesRef * @return a dictionary key-value pairs of dicovery portal names (addresses) * and the discovery records associated with the result of SendTargets * discovery of those portals. */ -CFDictionaryRef iSCSIDiscoveryCreateRecordsWithSendTargets(iSCSIPreferencesRef preferences) +CFDictionaryRef iSCSIDiscoveryCreateRecordsWithSendTargets(iSCSISessionManagerRef managerRef, + iSCSIPreferencesRef preferences) { if(!preferences) return NULL; @@ -198,14 +208,19 @@ CFDictionaryRef iSCSIDiscoveryCreateRecordsWithSendTargets(iSCSIPreferencesRef p // If there was an error, log it and move on to the next portal errno_t error = 0; iSCSIAuthRef auth = iSCSIAuthCreateNone(); - if((error = iSCSIQueryPortalForTargets(portal,auth,&discoveryRec,&statusCode))) + if((error = iSCSIQueryPortalForTargets(managerRef,portal,auth,&discoveryRec,&statusCode))) { CFStringRef errorString = CFStringCreateWithFormat( kCFAllocatorDefault,0, CFSTR("system error (code %d) occurred during SendTargets discovery of %@."), error,discoveryPortal); - asl_log(NULL,NULL,ASL_LEVEL_ERR,"%s",CFStringGetCStringPtr(errorString,kCFStringEncodingASCII)); + CFIndex errorStringLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(errorString),kCFStringEncodingASCII) + sizeof('\0'); + char errorStringBuffer[errorStringLength]; + CFStringGetCString(errorString,errorStringBuffer,errorStringLength,kCFStringEncodingASCII); + + asl_log(NULL, NULL, ASL_LEVEL_ERR, "%s", errorStringBuffer); + CFRelease(errorString); } else if(statusCode != kiSCSILoginSuccess) { @@ -214,7 +229,12 @@ CFDictionaryRef iSCSIDiscoveryCreateRecordsWithSendTargets(iSCSIPreferencesRef p CFSTR("login failed with (code %d) during SendTargets discovery of %@."), statusCode,discoveryPortal); - asl_log(NULL,NULL,ASL_LEVEL_ERR,"%s",CFStringGetCStringPtr(errorString,kCFStringEncodingASCII)); + CFIndex errorStringLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(errorString),kCFStringEncodingASCII) + sizeof('\0'); + char errorStringBuffer[errorStringLength]; + CFStringGetCString(errorString,errorStringBuffer,errorStringLength,kCFStringEncodingASCII); + + asl_log(NULL, NULL, ASL_LEVEL_ERR, "%s", errorStringBuffer); + CFRelease(errorString); } else { diff --git a/Source/User/iscsid/iSCSIDiscovery.h b/Source/User/iscsid/iSCSIDiscovery.h index 3af95fe8..c959e35c 100644 --- a/Source/User/iscsid/iSCSIDiscovery.h +++ b/Source/User/iscsid/iSCSIDiscovery.h @@ -45,7 +45,8 @@ * @return a dictionary key-value pairs of dicovery portal names (addresses) * and the discovery records associated with the result of SendTargets * discovery of those portals. */ -CFDictionaryRef iSCSIDiscoveryCreateRecordsWithSendTargets(iSCSIPreferencesRef preferences); +CFDictionaryRef iSCSIDiscoveryCreateRecordsWithSendTargets(iSCSISessionManagerRef managerRef, + iSCSIPreferencesRef preferences); /*! Updates an iSCSI preference sobject with information about targets as * contained in the provided discovery record. @@ -53,7 +54,8 @@ CFDictionaryRef iSCSIDiscoveryCreateRecordsWithSendTargets(iSCSIPreferencesRef p * @param discoveryPortal the portal (address) that was used to perform discovery. * @param discoveryRec the discovery record resulting from the discovery operation. * @return an error code indicating the result of the operation. */ -errno_t iSCSIDiscoveryUpdatePreferencesWithDiscoveredTargets(iSCSIPreferencesRef preferences, +errno_t iSCSIDiscoveryUpdatePreferencesWithDiscoveredTargets(iSCSISessionManagerRef managerRef, + iSCSIPreferencesRef preferences, CFStringRef discoveryPortal, iSCSIDiscoveryRecRef discoveryRec); diff --git a/Source/User/iscsid/iSCSIHBAInterface.c b/Source/User/iscsid/iSCSIHBAInterface.c new file mode 100644 index 00000000..fe8c83ae --- /dev/null +++ b/Source/User/iscsid/iSCSIHBAInterface.c @@ -0,0 +1,1068 @@ +/* + * Copyright (c) 2016, Nareg Sinenian + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INConnectionIdentifierENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "iSCSIHBAInterface.h" +#include "iSCSIHBATypes.h" +#include "iSCSIPDUUser.h" + +#include +#include + +struct __iSCSIHBAInterface { + + /*! Allocator used to create this instance. */ + CFAllocatorRef allocator; + + /*! The iSCSI HBA service. */ + io_service_t service; + + /*! Connection to kernel user client. */ + io_connect_t connect; + + /*! Runloop source associated with this instance. */ + CFRunLoopSourceRef source; + + /*! Mach port used to receive notifications. */ + CFMachPortRef notificationPort; + + /*! Callback that will handle kernel notifications. */ + iSCSIHBANotificationCallBack callback; + + /*! Notification data used when invoking the callback. */ + struct __iSCSIHBANotificationContext notifyContext; +}; + +/*! Handles messages sent from the HBA. This is an internal handler that is called first + * to adhere to the required Mach callback prototype. The info parameter contains + * information about an iSCSIHBAInterface instance (which includes user-defined data). */ +static void iSCSIHBANotificationHandler(CFMachPortRef port,void * msg,CFIndex size,void * info) +{ + // The parameter is a notification message + iSCSIHBANotificationMessage * notificationMsg; + if(!(notificationMsg = msg)) + return; + + // Process notification type and return if invalid + enum iSCSIHBANotificationTypes type = (enum iSCSIHBANotificationTypes)notificationMsg->notificationType; + + if(type == kiSCSIHBANotificationInvalid) + return; + + // Call the callback function with the message type and body + iSCSIHBAInterfaceRef interface = (iSCSIHBAInterfaceRef)info; + if(interface->callback) + interface->callback(interface,type,msg,interface->notifyContext.info); +} + +/*! Schedules execution of various tasks, including handling of kernel notifications + * on for the specified interface instance over the designated runloop. + * @param interface an instance of an iSCSIHBAInterface. + * @param runLoop the runloop to schedule + * @param runLoopMode the execution mode for the runLoop. */ +void iSCSIHBAInterfaceScheduleWithRunloop(iSCSIHBAInterfaceRef interface, + CFRunLoopRef runLoop, + CFStringRef runLoopMode) +{ + if(interface->notificationPort && interface->source == NULL) { + CFRunLoopSourceRef source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault,interface->notificationPort,0); + CFRunLoopAddSource(runLoop,source,runLoopMode); + interface->source = source; + } +} + +/*! Unschedules execution of various tasks, including handling of kernel notifications + * on for the specified interface instance over the designated runloop. + * @param interface an instance of an iSCSIHBAInterface. + * @param runLoop the runloop to schedule + * @param runLoopMode the execution mode for the runLoop. */ +void iSCSIHBAInterfaceUnscheduleWithRunloop(iSCSIHBAInterfaceRef interface, + CFRunLoopRef runLoop, + CFStringRef runLoopMode) +{ + if(interface->notificationPort) { + CFRunLoopSourceRef source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault,interface->notificationPort,0); + CFRunLoopRemoveSource(runLoop,source,runLoopMode); + } +} + +/*! Opens a connection to the iSCSI initiator. A connection must be + * successfully opened before any of the supporting functions below can be + * called. */ +iSCSIHBAInterfaceRef iSCSIHBAInterfaceCreate(CFAllocatorRef allocator,iSCSIHBANotificationCallBack callback,iSCSIHBANotificationContext * context) +{ + io_service_t service = IO_OBJECT_NULL; + io_connect_t connect = IO_OBJECT_NULL; + kern_return_t result = kIOReturnSuccess; + CFMachPortRef notificationPort = NULL; + + iSCSIHBAInterfaceRef interface = CFAllocatorAllocate(allocator,sizeof(struct __iSCSIHBAInterface),0); + + // Create a dictionary to match iSCSIkext + CFMutableDictionaryRef matchingDict = NULL; + matchingDict = IOServiceMatching(kiSCSIVirtualHBA_IOClassName); + + service = IOServiceGetMatchingService(kIOMasterPortDefault,matchingDict); + + // Check to see if the driver was found in the I/O registry + // and open a connection to it. + if(service != IO_OBJECT_NULL) { + result = IOServiceOpen(service,mach_task_self(),0,&connect); + } + + if(result == kIOReturnSuccess) + result = IOConnectCallScalarMethod(connect,kiSCSIOpenInitiator,0,0,0,0); + + if(result == kIOReturnSuccess) { + + CFMachPortContext notificationContext; + notificationContext.version = 0; + notificationContext.info = (void *)interface; + notificationContext.release = 0; + notificationContext.retain = 0; + notificationContext.copyDescription = NULL; + + // Create a mach port to receive notifications from the kernel + notificationPort = CFMachPortCreate(allocator,iSCSIHBANotificationHandler,¬ificationContext,NULL); + result = IOConnectSetNotificationPort(connect,0,CFMachPortGetPort(notificationPort),0); + } + + if(result == kIOReturnSuccess) { + interface->allocator = allocator; + interface->service = service; + interface->connect = connect; + interface->notificationPort = notificationPort; + interface->callback = callback; + interface->source = NULL; + memcpy(&interface->notifyContext,context,sizeof(struct __iSCSIHBANotificationContext)); + + // Retain user-defined data if a callback was provided + // (this may be NULL in which case we are not responsible) + if(interface->notifyContext.retain) + interface->notifyContext.retain(interface->notifyContext.info); + + } + // Cleanup + else { + if(notificationPort) + CFRelease(notificationPort); + if(connect != IO_OBJECT_NULL) + IOObjectRelease(connect); + if(service != IO_OBJECT_NULL) + IOObjectRelease(service); + + CFAllocatorDeallocate(allocator,interface); + } + return interface; +} + +/*! Closes a connection to the iSCSI initiator. */ +void iSCSIHBAInterfaceRelease(iSCSIHBAInterfaceRef interface) +{ + if(!interface) + return; + + // Release runloop source is one exists + if(interface->source) + CFRelease(interface->source); + + // Close connection to the driver + IOConnectCallScalarMethod(interface->connect,kiSCSICloseInitiator,0,0,0,0); + + // Clean up (now that we have a connection we no longer need the object) + IOServiceClose(interface->connect); + + // Stop receiving HBA notifications + if(interface->notificationPort) { + CFRelease(interface->notificationPort); + } + + // Release user-defined data if a callback was provided + // (this may be NULL in which case we are not responsible) + if(interface->notifyContext.release) + interface->notifyContext.release(interface->notifyContext.info); + + CFAllocatorDeallocate(interface->allocator,interface); +} + +/*! Allocates a new iSCSI session in the kernel and creates an associated + * connection to the target portal. Additional connections may be added to the + * session by calling iSCSIHBAInterfaceCreateConnection(). + * @param interface an instance of an iSCSIHBAInterface. + * @param targetIQN the name of the target. + * @param portalAddress the portal address (IPv4/IPv6, or DNS name). + * @param portalPort the TCP port used to connect to the portal. + * @param hostInterface the name of the host interface adapter to use. + * @param portalSockaddr the BSD socket structure used to identify the target. + * @param hostSockaddr the BSD socket structure used to identify the host. This + * specifies the interface that the connection will be bound to. + * @param sessionId the session identifier for the new session (returned). + * @param connectionId the identifier of the new connection (returned). + * @return An error code if a valid session could not be created. */ +IOReturn iSCSIHBAInterfaceCreateSession(iSCSIHBAInterfaceRef interface, + CFStringRef targetIQN, + CFStringRef portalAddress, + CFStringRef portalPort, + CFStringRef hostInterface, + const struct sockaddr_storage * remoteAddress, + const struct sockaddr_storage * localAddress, + SessionIdentifier * sessionId, + ConnectionIdentifier * connectionId) +{ + // Check parameters + if(!interface || !portalAddress || !portalPort || !hostInterface || !remoteAddress || + !localAddress || !sessionId || !connectionId) + return kIOReturnBadArgument; + + // Pack the input parameters into a single buffer to send to the kernel + const int kNumParams = 6; + void * params[kNumParams]; + size_t paramSize[kNumParams]; + + // Add one for string lengths to copy the NULL character (CFGetStringLength + // does not include the length of the NULL terminator) + paramSize[0] = CFStringGetLength(targetIQN) + 1; + paramSize[1] = CFStringGetLength(portalAddress) + 1; + paramSize[2] = CFStringGetLength(portalPort) + 1; + paramSize[3] = CFStringGetLength(hostInterface) + 1; + paramSize[4] = sizeof(struct sockaddr_storage); + paramSize[5] = sizeof(struct sockaddr_storage); + + // Populate parameters + params[0] = malloc(paramSize[0]); + params[1] = malloc(paramSize[1]); + params[2] = malloc(paramSize[2]); + params[3] = malloc(paramSize[3]); + params[4] = (void*)remoteAddress; + params[5] = (void*)localAddress; + + CFStringGetCString(targetIQN,params[0],paramSize[0],kCFStringEncodingASCII); + CFStringGetCString(portalAddress,params[1],paramSize[1],kCFStringEncodingASCII); + CFStringGetCString(portalPort,params[2],paramSize[2],kCFStringEncodingASCII); + CFStringGetCString(hostInterface,params[3],paramSize[3],kCFStringEncodingASCII); + + // The input buffer will first have eight bytes to denote the length of + // the portion that follows. So for each of the six input parameters, + // we'll have six UInt64 blocks that indicate the size up front. + size_t header = kNumParams*sizeof(UInt64); + size_t inputStructSize = header; + + CFIndex paramIdx = 0; + while(paramIdx < kNumParams) { + inputStructSize += paramSize[paramIdx]; + paramIdx++; + } + + UInt8 * inputStruct = (UInt8*)malloc(inputStructSize); + UInt8 * inputStructPos = inputStruct + header; + + paramIdx = 0; + while(paramIdx < kNumParams) { + memcpy(inputStructPos,params[paramIdx],paramSize[paramIdx]); + inputStructPos += paramSize[paramIdx]; + + UInt64 * header = (UInt64*)(inputStruct + sizeof(UInt64)*paramIdx); + *header = paramSize[paramIdx]; + paramIdx++; + } + + const UInt32 inputCnt = 1; + UInt64 inputs[inputCnt]; + inputs[0] = kNumParams; + + const UInt32 expOutputCnt = 3; + UInt64 output[expOutputCnt]; + UInt32 outputCnt = expOutputCnt; + + kern_return_t result = + IOConnectCallMethod(interface->connect,kiSCSICreateSession,inputs,inputCnt, + inputStruct,inputStructSize,output,&outputCnt,0,0); + + // Free allocated memory + free(params[0]); + free(params[1]); + free(params[2]); + free(params[3]); + free(inputStruct); + + if(result == kIOReturnSuccess && outputCnt == expOutputCnt) { + *sessionId = (UInt16)output[0]; + *connectionId = (UInt32)output[1]; + + IOReturn error = (IOReturn)output[2]; + return error; + } + + return result; +} + +/*! Releases an iSCSI session, including all connections associated with that + * session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId the session qualifier part of the ISID. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceReleaseSession(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId) +{ + // Check parameters + if(!interface || sessionId == kiSCSIInvalidSessionId) + return kIOReturnBadArgument; + + // Tell the kernel to drop this session and all of its related resources + const UInt32 inputCnt = 1; + UInt64 input = sessionId; + + return IOConnectCallScalarMethod(interface->connect,kiSCSIReleaseSession,&input,inputCnt,0,0); +} + +/*! Sets parameter associated with a particular session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId the qualifier part of the ISID (see RFC3720). + * @param parameter the parameter to set. + * @param paramVal the value for the specified parameter. + * @param paramSize the size, in bytes of paramVal. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceSetSessionParameter(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + enum iSCSIHBASessionParameters parameter, + void * paramVal, + size_t paramSize) +{ + // Check parameters + if(!interface || sessionId == kiSCSIInvalidSessionId || !paramVal || paramSize == 0) + return kIOReturnBadArgument; + + UInt64 paramValCopy = 0; + memcpy(¶mValCopy,paramVal,paramSize); + + const UInt32 inputCnt = 3; + const UInt64 input[] = {sessionId,parameter,paramValCopy}; + + return IOConnectCallScalarMethod(interface->connect,kiSCSISetSessionParameter,input,inputCnt,0,0); +} + +/*! Gets parameter associated with a particular session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId the qualifier part of the ISID (see RFC3720). + * @param parameter the parameter to get. + * @param paramVal the returned value for the specified parameter. + * @param paramSize the size, in bytes of paramVal. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceGetSessionParameter(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + enum iSCSIHBASessionParameters parameter, + void * paramVal, + size_t paramSize) +{ + // Check parameters + if(!interface || sessionId == kiSCSIInvalidSessionId || !paramVal || paramSize == 0) + return kIOReturnBadArgument; + + const UInt32 inputCnt = 2; + const UInt64 input[] = {sessionId,parameter}; + + UInt32 outputCnt = 1; + UInt64 output; + + kern_return_t error = IOConnectCallScalarMethod(interface->connect,kiSCSIGetSessionParameter, + input,inputCnt,&output,&outputCnt); + + if(error == kIOReturnSuccess) + memcpy(paramVal,&output,paramSize); + + return error; +} + +/*! Allocates an additional iSCSI connection for a particular session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId the session to create a new connection for. + * @param portalAddress the portal address (IPv4/IPv6, or DNS name). + * @param portalPort the TCP port used to connect to the portal. + * @param hostInterface the name of the host interface adapter to use. + * @param portalSockaddr the BSD socket structure used to identify the target. + * @param hostSockaddr the BSD socket structure used to identify the host. This + * specifies the interface that the connection will be bound to. + * @param connectionId the identifier of the new connection. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceCreateConnection(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + CFStringRef portalAddress, + CFStringRef portalPort, + CFStringRef hostInterface, + const struct sockaddr_storage * remoteAddress, + const struct sockaddr_storage * localAddress, + ConnectionIdentifier * connectionId) +{ + // Check parameters + if(!interface || sessionId == kiSCSIInvalidSessionId || !portalAddress || !portalPort || !hostInterface || !remoteAddress || !connectionId) + return kIOReturnBadArgument; + + // Pack the input parameters into a single buffer to send to the kernel + const int kNumParams = 5; + + void * params[kNumParams]; + size_t paramSize[kNumParams]; + + // Add one for string lengths to copy the NULL character (CFGetStringLength + // does not include the length of the NULL terminator) + paramSize[0] = CFStringGetLength(portalAddress) + 1; + paramSize[1] = CFStringGetLength(portalPort) + 1; + paramSize[2] = CFStringGetLength(hostInterface) + 1; + paramSize[3] = sizeof(struct sockaddr_storage); + paramSize[4] = sizeof(struct sockaddr_storage); + + params[0] = malloc(paramSize[0]); + params[1] = malloc(paramSize[1]); + params[2] = malloc(paramSize[2]); + params[3] = (void*)remoteAddress; + params[4] = (void*)localAddress; + + CFStringGetCString(portalAddress,params[0],paramSize[0],kCFStringEncodingASCII); + CFStringGetCString(portalPort,params[1],paramSize[1],kCFStringEncodingASCII); + CFStringGetCString(hostInterface,params[2],paramSize[2],kCFStringEncodingASCII); + + // The input buffer will first have eight bytes to denote the length of + // the portion that follows. So for each of the six input parameters, + // we'll have six UInt64 blocks that indicate the size up front. + size_t header = kNumParams*sizeof(UInt64); + size_t inputStructSize = header; + + CFIndex paramIdx = 0; + while(paramIdx < kNumParams) { + inputStructSize += paramSize[paramIdx]; + paramIdx++; + } + + UInt8 * inputStruct = (UInt8*)malloc(inputStructSize); + UInt8 * inputStructPos = inputStruct + header; + + paramIdx = 0; + while(paramIdx < kNumParams) { + memcpy(inputStructPos,params[paramIdx],paramSize[paramIdx]); + inputStructPos += paramSize[paramIdx]; + + UInt64 * header = (UInt64*)(inputStruct + sizeof(UInt64)*paramIdx); + *header = paramSize[paramIdx]; + paramIdx++; + } + + // Tell the kernel to drop this session and all of its related resources + const UInt32 inputCnt = 2; + const UInt64 inputs[] = {sessionId,kNumParams}; + + const UInt32 expOutputCnt = 2; + UInt64 output[expOutputCnt]; + UInt32 outputCnt = expOutputCnt; + + kern_return_t result = + IOConnectCallMethod(interface->connect,kiSCSICreateConnection,inputs,inputCnt,inputStruct, + inputStructSize,output,&outputCnt,0,0); + + // Free memory + free(params[0]); + free(params[1]); + free(params[2]); + + if(result == kIOReturnSuccess && outputCnt == expOutputCnt) { + *connectionId = (UInt32)output[0]; + IOReturn error = (IOReturn)output[1]; + + return error; + } + + return result; +} + +/*! Frees a given iSCSI connection associated with a given session. + * The session should be logged out using the appropriate PDUs. + * @param interface an instance of an iSCSIHBAInterface. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceReleaseConnection(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId) +{ + // Check parameters + if(!interface || sessionId == kiSCSIInvalidSessionId || connectionId == kiSCSIInvalidConnectionId) + return kIOReturnBadArgument; + + // Tell kernel to drop this connection + const UInt32 inputCnt = 2; + UInt64 inputs[] = {sessionId,connectionId}; + + return IOConnectCallScalarMethod(interface->connect,kiSCSIReleaseConnection,inputs,inputCnt,0,0); +} + +/*! Sends data over a kernel socket associated with iSCSI. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId the qualifier part of the ISID (see RFC3720). + * @param connectionId the connection associated with the session. + * @param bhs the basic header segment to send over the connection. + * @param data the data segment of the PDU to send over the connection. + * @param length the length of the data block to send over the connection. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceSend(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId, + iSCSIPDUInitiatorBHS * bhs, + void * data, + size_t length) +{ + // Check parameters + if(!interface || sessionId == kiSCSIInvalidSessionId || connectionId == kiSCSIInvalidConnectionId || !bhs || (!data && length > 0)) + return kIOReturnBadArgument; + + // Setup input scalar array + const UInt32 inputCnt = 2; + const UInt64 inputs[] = {sessionId, connectionId}; + + // Call kernel method to send (buffer) bhs and then data + kern_return_t result; + result = IOConnectCallStructMethod(interface->connect,kiSCSISendBHS,bhs, + sizeof(iSCSIPDUInitiatorBHS),NULL,NULL); + + if(result != kIOReturnSuccess) + return result; + + return IOConnectCallMethod(interface->connect,kiSCSISendData,inputs,inputCnt, + data,length,NULL,NULL,NULL,NULL); +} + +/*! Receives data over a kernel socket associated with iSCSI. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId the qualifier part of the ISID (see RFC3720). + * @param connectionId the connection associated with the session. + * @param bhs the basic header segment received over the connection. + * @param data the data segment of the PDU received over the connection. + * @param length the length of the data block received. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceReceive(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId, + iSCSIPDUTargetBHS * bhs, + void ** data, + size_t * length) +{ + // Check parameters + if(!interface || sessionId == kiSCSIInvalidSessionId || connectionId == kiSCSIInvalidConnectionId || !bhs) + return kIOReturnBadArgument; + + // Setup input scalar array + const UInt32 inputCnt = 2; + UInt64 inputs[] = {sessionId,connectionId}; + + size_t bhsLength = sizeof(iSCSIPDUTargetBHS); + + // Call kernel method to determine how much data there is to receive + // The inputs are the sesssion qualifier and connection ID + // The output is the size of the buffer we need to allocate to hold the data + kern_return_t result; + result = IOConnectCallMethod(interface->connect,kiSCSIRecvBHS,inputs,inputCnt,NULL,0, + NULL,NULL,bhs,&bhsLength); + + if(result != kIOReturnSuccess) + return result; + + // Determine how much data to allocate for the data buffer + *length = iSCSIPDUGetDataSegmentLength((iSCSIPDUCommonBHS *)bhs); + + // If no data, were done at this point + if(*length == 0) + return 0; + + *data = iSCSIPDUDataCreate(*length); + + if(*data == NULL) + return kIOReturnIOError; + + // Call kernel method to get data from a receive buffer + result = IOConnectCallMethod(interface->connect,kiSCSIRecvData,inputs,inputCnt,NULL,0, + NULL,NULL,*data,length); + + // If we failed, free the temporary buffer and quit with error + if(result != kIOReturnSuccess) + iSCSIPDUDataRelease(data); + + return result; +} + +/*! Sets parameter associated with a particular connection. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId the qualifier part of the ISID (see RFC3720). + * @param connectionId the connection associated with the session. + * @param parameter the parameter to set. + * @param paramVal the value for the specified parameter. + * @param paramSize the size, in bytes of paramVal. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceSetConnectionParameter(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId, + enum iSCSIHBAConnectionParameters parameter, + void * paramVal, + size_t paramSize) +{ + // Check parameters + if(!interface || sessionId == kiSCSIInvalidSessionId || connectionId == kiSCSIInvalidConnectionId || !paramVal || paramSize == 0) + return kIOReturnBadArgument; + + UInt64 paramValCopy = 0; + memcpy(¶mValCopy,paramVal,paramSize); + + const UInt32 inputCnt = 4; + const UInt64 inputs[] = {sessionId,connectionId,parameter,paramValCopy}; + + return IOConnectCallScalarMethod(interface->connect,kiSCSISetConnectionParameter,inputs,inputCnt,0,0); +} + +/*! Gets parameter associated with a particular connection. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId the qualifier part of the ISID (see RFC3720). + * @param connectionId the connection associated with the session. + * @param parameter the parameter to get. + * @param paramVal the returned value for the specified parameter. + * @param paramSize the size, in bytes of paramVal. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceGetConnectionParameter(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId, + enum iSCSIHBAConnectionParameters parameter, + void * paramVal, + size_t paramSize) +{ + // Check parameters + if(!interface || sessionId == kiSCSIInvalidSessionId || connectionId == kiSCSIInvalidConnectionId || !paramVal || paramSize == 0) + return kIOReturnBadArgument; + + const UInt32 inputCnt = 3; + const UInt64 input[] = {sessionId,connectionId,parameter}; + + UInt32 outputCnt = 1; + UInt64 output; + + kern_return_t error = IOConnectCallScalarMethod(interface->connect,kiSCSIGetConnectionParameter, + input,inputCnt,&output,&outputCnt); + + if(error == kIOReturnSuccess) + memcpy(paramVal,&output,paramSize); + + return error; +} + +/*! Activates an iSCSI connection associated with a session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId session associated with connection to activate. + * @param connectionId connection to activate. + * @return error code inidicating result of operation. */ +IOReturn iSCSIHBAInterfaceActivateConnection(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId) +{ + // Check parameters + if(!interface || sessionId == kiSCSIInvalidSessionId || connectionId == kiSCSIInvalidConnectionId) + return kIOReturnBadArgument; + + // Tell kernel to drop this connection + const UInt32 inputCnt = 2; + UInt64 inputs[] = {sessionId,connectionId}; + + return IOConnectCallScalarMethod(interface->connect,kiSCSIActivateConnection, + inputs,inputCnt,NULL,NULL); +} + +/*! Activates all iSCSI connections associated with a session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId session associated with connection to activate. + * @return error code inidicating result of operation. */ +IOReturn iSCSIHBAInterfaceActivateAllConnections(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId) +{ + // Check parameters + if(!interface || sessionId == kiSCSIInvalidSessionId) + return kIOReturnBadArgument; + + const UInt32 inputCnt = 1; + UInt64 input = sessionId; + + return IOConnectCallScalarMethod(interface->connect,kiSCSIActivateAllConnections, + &input,inputCnt,NULL,NULL); +} + +/*! Dectivates an iSCSI connection associated with a session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId session associated with connection to deactivate. + * @param connectionId connection to deactivate. + * @return error code inidicating result of operation. */ +IOReturn iSCSIHBAInterfaceDeactivateConnection(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId) +{ + // Check parameters + if(!interface || sessionId == kiSCSIInvalidSessionId || connectionId == kiSCSIInvalidConnectionId) + return kIOReturnBadArgument; + + // Tell kernel to drop this connection + const UInt32 inputCnt = 2; + UInt64 inputs[] = {sessionId,connectionId}; + + return IOConnectCallScalarMethod(interface->connect,kiSCSIDeactivateConnection, + inputs,inputCnt,NULL,NULL); +} + +/*! Dectivates all iSCSI sessions associated with a session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId session associated with connections to deactivate. + * @return error code inidicating result of operation. */ +IOReturn iSCSIHBAInterfaceDeactivateAllConnections(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId) +{ + // Check parameters + if(!interface || sessionId == kiSCSIInvalidSessionId) + return kIOReturnBadArgument; + + const UInt32 inputCnt = 1; + UInt64 input = sessionId; + + return IOConnectCallScalarMethod(interface->connect,kiSCSIDeactivateAllConnections, + &input,inputCnt,NULL,NULL); +} + +/*! Gets the first connection (the lowest connectionId) for the + * specified session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId obtain an connectionId for this session. + * @param connectionId the identifier of the connection. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceGetConnection(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier * connectionId) +{ + // Check parameters + if(!interface || sessionId == kiSCSIInvalidSessionId || !connectionId) + return kIOReturnBadArgument; + + const UInt32 inputCnt = 1; + UInt64 input = sessionId; + + const UInt32 expOutputCnt = 1; + UInt64 output[expOutputCnt]; + UInt32 outputCnt = expOutputCnt; + + kern_return_t result = + IOConnectCallScalarMethod(interface->connect,kiSCSIGetConnection,&input,inputCnt, + output,&outputCnt); + + if(result == kIOReturnSuccess && outputCnt == expOutputCnt) + *connectionId = (UInt32)output[0]; + + return result; +} + +/*! Gets the connection count for the specified session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId obtain the connection count for this session. + * @param numConnections the connection count. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceGetNumConnections(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + UInt32 * numConnections) +{ + // Check parameters + if(!interface || sessionId == kiSCSIInvalidSessionId || !numConnections) + return kIOReturnBadArgument; + + const UInt32 inputCnt = 1; + UInt64 input = sessionId; + + const UInt32 expOutputCnt = 1; + UInt64 output[expOutputCnt]; + UInt32 outputCnt = expOutputCnt; + + kern_return_t result = IOConnectCallScalarMethod( + interface->connect,kiSCSIGetNumConnections,&input,inputCnt,output,&outputCnt); + + if(result == kIOReturnSuccess && outputCnt == expOutputCnt) + *numConnections = (UInt32)output[0]; + + return result; +} + +/*! Looks up the session identifier associated with a particular target name. + * @param interface an instance of an iSCSIHBAInterface. + * @param targetIQN the IQN name of the target (e.q., iqn.2015-01.com.example) + * @return sessionId the session identifier. */ +SessionIdentifier iSCSIHBAInterfaceGetSessionIdForTargetIQN(iSCSIHBAInterfaceRef interface, + CFStringRef targetIQN) +{ + if(!interface || !targetIQN) + return kiSCSIInvalidSessionId; + + const UInt32 expOutputCnt = 1; + UInt64 output[expOutputCnt]; + UInt32 outputCnt = expOutputCnt; + + const int targetIQNBufferSize = (int)CFStringGetLength(targetIQN)+1; + char * targetIQNBuffer = (char *)malloc(targetIQNBufferSize); + if(!CFStringGetCString(targetIQN,targetIQNBuffer,targetIQNBufferSize,kCFStringEncodingASCII)) + { + free(targetIQNBuffer); + return kiSCSIInvalidSessionId; + } + + kern_return_t result = IOConnectCallMethod( + interface->connect, + kiSCSIGetSessionIdForTargetIQN,0,0, + targetIQNBuffer, + targetIQNBufferSize, + output,&outputCnt,0,0); + + free(targetIQNBuffer); + + if(result == kIOReturnSuccess && outputCnt == expOutputCnt) + return (SessionIdentifier)output[0]; + + return kiSCSIInvalidSessionId; +} + +/*! Looks up the connection identifier associated with a particular portal address. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId the session identifier. + * @param portalAddress the address passed to iSCSIHBAInterfaceCreateSession() or + * iSCSIHBAInterfaceCreateConnection() when the connection was created. + * @return the associated connection identifier. */ +ConnectionIdentifier iSCSIHBAInterfaceGetConnectionIdForPortalAddress(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + CFStringRef portalAddress) +{ + if(!interface || sessionId == kiSCSIInvalidSessionId || !portalAddress) + return kIOReturnBadArgument; + + const UInt32 inputCnt = 1; + UInt64 input = sessionId; + + const UInt32 expOutputCnt = 1; + UInt64 output[expOutputCnt]; + UInt32 outputCnt = expOutputCnt; + + const int portalAddressBufferSize = (int)CFStringGetLength(portalAddress)+1; + char * portalAddressBuffer = (char*)malloc(portalAddressBufferSize); + if(!CFStringGetCString(portalAddress,portalAddressBuffer,portalAddressBufferSize,kCFStringEncodingASCII)) + { + free(portalAddressBuffer); + return kIOReturnBadArgument; + } + + kern_return_t result = + IOConnectCallMethod(interface->connect,kiSCSIGetConnectionIdForPortalAddress, + &input,inputCnt, + portalAddressBuffer, + portalAddressBufferSize, + output,&outputCnt,0,0); + + free(portalAddressBuffer); + + if(result != kIOReturnSuccess || outputCnt != expOutputCnt) + return kiSCSIInvalidConnectionId; + + return (ConnectionIdentifier)output[0]; +} + +/*! Gets an array of session identifiers for each session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionIds an array of session identifiers. This MUST be large + * enough to hold the maximum number of sessions (kiSCSIMaxSessions). + * @param sessionCount number of session identifiers. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceGetSessionIds(iSCSIHBAInterfaceRef interface, + SessionIdentifier * sessionIds, + UInt16 * sessionCount) +{ + if(!interface || !sessionIds || !sessionCount) + return kIOReturnBadArgument; + + const UInt32 expOutputCnt = 1; + UInt64 output; + UInt32 outputCnt = expOutputCnt; + + *sessionCount = 0; + size_t outputStructSize = sizeof(SessionIdentifier)*kiSCSIMaxSessions; + + kern_return_t result = + IOConnectCallMethod(interface->connect,kiSCSIGetSessionIds,0,0,0,0, + &output,&outputCnt,sessionIds,&outputStructSize); + + if(result == kIOReturnSuccess && outputCnt == expOutputCnt) + *sessionCount = (UInt16)output; + + return result; +} + +/*! Gets an array of connection identifiers for each session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId session identifier. + * @param connectionIds an array of connection identifiers for the session. + * @param connectionCount number of connection identifiers. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceGetConnectionIds(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier * connectionIds, + UInt32 * connectionCount) +{ + if(!interface || sessionId == kiSCSIInvalidSessionId || !connectionIds || !connectionCount) + return kIOReturnBadArgument; + + const UInt32 inputCnt = 1; + UInt64 input = sessionId; + + const UInt32 expOutputCnt = 1; + UInt64 output; + UInt32 outputCnt = expOutputCnt; + + *connectionCount = 0; + size_t outputStructSize = sizeof(ConnectionIdentifier)*kiSCSIMaxConnectionsPerSession; + + kern_return_t result = + IOConnectCallMethod(interface->connect,kiSCSIGetConnectionIds,&input,inputCnt,0,0, + &output,&outputCnt,connectionIds,&outputStructSize); + + if(result == kIOReturnSuccess && outputCnt == expOutputCnt) + *connectionCount = (UInt32)output; + + return result; +} + +/*! Creates a string containing the target IQN associated with a session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId session identifier. + * @param targetIQN the name of the target. + * @param size the size of the targetIQNCString buffer. + * @return error code indicating result of operation. */ +CFStringRef iSCSIHBAInterfaceCreateTargetIQNForSessionId(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId) +{ + if(!interface || sessionId == kiSCSIInvalidSessionId) + return NULL; + + const UInt32 inputCnt = 1; + UInt64 input = sessionId; + + const char targetIQN[NI_MAXHOST]; + size_t targetIQNLength = NI_MAXHOST; + + kern_return_t result = IOConnectCallMethod(interface->connect,kiSCSICreateTargetIQNForSessionId, + &input,inputCnt,0,0,0,0, + (void*)targetIQN,&targetIQNLength); + if(result != kIOReturnSuccess) + return NULL; + + return CFStringCreateWithCString(kCFAllocatorDefault,targetIQN,kCFStringEncodingASCII); +} + +/*! Creates a string containing the address of the portal associated with + * the specified connection. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId session identifier. + * @param connectionId connection identifier. + * @return a string containing the portal address, or NULL if the session or + * connection was invalid. */ +CFStringRef iSCSIHBAInterfaceCreatePortalAddressForConnectionId(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId) +{ + if(!interface || sessionId == kiSCSIInvalidSessionId || connectionId == kiSCSIInvalidConnectionId) + return NULL; + + const UInt32 inputCnt = 2; + UInt64 input[] = {sessionId,connectionId}; + + const char portalAddress[NI_MAXHOST]; + size_t portalAddressLength = NI_MAXHOST; + + kern_return_t result = IOConnectCallMethod(interface->connect,kiSCSIGetPortalAddressForConnectionId, + input,inputCnt,0,0,0,0, + (void *)portalAddress,&portalAddressLength); + if(result != kIOReturnSuccess) + return NULL; + + return CFStringCreateWithCString(kCFAllocatorDefault,portalAddress,kCFStringEncodingASCII); +} + +/*! Creates a string containing the TCP port of the portal associated with + * the specified connection. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId session identifier. + * @param connectionId connection identifier. + * @return a string containing the TCP port of the portal, or NULL if the + * session or connection was invalid. */ +CFStringRef iSCSIHBAInterfaceCreatePortalPortForConnectionId(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId) +{ + if(!interface || sessionId == kiSCSIInvalidSessionId || connectionId == kiSCSIInvalidConnectionId) + return NULL; + + const UInt32 inputCnt = 2; + UInt64 input[] = {sessionId,connectionId}; + + const char portalPort[NI_MAXSERV]; + size_t portalPortLength = NI_MAXSERV; + + kern_return_t result = IOConnectCallMethod(interface->connect,kiSCSIGetPortalPortForConnectionId, + input,inputCnt,0,0,0,0, + (void *)portalPort,&portalPortLength); + if(result != kIOReturnSuccess) + return NULL; + + return CFStringCreateWithCString(kCFAllocatorDefault,portalPort,kCFStringEncodingASCII); +} + +/*! Creates a string containing the host interface used for the connection. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId session identifier. + * @param connectionId connection identifier. + * @return a string containing the host interface name, or NULL if the + * session or connection was invalid. */ +CFStringRef iSCSIHBAInterfaceCreateHostInterfaceForConnectionId(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId) +{ + if(!interface || sessionId == kiSCSIInvalidSessionId || connectionId == kiSCSIInvalidConnectionId) + return NULL; + + const UInt32 inputCnt = 2; + UInt64 input[] = {sessionId,connectionId}; + + + const char hostInterface[NI_MAXHOST]; + size_t hostInterfaceLength = NI_MAXHOST; + + kern_return_t result = IOConnectCallMethod(interface->connect,kiSCSIGetHostInterfaceForConnectionId, + input,inputCnt,0,0,0,0, + (void *)hostInterface,&hostInterfaceLength); + if(result != kIOReturnSuccess) + return NULL; + + return CFStringCreateWithCString(kCFAllocatorDefault,hostInterface,kCFStringEncodingASCII); +} \ No newline at end of file diff --git a/Source/User/iscsid/iSCSIHBAInterface.h b/Source/User/iscsid/iSCSIHBAInterface.h new file mode 100644 index 00000000..13cf8d3c --- /dev/null +++ b/Source/User/iscsid/iSCSIHBAInterface.h @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2016, Nareg Sinenian + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INConnectionIdentifierENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __ISCSI_HBA_INTERFACE_H__ +#define __ISCSI_HBA_INTERFACE_H__ + +#include "iSCSIHBATypes.h" +#include "iSCSITypesShared.h" +#include "iSCSIPDUShared.h" +#include +#include +#include +#include +#include + +#include +#include + + +typedef struct __iSCSIHBAInterface * iSCSIHBAInterfaceRef; + +/*! Notification context that is used when creating a new HBA + * instance. This struct is used to pass user-defined data + * when the iSCSIHBANotification callback functions are called. */ +typedef struct __iSCSIHBANotificationContext { + + /*! Version of this struct (set to 0). */ + CFIndex version; + + /*! User-defined data. */ + void * info; + + /*! Retain callback function (may be NULL). */ + CFAllocatorRetainCallBack retain; + + /*! Release callback function (may be NULL). */ + CFAllocatorReleaseCallBack release; + + /*! Copy description callback function (may be NULL). */ + CFAllocatorCopyDescriptionCallBack copyDescription; + +} iSCSIHBANotificationContext; + + +/*! Callback function used to relay notifications from the kernel. */ +typedef void (*iSCSIHBANotificationCallBack)(iSCSIHBAInterfaceRef interface, + enum iSCSIHBANotificationTypes type, + iSCSIHBANotificationMessage * msg, + void * info); + +/*! Opens a connection to the iSCSI initiator. A connection must be + * successfully opened before any of the supporting functions below can be + * called. A callback function is used to process notifications from the + * iSCSI kernel extension. + * @param allocator the allocator to use. + * @param callback the callback function to process notifications. + * @return a new iSCSI HBA interface or NULL if one could not be created. */ +iSCSIHBAInterfaceRef iSCSIHBAInterfaceCreate(CFAllocatorRef allocator, + iSCSIHBANotificationCallBack callback, + iSCSIHBANotificationContext * context); + +/*! Closes a connection to the iSCSI initiator. + * @return error code indicating the result of the operation. */ +void iSCSIHBAInterfaceRelease(iSCSIHBAInterfaceRef interface); + +/*! Schedules execution of various tasks, including handling of kernel notifications + * on for the specified interface instance over the designated runloop. + * @param interface an instance of an iSCSIHBAInterface. + * @param runLoop the runloop to schedule + * @param runLoopMode the execution mode for the runLoop. */ +void iSCSIHBAInterfaceScheduleWithRunloop(iSCSIHBAInterfaceRef interface, + CFRunLoopRef runLoop, + CFStringRef runLoopMode); + +/*! Unschedules execution of various tasks, including handling of kernel notifications + * on for the specified interface instance over the designated runloop. + * @param interface an instance of an iSCSIHBAInterface. + * @param runLoop the runloop to schedule + * @param runLoopMode the execution mode for the runLoop. */ +void iSCSIHBAInterfaceUnscheduleWithRunloop(iSCSIHBAInterfaceRef interface, + CFRunLoopRef runLoop, + CFStringRef runLoopMode); + +/*! Allocates a new iSCSI session in the kernel and creates an associated + * connection to the target portal. Additional connections may be added to the + * session by calling iSCSIHBAInterfaceCreateConnection(). + * @param interface an instance of an iSCSIHBAInterface. + * @param targetIQN the name of the target. + * @param portalAddress the portal address (IPv4/IPv6, or DNS name). + * @param portalPort the TCP port used to connect to the portal. + * @param hostInterface the name of the host interface adapter to use. + * @param portalSockaddr the BSD socket structure used to identify the target. + * @param hostSockaddr the BSD socket structure used to identify the host. This + * specifies the interface that the connection will be bound to. + * @param sessionId the session identifier for the new session (returned). + * @param connectionId the identifier of the new connection (returned). + * @return error code indicating the result of the operation. */ +IOReturn iSCSIHBAInterfaceCreateSession(iSCSIHBAInterfaceRef interface, + CFStringRef targetIQN, + CFStringRef portalAddress, + CFStringRef portalPort, + CFStringRef hostInterface, + const struct sockaddr_storage * remoteAddress, + const struct sockaddr_storage * localAddress, + SessionIdentifier * sessionId, + ConnectionIdentifier * connectionId); + +/*! Releases an iSCSI session, including all connections associated with that + * session (there is no requirement to release connections individually). + * @param sessionId the session qualifier part of the ISID. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceReleaseSession(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId); + +/*! Sets parameter associated with a particular session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId the qualifier part of the ISID (see RFC3720). + * @param parameter the parameter to set. + * @param paramVal the value for the specified parameter. + * @param paramSize the size, in bytes of paramVal. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceSetSessionParameter(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + enum iSCSIHBASessionParameters parameter, + void * paramVal, + size_t paramSize); + +/*! Gets parameter associated with a particular session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId the qualifier part of the ISID (see RFC3720). + * @param parameter the parameter to get. + * @param paramVal the returned value for the specified parameter. + * @param paramSize the size, in bytes of paramVal. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceGetSessionParameter(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + enum iSCSIHBASessionParameters parameter, + void * paramVal, + size_t paramSize); + +/*! Allocates an additional iSCSI connection for a particular session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId the qualifier part of the ISID (see RFC3720). + * @param portalAddress the portal address (IPv4/IPv6, or DNS name). + * @param portalPort the TCP port used to connect to the portal. + * @param hostInterface the name of the host interface adapter to use. + * @param portalSockaddr the BSD socket structure used to identify the target. + * @param hostSockaddr the BSD socket structure used to identify the host. This + * specifies the interface that the connection will be bound to. + * @param connectionId the identifier of the new connection. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceCreateConnection(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + CFStringRef portalAddress, + CFStringRef portalPort, + CFStringRef hostInterface, + const struct sockaddr_storage * remoteAddress, + const struct sockaddr_storage * localAddress, + ConnectionIdentifier * connectionId); + +/*! Frees a given iSCSI connection associated with a given session. + * The session should be logged out using the appropriate PDUs. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId the qualifier part of the ISID (see RFC3720). */ +IOReturn iSCSIHBAInterfaceReleaseConnection(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId); + +/*! Sends data over a kernel socket associated with iSCSI. The data sent should + * be specified by the buffer pointer to by data, with a length given by + * length. The size of the buffer (length) should include any padding required + * to achieve 4-byte alignment. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId the qualifier part of the ISID (see RFC3720). + * @param connectionId the connection associated with the session. + * @param bhs the basic header segment to send over the connection. + * @param data the data segment of the PDU to send over the connection. + * @param length the length of the data block to send over the connection. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceSend(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId, + iSCSIPDUInitiatorBHS * bhs, + void * data, + size_t length); + +/*! Receives data over a kernel socket associated with iSCSI. Function will + * receive the ENTIRE data segment of a PDU at once, and return the length + * of the buffer in length. This length includes any padding required to + * achieve 4-byte alignement required of PDUs. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId the qualifier part of the ISID (see RFC3720). + * @param connectionId the connection associated with the session. + * @param bhs the basic header segment received over the connection. + * @param data the data segment of the PDU received over the connection. + * @param length the length of the data block received. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceReceive(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId, + iSCSIPDUTargetBHS * bhs, + void * * data, + size_t * length); + +/*! Sets parameter associated with a particular connection. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId the qualifier part of the ISID (see RFC3720). + * @param connectionId the connection associated with the session. + * @param parameter the parameter to set. + * @param paramVal the value for the specified parameter. + * @param paramSize the size, in bytes of paramVal. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceSetConnectionParameter(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId, + enum iSCSIHBAConnectionParameters parameter, + void * paramVal, + size_t paramSize); + +/*! Gets parameter associated with a particular connection. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId the qualifier part of the ISID (see RFC3720). + * @param connectionId the connection associated with the session. + * @param parameter the parameter to get. + * @param paramVal the returned value for the specified parameter. + * @param paramSize the size, in bytes of paramVal. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceGetConnectionParameter(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId, + enum iSCSIHBAConnectionParameters parameter, + void * paramVal, + size_t paramSize); + +/*! Activates an iSCSI connection associated with a session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId session associated with connection to activate. + * @param connectionId connection to activate. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceActivateConnection(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId); + +/*! Activates all iSCSI connections associated with a session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId session associated with connection to activate. + * @return error code inidicating result of operation. */ +IOReturn iSCSIHBAInterfaceActivateAllConnections(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId); + +/*! Dectivates an iSCSI connection associated with a session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId session associated with connection to deactivate. + * @param connectionId connection to deactivate. + * @return error code inidicating result of operation. */ +IOReturn iSCSIHBAInterfaceDeactivateConnection(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId); + +/*! Dectivates all iSCSI sessions associated with a session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId session associated with connections to deactivate. + * @return error code inidicating result of operation. */ +IOReturn iSCSIHBAInterfaceDeactivateAllConnections(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId); + +/*! Gets the first connection (the lowest connectionId) for the + * specified session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId obtain an connectionId for this session. + * @param connectionId the identifier of the connection. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceGetConnection(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier * connectionId); + +/*! Gets the connection count for the specified session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId obtain the connection count for this session. + * @param numConnections the connection count. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceGetNumConnections(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + UInt32 * numConnections); + +/*! Looks up the session identifier associated with a particular target name. + * @param interface an instance of an iSCSIHBAInterface. + * @param targetIQN the IQN name of the target (e.q., iqn.2015-01.com.example) + * @return sessionId the session identifier. */ +SessionIdentifier iSCSIHBAInterfaceGetSessionIdForTargetIQN(iSCSIHBAInterfaceRef interface, + CFStringRef targetIQN); + +/*! Looks up the connection identifier associated with a particular portal address. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId the session identifier. + * @param portalAddress the address passed to iSCSIHBAInterfaceCreateSession() or + * iSCSIHBAInterfaceCreateConnection() when the connection was created. + * @return the associated connection identifier. */ +ConnectionIdentifier iSCSIHBAInterfaceGetConnectionIdForPortalAddress(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + CFStringRef portalAddress); + +/*! Gets an array of session identifiers for each session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionIds an array of session identifiers. + * @param sessionCount number of session identifiers. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceGetSessionIds(iSCSIHBAInterfaceRef interface, + SessionIdentifier * sessionIds, + UInt16 * sessionCount); + +/*! Gets an array of connection identifiers for each session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId session identifier. + * @param connectionIds an array of connection identifiers for the session. + * @param connectionCount number of connection identifiers. + * @return error code indicating result of operation. */ +IOReturn iSCSIHBAInterfaceGetConnectionIds(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier * connectionIds, + UInt32 * connectionCount); + +/*! Creates a string containing the target IQN associated with a session. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId session identifier. + * @param targetIQN the name of the target. + * @param size the size of the targetIQNCString buffer. + * @return error code indicating result of operation. */ +CFStringRef iSCSIHBAInterfaceCreateTargetIQNForSessionId(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId); + +/*! Creates a string containing the address of the portal associated with + * the specified connection. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId session identifier. + * @param connectionId connection identifier. + * @return a string containing the portal address, or NULL if the session or + * connection was invalid. */ +CFStringRef iSCSIHBAInterfaceCreatePortalAddressForConnectionId(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId); + +/*! Creates a string containing the TCP port of the portal associated with + * the specified connection. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId session identifier. + * @param connectionId connection identifier. + * @return a string containing the TCP port of the portal, or NULL if the + * session or connection was invalid. */ +CFStringRef iSCSIHBAInterfaceCreatePortalPortForConnectionId(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId); + +/*! Creates a string containing the host interface used for the connection. + * @param interface an instance of an iSCSIHBAInterface. + * @param sessionId session identifier. + * @param connectionId connection identifier. + * @return a string containing the host interface name, or NULL if the + * session or connection was invalid. */ +CFStringRef iSCSIHBAInterfaceCreateHostInterfaceForConnectionId(iSCSIHBAInterfaceRef interface, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId); + + +#endif /* defined(__ISCSI_HBA_INTERFACE_H__) */ diff --git a/Source/User/iscsid/iSCSIKernelInterface.c b/Source/User/iscsid/iSCSIKernelInterface.c index 1b4624da..f236e9f1 100644 --- a/Source/User/iscsid/iSCSIKernelInterface.c +++ b/Source/User/iscsid/iSCSIKernelInterface.c @@ -34,9 +34,7 @@ #include #include -static io_service_t service; static io_connect_t connection; -static CFMachPortContext notificationContext; static CFMachPortRef notificationPort = NULL; static iSCSIKernelNotificationCallback callback; @@ -101,7 +99,7 @@ errno_t iSCSIKernelInitialize(iSCSIKernelNotificationCallback callback) CFMutableDictionaryRef matchingDict = NULL; matchingDict = IOServiceMatching(kiSCSIVirtualHBA_IOClassName); - service = IOServiceGetMatchingService(kIOMasterPortDefault,matchingDict); + io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault,matchingDict); // Check to see if the driver was found in the I/O registry if(service == IO_OBJECT_NULL) @@ -109,12 +107,14 @@ errno_t iSCSIKernelInitialize(iSCSIKernelNotificationCallback callback) // Using the service handle, open a connection result = IOServiceOpen(service,mach_task_self(),0,&connection); + + // No longer need a reference to the service + IOObjectRelease(service); - if(result != kIOReturnSuccess) { - IOObjectRelease(service); + if(result != kIOReturnSuccess) return kIOReturnNotFound; - } + CFMachPortContext notificationContext; notificationContext.info = (void *)¬ificationContext; notificationContext.version = 0; notificationContext.release = NULL; @@ -138,7 +138,6 @@ errno_t iSCSIKernelCleanup() IOConnectCallScalarMethod(connection,kiSCSICloseInitiator,0,0,0,0); // Clean up (now that we have a connection we no longer need the object) - IOObjectRelease(service); IOServiceClose(connection); if(notificationPort) diff --git a/Source/User/iscsid/iSCSIQueryTarget.c b/Source/User/iscsid/iSCSIQueryTarget.c index da1d6ebd..f348b667 100644 --- a/Source/User/iscsid/iSCSIQueryTarget.c +++ b/Source/User/iscsid/iSCSIQueryTarget.c @@ -27,7 +27,7 @@ */ #include "iSCSIQueryTarget.h" -#include "iSCSIKernelInterface.h" +#include "iSCSIHBAInterface.h" errno_t iSCSISessionLoginSingleQuery(struct iSCSILoginQueryContext * context, enum iSCSILoginStatusCode * statusCode, @@ -52,9 +52,8 @@ errno_t iSCSISessionLoginSingleQuery(struct iSCSILoginQueryContext * context, size_t length = 0; iSCSIPDUDataCreateFromDict(textCmd,&data,&length); - errno_t error = iSCSIKernelSend(context->sessionId,context->connectionId, - (iSCSIPDUInitiatorBHS *)&cmd, - data,length); + errno_t error = iSCSIHBAInterfaceSend(context->interface,context->sessionId,context->connectionId, + (iSCSIPDUInitiatorBHS *)&cmd,data,length); iSCSIPDUDataRelease(&data); if(error) { @@ -65,8 +64,8 @@ errno_t iSCSISessionLoginSingleQuery(struct iSCSILoginQueryContext * context, iSCSIPDULoginRspBHS rsp; do { - if((error = iSCSIKernelRecv(context->sessionId,context->connectionId, - (iSCSIPDUTargetBHS *)&rsp,&data,&length))) + if((error = iSCSIHBAInterfaceReceive(context->interface,context->sessionId,context->connectionId, + (iSCSIPDUTargetBHS *)&rsp,&data,&length))) { iSCSIPDUDataRelease(&data); return error; @@ -84,8 +83,7 @@ errno_t iSCSISessionLoginSingleQuery(struct iSCSILoginQueryContext * context, iSCSIPDUDataParseToDict(data,length,textRsp); // Save & return the TSIH if this is the leading login - if(context->targetSessionId == 0 && - context->nextStage == kiSCSIPDUFullFeaturePhase) { + if(context->targetSessionId == 0 && context->nextStage == kiSCSIPDUFullFeaturePhase) { context->targetSessionId = CFSwapInt16BigToHost(rsp.TSIH); } @@ -94,7 +92,6 @@ errno_t iSCSISessionLoginSingleQuery(struct iSCSILoginQueryContext * context, context->statSN = rsp.statSN; context->expCmdSN = rsp.expCmdSN; context->transitNextStage = (rsp.loginStage & kiSCSIPDULoginTransitFlag); - } // For this case some other kind of PDU or invalid data was received else if(rsp.opCode == kiSCSIPDUOpCodeReject) @@ -135,6 +132,10 @@ errno_t iSCSISessionLoginQuery(struct iSCSILoginQueryContext * context, // Try a single query first errno_t error = iSCSISessionLoginSingleQuery(context,statusCode,rejectCode,textCmd,textRsp); + // If error occured, do nothing + if(error || *statusCode != kiSCSILoginSuccess) + return error; + // If we are not transitioning stages, or we are and the target agreed to // transition, then we can move on... if(context->currentStage == context->nextStage || context->transitNextStage) @@ -176,8 +177,8 @@ errno_t iSCSISessionLoginQuery(struct iSCSILoginQueryContext * context, * @param textCmd a dictionary of key-value pairs to send. * @param textRsp a dictionary of key-value pairs to receive. * @return an error code that indicates the result of the operation. */ -errno_t iSCSISessionTextQuery(SID sessionId, - CID connectionId, +errno_t iSCSISessionTextQuery(SessionIdentifier sessionId, + ConnectionIdentifier connectionId, CFDictionaryRef textCmd, CFMutableDictionaryRef textRsp) { @@ -190,7 +191,7 @@ errno_t iSCSISessionTextQuery(SID sessionId, size_t length; iSCSIPDUDataCreateFromDict(textCmd,&data,&length); - errno_t error = iSCSIKernelSend(sessionId, + errno_t error = iSCSIHBAInterfaceSend(0,sessionId, connectionId, (iSCSIPDUInitiatorBHS *)&cmd, data,length); @@ -204,7 +205,7 @@ errno_t iSCSISessionTextQuery(SID sessionId, iSCSIPDUTextRspBHS rsp; do { - if((error = iSCSIKernelRecv(sessionId,connectionId, + if((error = iSCSIHBAInterfaceReceive(0,sessionId,connectionId, (iSCSIPDUTargetBHS *)&rsp,&data,&length))) { iSCSIPDUDataRelease(&data); diff --git a/Source/User/iscsid/iSCSIQueryTarget.h b/Source/User/iscsid/iSCSIQueryTarget.h index 443aad84..391e80e4 100644 --- a/Source/User/iscsid/iSCSIQueryTarget.h +++ b/Source/User/iscsid/iSCSIQueryTarget.h @@ -31,17 +31,21 @@ #include "iSCSITypes.h" #include "iSCSIPDUUser.h" +#include "iSCSIHBAInterface.h" /*! Used to perform a login query during the login phase of a connection. */ struct iSCSILoginQueryContext { // These inputs are required when calling iSCSISessionLoginQuery() + /*! Reference to the HBA interface. */ + iSCSIHBAInterfaceRef interface; + /*! The session identifier. */ - SID sessionId; + SessionIdentifier sessionId; /*! The connection identifier. */ - CID connectionId; + ConnectionIdentifier connectionId; /*! The current stage of negotiation process. */ enum iSCSIPDULoginStages currentStage; @@ -59,7 +63,7 @@ struct iSCSILoginQueryContext { UInt32 expCmdSN; /*! The target session identifier. */ - TSIH targetSessionId; + TargetSessionIdentifier targetSessionId; /*! Whether the target agrees to advance to next stage. */ bool transitNextStage; @@ -101,8 +105,8 @@ errno_t iSCSISessionLoginQuery(struct iSCSILoginQueryContext * context, * @param textCmd a dictionary of key-value pairs to send. * @param textRsp a dictionary of key-value pairs to receive. * @return an error code that indicates the result of the operation. */ -errno_t iSCSISessionTextQuery(SID sessionId, - CID connectionId, +errno_t iSCSISessionTextQuery(SessionIdentifier sessionId, + ConnectionIdentifier connectionId, CFDictionaryRef textCmd, CFMutableDictionaryRef textRsp); diff --git a/Source/User/iscsid/iSCSISession.c b/Source/User/iscsid/iSCSISession.c index baff3f28..c82bf600 100644 --- a/Source/User/iscsid/iSCSISession.c +++ b/Source/User/iscsid/iSCSISession.c @@ -27,21 +27,13 @@ */ #include "iSCSISession.h" +#include "iSCSISessionManager.h" #include "iSCSIPDUUser.h" -#include "iSCSIKernelInterface.h" -#include "iSCSIKernelInterfaceShared.h" +#include "iSCSIHBAInterface.h" #include "iSCSIAuth.h" #include "iSCSIQueryTarget.h" -#include "iSCSITypes.h" -#include "iSCSIDA.h" -#include "iSCSIIORegistry.h" -#include "iSCSIRFC3720Defaults.h" -/*! Name of the initiator. */ -CFStringRef kiSCSIInitiatorIQN = CFSTR("iqn.2015-01.com.localhost"); - -/*! Alias of the initiator. */ -CFStringRef kiSCSIInitiatorAlias = CFSTR("default"); +#include "iSCSI.h" /*! Maximum number of key-value pairs supported by a dictionary that is used * to produce the data section of text and login PDUs. */ @@ -201,11 +193,14 @@ void iSCSINegotiateBuildSWDictCommon(iSCSISessionConfigRef sessCfg, CFDictionaryAddValue(sessCmd,kRFC3720_Key_ErrorRecoveryLevel,value); } -errno_t iSCSINegotiateParseSWDictCommon(SID sessionId, +errno_t iSCSINegotiateParseSWDictCommon(iSCSISessionManagerRef managerRef, + SessionIdentifier sessionId, CFDictionaryRef sessCmd, CFDictionaryRef sessRsp) { + iSCSIHBAInterfaceRef hbaInterface = iSCSISessionManagerGetHBAInterface(managerRef); + // Holds target value & comparison result for keys that we'll process CFStringRef targetRsp; @@ -220,8 +215,9 @@ errno_t iSCSINegotiateParseSWDictCommon(SID sessionId, return ENOTSUP; defaultTime2Retain = iSCSILVGetMin(initCmd,targetRsp); - iSCSIKernelSetSessionOpt(sessionId,kiSCSIKernelSODefaultTime2Retain, - &defaultTime2Retain,sizeof(defaultTime2Retain)); + iSCSIHBAInterfaceSetSessionParameter(hbaInterface,sessionId, + kiSCSIHBASODefaultTime2Retain, + &defaultTime2Retain,sizeof(defaultTime2Retain)); } else return ENOTSUP; @@ -238,8 +234,9 @@ errno_t iSCSINegotiateParseSWDictCommon(SID sessionId, return ENOTSUP; defaultTime2Wait = iSCSILVGetMin(initCmd,targetRsp);; - iSCSIKernelSetSessionOpt(sessionId,kiSCSIKernelSODefaultTime2Wait, - &defaultTime2Wait,sizeof(defaultTime2Wait)); + iSCSIHBAInterfaceSetSessionParameter(hbaInterface,sessionId, + kiSCSIHBASODefaultTime2Wait, + &defaultTime2Wait,sizeof(defaultTime2Wait)); } else return ENOTSUP; @@ -255,7 +252,7 @@ errno_t iSCSINegotiateParseSWDictCommon(SID sessionId, return ENOTSUP; errorRecoveryLevel = iSCSILVGetMin(initCmd,targetRsp); - iSCSIKernelSetSessionOpt(sessionId,kiSCSIKernelSOErrorRecoveryLevel, + iSCSIHBAInterfaceSetSessionParameter(hbaInterface,sessionId,kiSCSIHBASOErrorRecoveryLevel, &errorRecoveryLevel,sizeof(errorRecoveryLevel)); } else @@ -265,11 +262,14 @@ errno_t iSCSINegotiateParseSWDictCommon(SID sessionId, return 0; } -errno_t iSCSINegotiateParseSWDictNormal(SID sessionId, +errno_t iSCSINegotiateParseSWDictNormal(iSCSISessionManagerRef managerRef, + SessionIdentifier sessionId, CFDictionaryRef sessCmd, CFDictionaryRef sessRsp) { + iSCSIHBAInterfaceRef hbaInterface = iSCSISessionManagerGetHBAInterface(managerRef); + // Holds target value & comparison result for keys that we'll process CFStringRef targetRsp; @@ -284,9 +284,8 @@ errno_t iSCSINegotiateParseSWDictNormal(SID sessionId, return ENOTSUP; maxConnections = iSCSILVGetMin(initCmd,targetRsp); - iSCSIKernelSetSessionOpt(sessionId,kiSCSIKernelSOMaxConnections, - &maxConnections,sizeof(maxConnections)); - + iSCSIHBAInterfaceSetSessionParameter(hbaInterface,sessionId,kiSCSIHBASOMaxConnections, + &maxConnections,sizeof(maxConnections)); } // Grab the OR for initialR2T command and response @@ -294,8 +293,8 @@ errno_t iSCSINegotiateParseSWDictNormal(SID sessionId, { CFStringRef initCmd = CFDictionaryGetValue(sessCmd,kRFC3720_Key_InitialR2T); Boolean initialR2T = iSCSILVGetOr(initCmd,targetRsp); - iSCSIKernelSetSessionOpt(sessionId,kiSCSIKernelSOInitialR2T, - &initialR2T,sizeof(initialR2T)); + iSCSIHBAInterfaceSetSessionParameter(hbaInterface,sessionId,kiSCSIHBASOInitialR2T, + &initialR2T,sizeof(initialR2T)); } // Grab the AND for immediate data command and response @@ -303,8 +302,8 @@ errno_t iSCSINegotiateParseSWDictNormal(SID sessionId, { CFStringRef initCmd = CFDictionaryGetValue(sessCmd,kRFC3720_Key_ImmediateData); Boolean immediateData = iSCSILVGetAnd(initCmd,targetRsp); - iSCSIKernelSetSessionOpt(sessionId,kiSCSIKernelSOImmediateData, - &immediateData,sizeof(immediateData)); + iSCSIHBAInterfaceSetSessionParameter(hbaInterface,sessionId,kiSCSIHBASOImmediateData, + &immediateData,sizeof(immediateData)); } // Get the OR of data PDU in order @@ -312,7 +311,7 @@ errno_t iSCSINegotiateParseSWDictNormal(SID sessionId, { CFStringRef initCmd = CFDictionaryGetValue(sessCmd,kRFC3720_Key_DataPDUInOrder); Boolean dataPDUInOrder = iSCSILVGetAnd(initCmd,targetRsp); - iSCSIKernelSetSessionOpt(sessionId,kiSCSIKernelSODataPDUInOrder, + iSCSIHBAInterfaceSetSessionParameter(hbaInterface,sessionId,kiSCSIHBASODataPDUInOrder, &dataPDUInOrder,sizeof(dataPDUInOrder)); } @@ -321,7 +320,7 @@ errno_t iSCSINegotiateParseSWDictNormal(SID sessionId, { CFStringRef initCmd = CFDictionaryGetValue(sessCmd,kRFC3720_Key_DataSequenceInOrder); Boolean dataSequenceInOrder = iSCSILVGetAnd(initCmd,targetRsp); - iSCSIKernelSetSessionOpt(sessionId,kiSCSIKernelSODataSequenceInOrder, + iSCSIHBAInterfaceSetSessionParameter(hbaInterface,sessionId,kiSCSIHBASODataSequenceInOrder, &dataSequenceInOrder,sizeof(dataSequenceInOrder)); } @@ -330,7 +329,7 @@ errno_t iSCSINegotiateParseSWDictNormal(SID sessionId, { CFStringRef initCmd = CFDictionaryGetValue(sessCmd,kRFC3720_Key_MaxBurstLength); UInt32 maxBurstLength = iSCSILVGetMin(initCmd,targetRsp); - iSCSIKernelSetSessionOpt(sessionId,kiSCSIKernelSOMaxBurstLength, + iSCSIHBAInterfaceSetSessionParameter(hbaInterface,sessionId,kiSCSIHBASOMaxBurstLength, &maxBurstLength,sizeof(maxBurstLength)); } @@ -345,7 +344,7 @@ errno_t iSCSINegotiateParseSWDictNormal(SID sessionId, return ENOTSUP; firstBurstLength = iSCSILVGetMin(initCmd,targetRsp); - iSCSIKernelSetSessionOpt(sessionId,kiSCSIKernelSOFirstBurstLength, + iSCSIHBAInterfaceSetSessionParameter(hbaInterface,sessionId,kiSCSIHBASOFirstBurstLength, &firstBurstLength,sizeof(firstBurstLength)); } @@ -360,7 +359,7 @@ errno_t iSCSINegotiateParseSWDictNormal(SID sessionId, return ENOTSUP; maxOutStandingR2T = iSCSILVGetMin(initCmd,targetRsp); - iSCSIKernelSetSessionOpt(sessionId,kiSCSIKernelSOMaxOutstandingR2T, + iSCSIHBAInterfaceSetSessionParameter(hbaInterface,sessionId,kiSCSIHBASOMaxOutstandingR2T, &maxOutStandingR2T,sizeof(maxOutStandingR2T)); } @@ -407,12 +406,15 @@ void iSCSINegotiateBuildCWDict(iSCSIConnectionConfigRef connCfg, * @param connCfgKernel a connection options object used to store options with * the iSCSI kernel extension * there were received from the target. */ -errno_t iSCSINegotiateParseCWDict(SID sessionId, - CID connectionId, +errno_t iSCSINegotiateParseCWDict(iSCSISessionManagerRef managerRef, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId, CFDictionaryRef connCmd, CFDictionaryRef connRsp) { + iSCSIHBAInterfaceRef hbaInterface = iSCSISessionManagerGetHBAInterface(managerRef); + // Holds target value & comparison result for keys that we'll process CFStringRef targetRsp; @@ -427,9 +429,9 @@ errno_t iSCSINegotiateParseCWDict(SID sessionId, Boolean useDataDigest = agree && iSCSILVGetEqual(targetRsp,kRFC3720_Value_DataDigestCRC32C); // Store value - iSCSIKernelSetConnectionOpt(sessionId,connectionId,kiSCSIKernelCOUseDataDigest, - &useDataDigest,sizeof(useDataDigest)); - + iSCSIHBAInterfaceSetConnectionParameter(hbaInterface,sessionId,connectionId, + kiSCSIHBACOUseDataDigest, + &useDataDigest,sizeof(useDataDigest)); // Reset agreement flag agree = false; @@ -441,17 +443,17 @@ errno_t iSCSINegotiateParseCWDict(SID sessionId, Boolean useHeaderDigest = agree && iSCSILVGetEqual(targetRsp,kRFC3720_Value_HeaderDigestCRC32C); // Store value - iSCSIKernelSetConnectionOpt(sessionId,connectionId,kiSCSIKernelCOUseHeaderDigest, - &useHeaderDigest,sizeof(useHeaderDigest)); - + iSCSIHBAInterfaceSetConnectionParameter(hbaInterface,sessionId,connectionId, + kiSCSIHBACOUseHeaderDigest, + &useHeaderDigest,sizeof(useHeaderDigest)); // This option is declarative; we sent the default length, and the target // must accept our choice as it is within a valid range UInt32 maxRecvDataSegmentLength = kRFC3720_MaxRecvDataSegmentLength; - iSCSIKernelSetConnectionOpt(sessionId,connectionId,kiSCSIKernelCOMaxRecvDataSegmentLength, - &maxRecvDataSegmentLength,sizeof(maxRecvDataSegmentLength)); - + iSCSIHBAInterfaceSetConnectionParameter(hbaInterface,sessionId,connectionId, + kiSCSIHBACOMaxRecvDataSegmentLength, + &maxRecvDataSegmentLength,sizeof(maxRecvDataSegmentLength)); // This is the declaration made by the target as to the length it can // receive. Accept the value if it is within the RFC3720 allowed range @@ -470,18 +472,22 @@ errno_t iSCSINegotiateParseCWDict(SID sessionId, } // Store value - iSCSIKernelSetConnectionOpt(sessionId,connectionId,kiSCSIKernelCOMaxSendDataSegmentLength, - &maxSendDataSegmentLength,sizeof(maxSendDataSegmentLength)); + iSCSIHBAInterfaceSetConnectionParameter(hbaInterface,sessionId,connectionId, + kiSCSIHBACOMaxSendDataSegmentLength, + &maxSendDataSegmentLength,sizeof(maxSendDataSegmentLength)); return 0; } -errno_t iSCSINegotiateSession(iSCSIMutableTargetRef target, - SID sessionId, - CID connectionId, +errno_t iSCSINegotiateSession(iSCSISessionManagerRef managerRef, + iSCSIMutableTargetRef target, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId, iSCSISessionConfigRef sessCfg, iSCSIConnectionConfigRef connCfg, enum iSCSILoginStatusCode * statusCode) { + iSCSIHBAInterfaceRef hbaInterface = iSCSISessionManagerGetHBAInterface(managerRef); + // Create a new dictionary for connection parameters we want to send CFMutableDictionaryRef sessCmd = CFDictionaryCreateMutable( kCFAllocatorDefault, @@ -507,6 +513,7 @@ errno_t iSCSINegotiateSession(iSCSIMutableTargetRef target, &kCFTypeDictionaryValueCallBacks); struct iSCSILoginQueryContext context; + context.interface = hbaInterface; context.sessionId = sessionId; context.connectionId = connectionId; context.currentStage = kiSCSIPDULoginOperationalNegotiation; @@ -526,16 +533,17 @@ errno_t iSCSINegotiateSession(iSCSIMutableTargetRef target, // The TSIH was recorded by iSCSISessionLoginQuery since we're // entering the full-feature phase (see iSCSISessionLoginQuery documentation) - iSCSIKernelSetSessionOpt(sessionId,kiSCSIKernelSOTargetSessionId, - &context.targetSessionId,sizeof(context.targetSessionId)); + iSCSIHBAInterfaceSetSessionParameter(hbaInterface,sessionId, + kiSCSIHBASOTargetSessionId, + &context.targetSessionId,sizeof(context.targetSessionId)); if(!error) - error = iSCSINegotiateParseSWDictCommon(sessionId,sessCmd,sessRsp); + error = iSCSINegotiateParseSWDictCommon(managerRef,sessionId,sessCmd,sessRsp); if(!error && iSCSITargetGetIQN(target) != NULL) - error = iSCSINegotiateParseSWDictNormal(sessionId,sessCmd,sessRsp); + error = iSCSINegotiateParseSWDictNormal(managerRef,sessionId,sessCmd,sessRsp); if(!error) - error = iSCSINegotiateParseCWDict(sessionId,connectionId,sessCmd,sessRsp); + error = iSCSINegotiateParseCWDict(managerRef,sessionId,connectionId,sessCmd,sessRsp); } // If no error and the target returned an alias save it... @@ -551,11 +559,14 @@ errno_t iSCSINegotiateSession(iSCSIMutableTargetRef target, /*! Helper function. Negotiates operational parameters for a connection * as part of the login and connection instantiation process. */ -errno_t iSCSINegotiateConnection(iSCSITargetRef target, - SID sessionId, - CID connectionId, +errno_t iSCSINegotiateConnection(iSCSISessionManagerRef managerRef, + iSCSITargetRef target, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId, enum iSCSILoginStatusCode * statusCode) { + iSCSIHBAInterfaceRef hbaInterface = iSCSISessionManagerGetHBAInterface(managerRef); + // Create a dictionary to store query request CFMutableDictionaryRef connCmd = CFDictionaryCreateMutable( kCFAllocatorDefault, @@ -574,6 +585,7 @@ errno_t iSCSINegotiateConnection(iSCSITargetRef target, &kCFTypeDictionaryValueCallBacks); struct iSCSILoginQueryContext context; + context.interface = hbaInterface; context.sessionId = sessionId; context.connectionId = connectionId; context.currentStage = kiSCSIPDULoginOperationalNegotiation; @@ -583,8 +595,8 @@ errno_t iSCSINegotiateConnection(iSCSITargetRef target, // active session if we are simply adding a connection, so we // can't assume that this is the leading login and therefore // cannot set TSIH to 0. - iSCSIKernelGetSessionOpt(sessionId,kiSCSIKernelSOTargetSessionId, - &context.targetSessionId,sizeof(context.targetSessionId)); + iSCSIHBAInterfaceGetSessionParameter(hbaInterface,sessionId,kiSCSIHBASOTargetSessionId, + &context.targetSessionId,sizeof(context.targetSessionId)); // If the target session ID is non-zero, we're simply adding a new // connection and we can enter the full feature after this negotiation. @@ -602,7 +614,7 @@ errno_t iSCSINegotiateConnection(iSCSITargetRef target, // If no error, parse received dictionary and store connection options if(!error && *statusCode == kiSCSILoginSuccess) - error = iSCSINegotiateParseCWDict(sessionId,connectionId,connCmd,connRsp); + error = iSCSINegotiateParseCWDict(managerRef,sessionId,connectionId,connCmd,connRsp); CFRelease(connCmd); CFRelease(connRsp); @@ -610,21 +622,23 @@ errno_t iSCSINegotiateConnection(iSCSITargetRef target, } /*! Helper function used to log out of connections and sessions. */ -errno_t iSCSISessionLogoutCommon(SID sessionId, - CID connectionId, +errno_t iSCSISessionLogoutCommon(iSCSISessionManagerRef managerRef, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId, enum iSCSIPDULogoutReasons logoutReason, enum iSCSILogoutStatusCode * statusCode) { if(sessionId >= kiSCSIInvalidSessionId || connectionId >= kiSCSIInvalidConnectionId) return EINVAL; + iSCSIHBAInterfaceRef hbaInterface = iSCSISessionManagerGetHBAInterface(managerRef); errno_t error = 0; // Create a logout PDU and log out of the session iSCSIPDULogoutReqBHS cmd = iSCSIPDULogoutReqBHSInit; cmd.reasonCode = logoutReason | kISCSIPDULogoutReasonCodeFlag; - if((error = iSCSIKernelSend(sessionId,connectionId,(iSCSIPDUInitiatorBHS *)&cmd,NULL,0))) + if((error = iSCSIHBAInterfaceSend(hbaInterface,sessionId,connectionId,(iSCSIPDUInitiatorBHS *)&cmd,NULL,0))) return error; // Get response from iSCSI portal @@ -633,7 +647,7 @@ errno_t iSCSISessionLogoutCommon(SID sessionId, size_t length = 0; // Receive response PDU... - if((error = iSCSIKernelRecv(sessionId,connectionId,(iSCSIPDUTargetBHS *)&rsp,&data,&length))) + if((error = iSCSIHBAInterfaceReceive(hbaInterface,sessionId,connectionId,(iSCSIPDUTargetBHS *)&rsp,&data,&length))) return error; if(rsp.opCode == kiSCSIPDUOpCodeLogoutRsp) @@ -647,106 +661,6 @@ errno_t iSCSISessionLogoutCommon(SID sessionId, return error; } -/*! Helper function used to resolve target nodes as specified by connInfo. - * The target nodes specified in connInfo may be a DNS name, an IPv4 or - * IPv6 address. */ -errno_t iSCSISessionResolveNode(iSCSIPortalRef portal, - struct sockaddr_storage * ssTarget, - struct sockaddr_storage * ssHost) -{ - if (!portal || !ssTarget || !ssHost) - return EINVAL; - - errno_t error = 0; - - // Resolve the target node first and get a sockaddr info for it - const char * targetAddr, * targetPort; - - targetAddr = CFStringGetCStringPtr(iSCSIPortalGetAddress(portal),kCFStringEncodingUTF8); - targetPort = CFStringGetCStringPtr(iSCSIPortalGetPort(portal),kCFStringEncodingUTF8); - - struct addrinfo hints = { - .ai_family = AF_UNSPEC, - .ai_socktype = SOCK_STREAM, - .ai_protocol = IPPROTO_TCP, - }; - - struct addrinfo * aiTarget = NULL; - if((error = getaddrinfo(targetAddr,targetPort,&hints,&aiTarget))) - return error; - - // Copy the sock_addr structure into a sockaddr_storage structure (this - // may be either an IPv4 or IPv6 sockaddr structure) - memcpy(ssTarget,aiTarget->ai_addr,aiTarget->ai_addrlen); - - freeaddrinfo(aiTarget); - - // If the default interface is to be used, prepare a structure for it - CFStringRef hostIface = iSCSIPortalGetHostInterface(portal); - - if(CFStringCompare(hostIface,kiSCSIDefaultHostInterface,0) == kCFCompareEqualTo) - { - ssHost->ss_family = ssTarget->ss_family; - - // For completeness, setup the sockaddr_in structure - if(ssHost->ss_family == AF_INET) - { - struct sockaddr_in * sa = (struct sockaddr_in *)ssHost; - sa->sin_port = 0; - sa->sin_addr.s_addr = htonl(INADDR_ANY); - sa->sin_len = sizeof(struct sockaddr_in); - } - -// TODO: test IPv6 functionality - else if(ssHost->ss_family == AF_INET6) - { - struct sockaddr_in6 * sa = (struct sockaddr_in6 *)ssHost; - sa->sin6_addr = in6addr_any; - } - - return error; - } - - // Otherwise we have to search the list of all interfaces for the specified - // interface and copy the corresponding address structure - struct ifaddrs * interfaceList; - - if((error = getifaddrs(&interfaceList))) - return error; - - error = EAFNOSUPPORT; - struct ifaddrs * interface = interfaceList; - - while(interface) - { - // Check if interface supports the targets address family (e.g., IPv4) - if(interface->ifa_addr->sa_family == ssTarget->ss_family) - { - CFStringRef currIface = CFStringCreateWithCStringNoCopy( - kCFAllocatorDefault, - interface->ifa_name, - kCFStringEncodingUTF8, - kCFAllocatorNull); - - Boolean ifaceNameMatch = - CFStringCompare(currIface,hostIface,kCFCompareCaseInsensitive) == kCFCompareEqualTo; - CFRelease(currIface); - // Check if interface names match... - if(ifaceNameMatch) - { - memcpy(ssHost,interface->ifa_addr,interface->ifa_addr->sa_len); - error = 0; - break; - } - } - interface = interface->ifa_next; - } - - freeifaddrs(interfaceList); - return error; -} - - /*! Adds a new connection to an iSCSI session. * @param sessionId the new session identifier. * @param portal specifies the portal to use for the connection. @@ -756,85 +670,87 @@ errno_t iSCSISessionResolveNode(iSCSIPortalRef portal, * @param connectionId the new connection identifier. * @param statusCode iSCSI response code indicating operation status. * @return an error code indicating whether the operation was successful. */ -errno_t iSCSILoginConnection(SID sessionId, - iSCSIPortalRef portal, - iSCSIAuthRef initiatorAuth, - iSCSIAuthRef targetAuth, - iSCSIConnectionConfigRef connCfg, - CID * connectionId, - enum iSCSILoginStatusCode * statusCode) +errno_t iSCSISessionAddConnection(iSCSISessionManagerRef managerRef, + SessionIdentifier sessionId, + iSCSIPortalRef portal, + iSCSIAuthRef initiatorAuth, + iSCSIAuthRef targetAuth, + iSCSIConnectionConfigRef connCfg, + ConnectionIdentifier * connectionId, + enum iSCSILoginStatusCode * statusCode) { if(!portal || sessionId == kiSCSIInvalidSessionId || !connectionId) return EINVAL; - // Reset connection ID by default + iSCSIHBAInterfaceRef hbaInterface = iSCSISessionManagerGetHBAInterface(managerRef); *connectionId = kiSCSIInvalidConnectionId; - errno_t error = 0; // Resolve information about the target struct sockaddr_storage ssTarget, ssHost; - if((error = iSCSISessionResolveNode(portal,&ssTarget,&ssHost))) + if((error = iSCSIUtilsGetAddressForPortal(portal,&ssTarget,&ssHost))) return error; // If both target and host were resolved, grab a connection - error = iSCSIKernelCreateConnection(sessionId, - iSCSIPortalGetAddress(portal), - iSCSIPortalGetPort(portal), - iSCSIPortalGetHostInterface(portal), - &ssTarget, - &ssHost,connectionId); + error = iSCSIHBAInterfaceCreateConnection(hbaInterface,sessionId, + iSCSIPortalGetAddress(portal), + iSCSIPortalGetPort(portal), + iSCSIPortalGetHostInterface(portal), + &ssTarget, + &ssHost,connectionId); // If we can't accomodate a new connection quit; try again later if(error || *connectionId == kiSCSIInvalidConnectionId) return EAGAIN; - iSCSITargetRef targetTemp = iSCSICreateTargetForSessionId(sessionId); + iSCSITargetRef targetTemp = iSCSISessionCopyTargetForId(managerRef,sessionId); iSCSIMutableTargetRef target = iSCSITargetCreateMutableCopy(targetTemp); iSCSITargetRelease(targetTemp); // If no error, authenticate (negotiate security parameters) if(!error && *statusCode == kiSCSILoginSuccess) - error = iSCSIAuthNegotiate(target,initiatorAuth,targetAuth,sessionId,*connectionId,statusCode); + error = iSCSIAuthNegotiate(managerRef,target,initiatorAuth,targetAuth,sessionId,*connectionId,statusCode); if(!error && *statusCode == kiSCSILoginSuccess) - iSCSIKernelActivateConnection(sessionId,*connectionId); + iSCSIHBAInterfaceActivateConnection(hbaInterface,sessionId,*connectionId); else - iSCSIKernelReleaseConnection(sessionId,*connectionId); + iSCSIHBAInterfaceReleaseConnection(hbaInterface,sessionId,*connectionId); iSCSITargetRelease(target); return 0; } -errno_t iSCSILogoutConnection(SID sessionId, - CID connectionId, - enum iSCSILogoutStatusCode * statusCode) +errno_t iSCSISessionRemoveConnection(iSCSISessionManagerRef managerRef, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId, + enum iSCSILogoutStatusCode * statusCode) { if(sessionId >= kiSCSIInvalidSessionId || connectionId >= kiSCSIInvalidConnectionId) return EINVAL; + iSCSIHBAInterfaceRef hbaInterface = iSCSISessionManagerGetHBAInterface(managerRef); errno_t error = 0; // Release the session instead if there's only a single connection // for this session UInt32 numConnections = 0; - if((error = iSCSIKernelGetNumConnections(sessionId,&numConnections))) + if((error = iSCSIHBAInterfaceGetNumConnections(hbaInterface,sessionId,&numConnections))) return error; if(numConnections == 1) - return iSCSILogoutSession(sessionId,statusCode); + return iSCSISessionLogout(managerRef,sessionId,statusCode); // Deactivate connection before we remove it (this is optional but good // practice, as the kernel will deactivate the connection for us). - if(!(error = iSCSIKernelDeactivateConnection(sessionId,connectionId))) { + if(!(error = iSCSIHBAInterfaceDeactivateConnection(hbaInterface,sessionId,connectionId))) { // Logout the connection or session, as necessary - error = iSCSISessionLogoutCommon(sessionId,connectionId, + error = iSCSISessionLogoutCommon(managerRef,sessionId,connectionId, kISCSIPDULogoutCloseConnection,statusCode); } // Release the connection in the kernel - iSCSIKernelReleaseConnection(sessionId,connectionId); + iSCSIHBAInterfaceReleaseConnection(hbaInterface,sessionId,connectionId); return error; } @@ -851,37 +767,38 @@ errno_t iSCSILogoutConnection(SID sessionId, * @param connectionId the new connection identifier. * @param statusCode iSCSI response code indicating operation status. * @return an error code indicating whether the operation was successful. */ -errno_t iSCSILoginSession(iSCSIMutableTargetRef target, +errno_t iSCSISessionLogin(iSCSISessionManagerRef managerRef, + iSCSIMutableTargetRef target, iSCSIPortalRef portal, iSCSIAuthRef initiatorAuth, iSCSIAuthRef targetAuth, iSCSISessionConfigRef sessCfg, iSCSIConnectionConfigRef connCfg, - SID * sessionId, - CID * connectionId, + SessionIdentifier * sessionId, + ConnectionIdentifier * connectionId, enum iSCSILoginStatusCode * statusCode) { if(!target || !portal || !sessCfg || !connCfg || !sessionId || !connectionId || !statusCode || !initiatorAuth || !targetAuth) return EINVAL; - // Store errno from helpers and pass back to up the call chain + iSCSIHBAInterfaceRef hbaInterface = iSCSISessionManagerGetHBAInterface(managerRef); errno_t error = 0; // Resolve the target address struct sockaddr_storage ssTarget, ssHost; - if((error = iSCSISessionResolveNode(portal,&ssTarget,&ssHost))) + if((error = iSCSIUtilsGetAddressForPortal(portal,&ssTarget,&ssHost))) return error; - // Create a new session in the kernel. This allocates session and // connection identifiers - error = iSCSIKernelCreateSession(iSCSITargetGetIQN(target), - iSCSIPortalGetAddress(portal), - iSCSIPortalGetPort(portal), - iSCSIPortalGetHostInterface(portal), - &ssTarget,&ssHost,sessionId,connectionId); + error = iSCSIHBAInterfaceCreateSession(hbaInterface, + iSCSITargetGetIQN(target), + iSCSIPortalGetAddress(portal), + iSCSIPortalGetPort(portal), + iSCSIPortalGetHostInterface(portal), + &ssTarget,&ssHost,sessionId,connectionId); // If session couldn't be allocated were maxed out; try again later if(!error && (*sessionId == kiSCSIInvalidSessionId || *connectionId == kiSCSIInvalidConnectionId)) @@ -889,20 +806,20 @@ errno_t iSCSILoginSession(iSCSIMutableTargetRef target, // If no error, authenticate (negotiate security parameters) if(!error) { - error = iSCSIAuthNegotiate(target,initiatorAuth,targetAuth, + error = iSCSIAuthNegotiate(managerRef,target,initiatorAuth,targetAuth, *sessionId,*connectionId,statusCode); } // Negotiate session & connection parameters if(!error && *statusCode == kiSCSILoginSuccess) - error = iSCSINegotiateSession(target,*sessionId,*connectionId,sessCfg,connCfg,statusCode); + error = iSCSINegotiateSession(managerRef,target,*sessionId,*connectionId,sessCfg,connCfg,statusCode); // Only activate connections for kernel use if no errors have occurred and // the session is not a discovery session if(error || *statusCode != kiSCSILoginSuccess) - iSCSIKernelReleaseSession(*sessionId); + iSCSIHBAInterfaceReleaseSession(hbaInterface,*sessionId); else if(CFStringCompare(iSCSITargetGetIQN(target),kiSCSIUnspecifiedTargetIQN,0) != kCFCompareEqualTo) - iSCSIKernelActivateConnection(*sessionId,*connectionId); + iSCSIHBAInterfaceActivateConnection(hbaInterface,*sessionId,*connectionId); return error; } @@ -914,25 +831,27 @@ errno_t iSCSILoginSession(iSCSIMutableTargetRef target, * sessions the future. * @param sessionId the session to release. * @return an error code indicating whether the operation was successful. */ -errno_t iSCSILogoutSession(SID sessionId, +errno_t iSCSISessionLogout(iSCSISessionManagerRef managerRef, + SessionIdentifier sessionId, enum iSCSILogoutStatusCode * statusCode) { if(sessionId == kiSCSIInvalidSessionId) return EINVAL; + iSCSIHBAInterfaceRef hbaInterface = iSCSISessionManagerGetHBAInterface(managerRef); errno_t error = 0; // First deactivate all of the connections - if((error = iSCSIKernelDeactivateAllConnections(sessionId))) + if((error = iSCSIHBAInterfaceDeactivateAllConnections(hbaInterface,sessionId))) return error; // Grab a handle to any connection so we can logout of the session - CID connectionId = kiSCSIInvalidConnectionId; - if(!(error = iSCSIKernelGetConnection(sessionId,&connectionId))) - error = iSCSISessionLogoutCommon(sessionId, connectionId,kiSCSIPDULogoutCloseSession,statusCode); + ConnectionIdentifier connectionId = kiSCSIInvalidConnectionId; + if(!(error = iSCSIHBAInterfaceGetConnection(hbaInterface,sessionId,&connectionId))) + error = iSCSISessionLogoutCommon(managerRef,sessionId,connectionId,kiSCSIPDULogoutCloseSession,statusCode); // Release all of the connections in the kernel by releasing the session - iSCSIKernelReleaseSession(sessionId); + iSCSIHBAInterfaceReleaseSession(hbaInterface,sessionId); return error; } @@ -996,7 +915,8 @@ void iSCSIPDUDataParseToDiscoveryRecCallback(void * keyContainer,CFStringRef key * @param discoveryRec a discovery record, containing the query results. * @param statusCode iSCSI response code indicating operation status. * @return an error code indicating whether the operation was successful. */ -errno_t iSCSIQueryPortalForTargets(iSCSIPortalRef portal, +errno_t iSCSIQueryPortalForTargets(iSCSISessionManagerRef managerRef, + iSCSIPortalRef portal, iSCSIAuthRef initiatorAuth, iSCSIMutableDiscoveryRecRef * discoveryRec, enum iSCSILoginStatusCode * statusCode) @@ -1009,15 +929,15 @@ errno_t iSCSIQueryPortalForTargets(iSCSIPortalRef portal, iSCSIMutableTargetRef target = iSCSITargetCreateMutable(); iSCSITargetSetIQN(target,kiSCSIUnspecifiedTargetIQN); - SID sessionId; - CID connectionId; + SessionIdentifier sessionId; + ConnectionIdentifier connectionId; iSCSIMutableSessionConfigRef sessCfg = iSCSISessionConfigCreateMutable(); iSCSIMutableConnectionConfigRef connCfg = iSCSIConnectionConfigCreateMutable(); iSCSIAuthRef targetAuth = iSCSIAuthCreateNone(); - errno_t error = iSCSILoginSession(target,portal,initiatorAuth,targetAuth, + errno_t error = iSCSISessionLogin(managerRef,target,portal,initiatorAuth,targetAuth, sessCfg,connCfg,&sessionId, &connectionId,statusCode); @@ -1048,8 +968,9 @@ errno_t iSCSIQueryPortalForTargets(iSCSIPortalRef portal, iSCSIPDUTextReqBHS cmd = iSCSIPDUTextReqBHSInit; cmd.textReqStageFlags |= kiSCSIPDUTextReqFinalFlag; cmd.targetTransferTag = kiSCSIPDUTargetTransferTagReserved; - - error = iSCSIKernelSend(sessionId,connectionId,(iSCSIPDUInitiatorBHS *)&cmd,data,length); + + iSCSIHBAInterfaceRef hbaInterface = iSCSISessionManagerGetHBAInterface(managerRef); + error = iSCSIHBAInterfaceSend(hbaInterface,sessionId,connectionId,(iSCSIPDUInitiatorBHS *)&cmd,data,length); iSCSIPDUDataRelease(&data); CFRelease(textCmd); @@ -1057,7 +978,7 @@ errno_t iSCSIQueryPortalForTargets(iSCSIPortalRef portal, if(error) { enum iSCSILogoutStatusCode statusCode; - iSCSILogoutSession(sessionId,&statusCode); + iSCSISessionLogout(managerRef,sessionId,&statusCode); return error; } @@ -1067,12 +988,12 @@ errno_t iSCSIQueryPortalForTargets(iSCSIPortalRef portal, *discoveryRec = iSCSIDiscoveryRecCreateMutable(); do { - if((error = iSCSIKernelRecv(sessionId,connectionId,(iSCSIPDUTargetBHS *)&rsp,&data,&length))) + if((error = iSCSIHBAInterfaceReceive(hbaInterface,sessionId,connectionId,(iSCSIPDUTargetBHS *)&rsp,&data,&length))) { iSCSIPDUDataRelease(&data); enum iSCSILogoutStatusCode statusCode; - iSCSILogoutSession(sessionId,&statusCode); + iSCSISessionLogout(managerRef,sessionId,&statusCode); return error; } @@ -1094,7 +1015,7 @@ errno_t iSCSIQueryPortalForTargets(iSCSIPortalRef portal, iSCSIPDUDataRelease(&data); enum iSCSILogoutStatusCode logoutStatusCode; - iSCSILogoutSession(sessionId,&logoutStatusCode); + iSCSISessionLogout(managerRef,sessionId,&logoutStatusCode); // Per RFC3720, the "TargetAddress" key is optional in a SendTargets // discovery operation. Therefore, certain targets may respond with @@ -1136,21 +1057,22 @@ errno_t iSCSIQueryPortalForTargets(iSCSIPortalRef portal, * @param initiatorAuth specifies the initiator authentication parameters. * @param statusCode iSCSI response code indicating operation status. * @return an error code indicating whether the operation was successful. */ -errno_t iSCSIQueryTargetForAuthMethod(iSCSIPortalRef portal, +errno_t iSCSIQueryTargetForAuthMethod(iSCSISessionManagerRef managerRef, + iSCSIPortalRef portal, CFStringRef targetIQN, enum iSCSIAuthMethods * authMethod, enum iSCSILoginStatusCode * statusCode) { if(!portal || !authMethod) return EINVAL; - - // Store errno from helpers and pass back up the call chain + + iSCSIHBAInterfaceRef hbaInterface = iSCSISessionManagerGetHBAInterface(managerRef); errno_t error = 0; // Resolve information about the target struct sockaddr_storage ssTarget, ssHost; - if((error = iSCSISessionResolveNode(portal,&ssTarget,&ssHost))) + if((error = iSCSIUtilsGetAddressForPortal(portal,&ssTarget,&ssHost))) return error; // Create a discovery session to the portal @@ -1159,9 +1081,10 @@ errno_t iSCSIQueryTargetForAuthMethod(iSCSIPortalRef portal, // Create session (incl. qualifier) and a new connection (incl. Id) // Reset qualifier and connection ID by default - SID sessionId; - CID connectionId; - error = iSCSIKernelCreateSession(targetIQN, + SessionIdentifier sessionId; + ConnectionIdentifier connectionId; + error = iSCSIHBAInterfaceCreateSession(hbaInterface, + targetIQN, iSCSIPortalGetAddress(portal), iSCSIPortalGetPort(portal), iSCSIPortalGetHostInterface(portal), @@ -1172,13 +1095,14 @@ errno_t iSCSIQueryTargetForAuthMethod(iSCSIPortalRef portal, // If no error, authenticate (negotiate security parameters) if(!error) - error = iSCSIAuthInterrogate(target, + error = iSCSIAuthInterrogate(managerRef, + target, sessionId, connectionId, authMethod, statusCode); - iSCSIKernelReleaseSession(sessionId); + iSCSIHBAInterfaceReleaseSession(hbaInterface,sessionId); iSCSITargetRelease(target); return error; @@ -1187,29 +1111,35 @@ errno_t iSCSIQueryTargetForAuthMethod(iSCSIPortalRef portal, /*! Gets the session identifier associated with the specified target. * @param targetIQN the name of the target. * @return the session identiifer. */ -SID iSCSIGetSessionIdForTarget(CFStringRef targetIQN) +SessionIdentifier iSCSISessionGetSessionIdForTarget(iSCSISessionManagerRef managerRef, + CFStringRef targetIQN) { - return iSCSIKernelGetSessionIdForTargetIQN(targetIQN); + iSCSIHBAInterfaceRef hbaInterface = iSCSISessionManagerGetHBAInterface(managerRef); + return iSCSIHBAInterfaceGetSessionIdForTargetIQN(hbaInterface,targetIQN); } /*! Gets the connection identifier associated with the specified portal. * @param sessionId the session identifier. * @param portal the portal connected on the specified session. * @return the associated connection identifier. */ -CID iSCSIGetConnectionIdForPortal(SID sessionId,iSCSIPortalRef portal) +ConnectionIdentifier iSCSISessionGetConnectionIdForPortal(iSCSISessionManagerRef managerRef, + SessionIdentifier sessionId, + iSCSIPortalRef portal) { - return iSCSIKernelGetConnectionIdForPortalAddress(sessionId,iSCSIPortalGetAddress(portal)); + iSCSIHBAInterfaceRef hbaInterface = iSCSISessionManagerGetHBAInterface(managerRef); + return iSCSIHBAInterfaceGetConnectionIdForPortalAddress(hbaInterface,sessionId,iSCSIPortalGetAddress(portal)); } /*! Gets an array of session identifiers for each session. * @param sessionIds an array of session identifiers. * @return an array of session identifiers. */ -CFArrayRef iSCSICreateArrayOfSessionIds() +CFArrayRef iSCSISessionCopyArrayOfSessionIds(iSCSISessionManagerRef managerRef) { - SID sessionIds[kiSCSIMaxSessions]; + iSCSIHBAInterfaceRef hbaInterface = iSCSISessionManagerGetHBAInterface(managerRef); + SessionIdentifier sessionIds[kiSCSIMaxSessions]; UInt16 sessionCount; - - if(iSCSIKernelGetSessionIds(sessionIds,&sessionCount)) + + if(iSCSIHBAInterfaceGetSessionIds(hbaInterface,sessionIds,&sessionCount)) return NULL; return CFArrayCreate(kCFAllocatorDefault,(const void **) sessionIds,sessionCount,NULL); @@ -1218,15 +1148,17 @@ CFArrayRef iSCSICreateArrayOfSessionIds() /*! Gets an array of connection identifiers for each session. * @param sessionId session identifier. * @return an array of connection identifiers. */ -CFArrayRef iSCSICreateArrayOfConnectionsIds(SID sessionId) +CFArrayRef iSCSISessionCopyArrayOfConnectionIds(iSCSISessionManagerRef managerRef, + SessionIdentifier sessionId) { if(sessionId == kiSCSIInvalidSessionId) return NULL; - CID connectionIds[kiSCSIMaxConnectionsPerSession]; + iSCSIHBAInterfaceRef hbaInterface = iSCSISessionManagerGetHBAInterface(managerRef); + ConnectionIdentifier connectionIds[kiSCSIMaxConnectionsPerSession]; UInt32 connectionCount; - if(iSCSIKernelGetConnectionIds(sessionId,connectionIds,&connectionCount)) + if(iSCSIHBAInterfaceGetConnectionIds(hbaInterface,sessionId,connectionIds,&connectionCount)) return NULL; return CFArrayCreate(kCFAllocatorDefault,(const void **)&connectionIds,connectionCount,NULL); @@ -1235,12 +1167,13 @@ CFArrayRef iSCSICreateArrayOfConnectionsIds(SID sessionId) /*! Creates a target object for the specified session. * @param sessionId the session identifier. * @return target the target object. */ -iSCSITargetRef iSCSICreateTargetForSessionId(SID sessionId) +iSCSITargetRef iSCSISessionCopyTargetForId(iSCSISessionManagerRef managerRef,SessionIdentifier sessionId) { if(sessionId == kiSCSIInvalidSessionId) return NULL; - CFStringRef targetIQN = iSCSIKernelCreateTargetIQNForSessionId(sessionId); + iSCSIHBAInterfaceRef hbaInterface = iSCSISessionManagerGetHBAInterface(managerRef); + CFStringRef targetIQN = iSCSIHBAInterfaceCreateTargetIQNForSessionId(hbaInterface,sessionId); if(!targetIQN) return NULL; @@ -1257,24 +1190,27 @@ iSCSITargetRef iSCSICreateTargetForSessionId(SID sessionId) * @param sessionId the session identifier. * @param connectionId the connection identifier. * @return portal information about the portal. */ -iSCSIPortalRef iSCSICreatePortalForConnectionId(SID sessionId,CID connectionId) +iSCSIPortalRef iSCSISessionCopyPortalForConnectionId(iSCSISessionManagerRef managerRef, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId) { if(sessionId == kiSCSIInvalidSessionId || connectionId == kiSCSIInvalidConnectionId) return NULL; + iSCSIHBAInterfaceRef hbaInterface = iSCSISessionManagerGetHBAInterface(managerRef); CFStringRef address,port,hostInterface; - if(!(address = iSCSIKernelCreatePortalAddressForConnectionId(sessionId,connectionId))) + if(!(address = iSCSIHBAInterfaceCreatePortalAddressForConnectionId(hbaInterface,sessionId,connectionId))) return NULL; - if(!(port = iSCSIKernelCreatePortalPortForConnectionId(sessionId,connectionId))) + if(!(port = iSCSIHBAInterfaceCreatePortalPortForConnectionId(hbaInterface,sessionId,connectionId))) { CFRelease(address); return NULL; } - if(!(hostInterface = iSCSIKernelCreateHostInterfaceForConnectionId(sessionId,connectionId))) + if(!(hostInterface = iSCSIHBAInterfaceCreateHostInterfaceForConnectionId(hbaInterface,sessionId,connectionId))) { CFRelease(address); CFRelease(port); @@ -1298,51 +1234,53 @@ iSCSIPortalRef iSCSICreatePortalForConnectionId(SID sessionId,CID connectionId) * @param target the target to check for associated sessions to generate * a dictionary of session parameters. * @return a dictionary of session properties. */ -CFDictionaryRef iSCSICreateCFPropertiesForSession(iSCSITargetRef target) +CFDictionaryRef iSCSISessionCopyCFPropertiesForTarget(iSCSISessionManagerRef managerRef, + iSCSITargetRef target) { if(!target) return NULL; + iSCSIHBAInterfaceRef hbaInterface = iSCSISessionManagerGetHBAInterface(managerRef); CFDictionaryRef dictionary = NULL; - SID sessionId = iSCSIGetSessionIdForTarget(iSCSITargetGetIQN(target)); + + SessionIdentifier sessionId = iSCSISessionGetSessionIdForTarget(managerRef,iSCSITargetGetIQN(target)); if(sessionId == kiSCSIInvalidSessionId) return NULL; // Get session options from kernel - - UInt32 optVal32 = 0; + UInt32 paramVal32 = 0; - iSCSIKernelGetSessionOpt(sessionId,kiSCSIKernelSOMaxConnections,&optVal32,sizeof(optVal32)); - CFNumberRef maxConnections = CFNumberCreate(kCFAllocatorDefault,kCFNumberIntType,&optVal32); + iSCSIHBAInterfaceGetSessionParameter(hbaInterface,sessionId,kiSCSIHBASOMaxConnections,¶mVal32,sizeof(paramVal32)); + CFNumberRef maxConnections = CFNumberCreate(kCFAllocatorDefault,kCFNumberIntType,¶mVal32); - iSCSIKernelGetSessionOpt(sessionId,kiSCSIKernelSOMaxBurstLength,&optVal32,sizeof(optVal32)); - CFNumberRef maxBurstLength = CFNumberCreate(kCFAllocatorDefault,kCFNumberIntType,&optVal32); + iSCSIHBAInterfaceGetSessionParameter(hbaInterface,sessionId,kiSCSIHBASOMaxBurstLength,¶mVal32,sizeof(paramVal32)); + CFNumberRef maxBurstLength = CFNumberCreate(kCFAllocatorDefault,kCFNumberIntType,¶mVal32); - iSCSIKernelGetSessionOpt(sessionId,kiSCSIKernelSOFirstBurstLength,&optVal32,sizeof(optVal32)); - CFNumberRef firstBurstLength = CFNumberCreate(kCFAllocatorDefault,kCFNumberIntType,&optVal32); + iSCSIHBAInterfaceGetSessionParameter(hbaInterface,sessionId,kiSCSIHBASOFirstBurstLength,¶mVal32,sizeof(paramVal32)); + CFNumberRef firstBurstLength = CFNumberCreate(kCFAllocatorDefault,kCFNumberIntType,¶mVal32); - iSCSIKernelGetSessionOpt(sessionId,kiSCSIKernelSOMaxOutstandingR2T,&optVal32,sizeof(optVal32)); - CFNumberRef maxOutStandingR2T = CFNumberCreate(kCFAllocatorDefault,kCFNumberIntType,&optVal32); + iSCSIHBAInterfaceGetSessionParameter(hbaInterface,sessionId,kiSCSIHBASOMaxOutstandingR2T,¶mVal32,sizeof(paramVal32)); + CFNumberRef maxOutStandingR2T = CFNumberCreate(kCFAllocatorDefault,kCFNumberIntType,¶mVal32); - iSCSIKernelGetSessionOpt(sessionId,kiSCSIKernelSODefaultTime2Retain,&optVal32,sizeof(optVal32)); - CFNumberRef defaultTime2Retain = CFNumberCreate(kCFAllocatorDefault,kCFNumberIntType,&optVal32); + iSCSIHBAInterfaceGetSessionParameter(hbaInterface,sessionId,kiSCSIHBASODefaultTime2Retain,¶mVal32,sizeof(paramVal32)); + CFNumberRef defaultTime2Retain = CFNumberCreate(kCFAllocatorDefault,kCFNumberIntType,¶mVal32); - iSCSIKernelGetSessionOpt(sessionId,kiSCSIKernelSODefaultTime2Wait,&optVal32,sizeof(optVal32)); - CFNumberRef defaultTime2Wait = CFNumberCreate(kCFAllocatorDefault,kCFNumberIntType,&optVal32); + iSCSIHBAInterfaceGetSessionParameter(hbaInterface,sessionId,kiSCSIHBASODefaultTime2Wait,¶mVal32,sizeof(paramVal32)); + CFNumberRef defaultTime2Wait = CFNumberCreate(kCFAllocatorDefault,kCFNumberIntType,¶mVal32); - TPGT tpgt = 0; - iSCSIKernelGetSessionOpt(sessionId,kiSCSIKernelSOTargetPortalGroupTag,&tpgt,sizeof(TPGT)); + TargetPortalGroupTag tpgt = 0; + iSCSIHBAInterfaceGetSessionParameter(hbaInterface,sessionId,kiSCSIHBASOTargetPortalGroupTag,&tpgt,sizeof(TargetPortalGroupTag)); CFNumberRef targetPortalGroupTag = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt16Type,&tpgt); - TSIH tsih = 0; - iSCSIKernelGetSessionOpt(sessionId,kiSCSIKernelSOTargetSessionId,&tsih,sizeof(TSIH)); + TargetSessionIdentifier tsih = 0; + iSCSIHBAInterfaceGetSessionParameter(hbaInterface,sessionId,kiSCSIHBASOTargetSessionId,&tsih,sizeof(TargetSessionIdentifier)); CFNumberRef targetSessionId = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt16Type,&tsih); - UInt8 optVal8 = 0; - iSCSIKernelGetSessionOpt(sessionId,kiSCSIKernelSOErrorRecoveryLevel,&optVal8,sizeof(optVal8)); - CFNumberRef errorRecoveryLevel = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt8Type,&optVal8); + UInt8 paramVal8 = 0; + iSCSIHBAInterfaceGetSessionParameter(hbaInterface,sessionId,kiSCSIHBASOErrorRecoveryLevel,¶mVal8,sizeof(paramVal8)); + CFNumberRef errorRecoveryLevel = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt8Type,¶mVal8); CFNumberRef sessionIdentifier = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt16Type,&sessionId); @@ -1351,22 +1289,22 @@ CFDictionaryRef iSCSICreateCFPropertiesForSession(iSCSITargetRef target) CFStringRef dataPDUInOrder = kRFC3720_Value_No; CFStringRef dataSequenceInOrder = kRFC3720_Value_No; - Boolean optValBool = false; + Boolean paramValBool = false; - iSCSIKernelGetSessionOpt(sessionId,kiSCSIKernelSOImmediateData,&optValBool,sizeof(Boolean)); - if(optValBool) + iSCSIHBAInterfaceGetSessionParameter(hbaInterface,sessionId,kiSCSIHBASOImmediateData,¶mValBool,sizeof(Boolean)); + if(paramValBool) immediateData = kRFC3720_Value_Yes; - iSCSIKernelGetSessionOpt(sessionId,kiSCSIKernelSOInitialR2T,&optValBool,sizeof(Boolean)); - if(optValBool) + iSCSIHBAInterfaceGetSessionParameter(hbaInterface,sessionId,kiSCSIHBASOInitialR2T,¶mValBool,sizeof(Boolean)); + if(paramValBool) initialR2T = kRFC3720_Value_Yes; - iSCSIKernelGetSessionOpt(sessionId,kiSCSIKernelSODataPDUInOrder,&optValBool,sizeof(Boolean)); - if(optValBool) + iSCSIHBAInterfaceGetSessionParameter(hbaInterface,sessionId,kiSCSIHBASODataPDUInOrder,¶mValBool,sizeof(Boolean)); + if(paramValBool) dataPDUInOrder = kRFC3720_Value_Yes; - iSCSIKernelGetSessionOpt(sessionId,kiSCSIKernelSODataSequenceInOrder,&optValBool,sizeof(Boolean)); - if(optValBool) + iSCSIHBAInterfaceGetSessionParameter(hbaInterface,sessionId,kiSCSIHBASODataSequenceInOrder,¶mValBool,sizeof(Boolean)); + if(paramValBool) dataSequenceInOrder = kRFC3720_Value_Yes; const void * keys[] = { @@ -1418,45 +1356,47 @@ CFDictionaryRef iSCSICreateCFPropertiesForSession(iSCSITargetRef target) * @param portal the portal to check for active connections to generate * a dictionary of connection parameters. * @return a dictionary of connection properties. */ -CFDictionaryRef iSCSICreateCFPropertiesForConnection(iSCSITargetRef target, +CFDictionaryRef iSCSISessionCopyCFPropertiesForPortal(iSCSISessionManagerRef managerRef, + iSCSITargetRef target, iSCSIPortalRef portal) { if(!target || !portal) return NULL; + iSCSIHBAInterfaceRef hbaInterface = iSCSISessionManagerGetHBAInterface(managerRef); CFDictionaryRef dictionary = NULL; - SID sessionId = iSCSIGetSessionIdForTarget(iSCSITargetGetIQN(target)); - CID connectionId = kiSCSIInvalidConnectionId; + SessionIdentifier sessionId = iSCSISessionGetSessionIdForTarget(managerRef,iSCSITargetGetIQN(target)); + ConnectionIdentifier connectionId = kiSCSIInvalidConnectionId; if(sessionId == kiSCSIInvalidSessionId) return NULL; // Validate connection identifier - connectionId = iSCSIGetConnectionIdForPortal(sessionId,portal); + connectionId = iSCSISessionGetConnectionIdForPortal(managerRef,sessionId,portal); if(connectionId == kiSCSIInvalidConnectionId) return NULL; // Get connection options from kernel - UInt32 optVal32 = 0; - iSCSIKernelGetConnectionOpt(sessionId,connectionId,kiSCSIKernelCOMaxRecvDataSegmentLength,&optVal32,sizeof(optVal32)); - CFNumberRef maxRecvDataSegmentLength = CFNumberCreate(kCFAllocatorDefault,kCFNumberIntType,&optVal32); + UInt32 paramVal32 = 0; + iSCSIHBAInterfaceGetConnectionParameter(hbaInterface,sessionId,connectionId,kiSCSIHBACOMaxRecvDataSegmentLength,¶mVal32,sizeof(paramVal32)); + CFNumberRef maxRecvDataSegmentLength = CFNumberCreate(kCFAllocatorDefault,kCFNumberIntType,¶mVal32); CFNumberRef connectionIdentifier = CFNumberCreate(kCFAllocatorDefault,kCFNumberIntType,&connectionId); - + enum iSCSIDigestTypes dataDigestType = kiSCSIDigestNone; enum iSCSIDigestTypes headerDigestType = kiSCSIDigestNone; - Boolean optValBool = false; + Boolean paramValBool = false; - iSCSIKernelGetConnectionOpt(sessionId,connectionId,kiSCSIKernelCOUseDataDigest,&optValBool,sizeof(Boolean)); - if(optValBool) + iSCSIHBAInterfaceGetConnectionParameter(hbaInterface,sessionId,connectionId,kiSCSIHBACOUseDataDigest,¶mValBool,sizeof(Boolean)); + if(paramValBool) dataDigestType = kiSCSIDigestCRC32C; - iSCSIKernelGetConnectionOpt(sessionId,connectionId,kiSCSIKernelCOUseHeaderDigest,&optValBool,sizeof(Boolean)); - if(optValBool) + iSCSIHBAInterfaceGetConnectionParameter(hbaInterface,sessionId,connectionId,kiSCSIHBACOUseHeaderDigest,¶mValBool,sizeof(Boolean)); + if(paramValBool) dataDigestType = kiSCSIDigestCRC32C; CFNumberRef dataDigest = CFNumberCreate(kCFAllocatorDefault, @@ -1487,109 +1427,4 @@ CFDictionaryRef iSCSICreateCFPropertiesForConnection(iSCSITargetRef target, return dictionary; } -/*! Sets the name of this initiator. This is the IQN-format name that is - * exchanged with a target during negotiation. - * @param initiatorIQN the initiator name. */ -void iSCSISetInitiatorName(CFStringRef initiatorIQN) -{ - if(!initiatorIQN) - return; - - CFRelease(kiSCSIInitiatorIQN); - kiSCSIInitiatorIQN = CFStringCreateCopy(kCFAllocatorDefault,initiatorIQN); -} - -/*! Sets the alias of this initiator. This is the IQN-format alias that is - * exchanged with a target during negotiation. - * @param initiatorAlias the initiator alias. */ -void iSCSISetInitiatorAlias(CFStringRef initiatorAlias) -{ - if(!initiatorAlias) - return; - - CFRelease(kiSCSIInitiatorAlias); - kiSCSIInitiatorAlias = CFStringCreateCopy(kCFAllocatorDefault,initiatorAlias); -} - -/*! The kernel calls this function only for asynchronous events that - * involve dropped sessions, connections, logout requests and parameter - * negotiation. This function is not called for asynchronous SCSI messages - * or vendor-specific messages. */ -void iSCSISessionHandleKernelNotificationAsyncMessage(iSCSIKernelNotificationAsyncMessage * msg) -{ - enum iSCSIPDUAsyncMsgEvent asyncEvent = (enum iSCSIPDUAsyncMsgEvent)msg->asyncEvent; - enum iSCSILogoutStatusCode statusCode; - - CFStringRef statusString = CFStringCreateWithFormat(kCFAllocatorDefault,0, - CFSTR("iSCSI asynchronous message (code %d) received (sid: %d, cid: %d)"), - asyncEvent,msg->sessionId,msg->connectionId); - - asl_log(NULL,NULL,ASL_LEVEL_WARNING,"%s",CFStringGetCStringPtr(statusString,kCFStringEncodingASCII)); - - CFRelease(statusString); - - switch (asyncEvent) { - - // We are required to issue a logout request - case kiSCSIPDUAsyncMsgLogout: - iSCSILogoutConnection(msg->sessionId,msg->connectionId,&statusCode); - break; - - // We have been asked to re-negotiate parameters for this connection - // (this is currently unsupported and we logout) - case kiSCSIPDUAsyncMsgNegotiateParams: - iSCSILogoutConnection(msg->sessionId,msg->connectionId,&statusCode); - break; - - default: - break; - } -} - -/*! This function is called by the kernel extension to notify the daemon - * of an event that has occured within the kernel extension. */ -void iSCSISessionHandleKernelNotifications(enum iSCSIKernelNotificationTypes type, - iSCSIKernelNotificationMessage * msg) -{ - // Process an asynchronous message - switch(type) - { - // The kernel received an iSCSI asynchronous event message - case kiSCSIKernelNotificationAsyncMessage: - iSCSISessionHandleKernelNotificationAsyncMessage((iSCSIKernelNotificationAsyncMessage *)msg); - break; - case kISCSIKernelNotificationTerminate: break; - default: break; - }; -} - -/*! Call to initialize iSCSI session management functions. This function will - * initialize the kernel layer after which other session-related functions - * may be called. - * @param rl the runloop to use for executing session-related functions. - * @return an error code indicating the result of the operation. */ -errno_t iSCSIInitialize(CFRunLoopRef rl) -{ - errno_t error = iSCSIKernelInitialize(&iSCSISessionHandleKernelNotifications); - - if(!error) { - CFRunLoopSourceRef source; - - // Create a run loop source tied to the kernel notification system; - // if fail then kext may not be loaded, try again later - if((source = iSCSIKernelCreateRunLoopSource())) - CFRunLoopAddSource(rl,source,kCFRunLoopDefaultMode); - else - error = EAGAIN; - } - return error; -} -/*! Called to cleanup kernel resources used by the iSCSI session management - * functions. This function will close any connections to the kernel - * and stop processing messages related to the kernel. - * @return an error code indicating the result of the operation. */ -errno_t iSCSICleanup() -{ - return iSCSIKernelCleanup(); -} diff --git a/Source/User/iscsid/iSCSISession.h b/Source/User/iscsid/iSCSISession.h index dd7c235a..d6da6126 100644 --- a/Source/User/iscsid/iSCSISession.h +++ b/Source/User/iscsid/iSCSISession.h @@ -30,28 +30,13 @@ #define __ISCSI_SESSION_H__ #include -#include -#include -#include - -#include "iSCSITypes.h" -#include "iSCSIRFC3720Keys.h" - -/*! Call to initialize iSCSI session management functions. This function will - * initialize the kernel layer after which other session-related functions - * may be called. - * @param rl the runloop to use for executing session-related functions. - * @return an error code indicating the result of the operation. */ -errno_t iSCSIInitialize(CFRunLoopRef rl); - -/*! Called to cleanup kernel resources used by the iSCSI session management - * functions. This function will close any connections to the kernel - * and stop processing messages related to the kernel. - * @return an error code indicating the result of the operation. */ -errno_t iSCSICleanup(); + +#include "iSCSISessionManager.h" +#include "iSCSI.h" /*! Creates a normal iSCSI session and returns a handle to the session. Users * must call iSCSISessionClose to close this session and free resources. + * @param managerRef a session manager instance. * @param target specifies the target and connection parameters to use. * @param portal specifies the portal to use for the new session. * @param initiatorAuth specifies the initiator authentication parameters. @@ -62,22 +47,26 @@ errno_t iSCSICleanup(); * @param connectionId the new connection identifier. * @param statusCode iSCSI response code indicating operation status. * @return an error code indicating whether the operation was successful. */ -errno_t iSCSILoginSession(iSCSIMutableTargetRef target, +errno_t iSCSISessionLogin(iSCSISessionManagerRef managerRef, + iSCSIMutableTargetRef target, iSCSIPortalRef portal, iSCSIAuthRef initiatorAuth, iSCSIAuthRef targetAuth, iSCSISessionConfigRef sessCfg, iSCSIConnectionConfigRef connCfg, - SID * sessionId, - CID * connectionId, + SessionIdentifier * sessionId, + ConnectionIdentifier * connectionId, enum iSCSILoginStatusCode * statusCode); /*! Closes the iSCSI connection and frees the session qualifier. + * @param managerRef a session manager instance. * @param sessionId the session to free. */ -errno_t iSCSILogoutSession(SID sessionId, +errno_t iSCSISessionLogout(iSCSISessionManagerRef managerRef, + SessionIdentifier sessionId, enum iSCSILogoutStatusCode * statusCode); /*! Adds a new connection to an iSCSI session. + * @param managerRef a session manager instance. * @param sessionId the new session identifier. * @param portal specifies the portal to use for the connection. * @param initiatorAuth specifies the initiator authentication parameters. @@ -86,77 +75,95 @@ errno_t iSCSILogoutSession(SID sessionId, * @param connectionId the new connection identifier. * @param statusCode iSCSI response code indicating operation status. * @return an error code indicating whether the operation was successful. */ -errno_t iSCSILoginConnection(SID sessionId, - iSCSIPortalRef portal, - iSCSIAuthRef initiatorAuth, - iSCSIAuthRef targetAuth, - iSCSIConnectionConfigRef connCfg, - CID * connectionId, - enum iSCSILoginStatusCode * statusCode); +errno_t iSCSISessionAddConnection(iSCSISessionManagerRef managerRef, + SessionIdentifier sessionId, + iSCSIPortalRef portal, + iSCSIAuthRef initiatorAuth, + iSCSIAuthRef targetAuth, + iSCSIConnectionConfigRef connCfg, + ConnectionIdentifier * connectionId, + enum iSCSILoginStatusCode * statusCode); /*! Removes a connection from an existing session. + * @param managerRef a session manager instance. * @param sessionId the session to remove a connection from. * @param connectionId the connection to remove. * @param statusCode iSCSI response code indicating operation status. * @return an error code indicating whether the operation was successful. */ -errno_t iSCSILogoutConnection(SID sessionId, - CID connectionId, - enum iSCSILogoutStatusCode * statusCode); +errno_t iSCSISessionRemoveConnection(iSCSISessionManagerRef managerRef, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId, + enum iSCSILogoutStatusCode * statusCode); /*! Queries a portal for available targets (utilizes iSCSI SendTargets). + * @param managerRef a session manager instance. * @param portal the iSCSI portal to query. * @param auth specifies the authentication parameters to use. * @param discoveryRec a discovery record, containing the query results. * @param statusCode iSCSI response code indicating operation status. * @return an error code indicating whether the operation was successful. */ -errno_t iSCSIQueryPortalForTargets(iSCSIPortalRef portal, +errno_t iSCSIQueryPortalForTargets(iSCSISessionManagerRef managerRef, + iSCSIPortalRef portal, iSCSIAuthRef initiatorAuth, iSCSIMutableDiscoveryRecRef * discoveryRec, enum iSCSILoginStatusCode * statuscode); /*! Retrieves a list of targets available from a give portal. + * @param managerRef a session manager instance. * @param portal the iSCSI portal to look for targets. * @param initiatorAuth specifies the initiator authentication parameters. * @param statusCode iSCSI response code indicating operation status. * @return an error code indicating whether the operation was successful. */ -errno_t iSCSIQueryTargetForAuthMethod(iSCSIPortalRef portal, +errno_t iSCSIQueryTargetForAuthMethod(iSCSISessionManagerRef managerRef, + iSCSIPortalRef portal, CFStringRef targetIQN, enum iSCSIAuthMethods * authMethod, enum iSCSILoginStatusCode * statusCode); /*! Gets the session identifier associated with the specified target. + * @param managerRef a session manager instance. * @param targetIQN the name of the target. * @return the session identiifer. */ -SID iSCSIGetSessionIdForTarget(CFStringRef targetIQN); +SessionIdentifier iSCSISessionGetSessionIdForTarget(iSCSISessionManagerRef managerRef, + CFStringRef targetIQN); /*! Gets the connection identifier associated with the specified portal. + * @param managerRef a session manager instance. * @param sessionId the session identifier. * @param portal the portal connected on the specified session. * @return the associated connection identifier. */ -CID iSCSIGetConnectionIdForPortal(SID sessionId,iSCSIPortalRef portal); +ConnectionIdentifier iSCSISessionGetConnectionIdForPortal(iSCSISessionManagerRef managerRef, + SessionIdentifier sessionId, + iSCSIPortalRef portal); /*! Gets an array of session identifiers for each session. + * @param managerRef a session manager instance. * @param sessionIds an array of session identifiers. * @return an array of session identifiers. */ -CFArrayRef iSCSICreateArrayOfSessionIds(); +CFArrayRef iSCSISessionCopyArrayOfSessionIds(iSCSISessionManagerRef managerRef); /*! Gets an array of connection identifiers for each session. + * @param managerRef a session manager instance. * @param sessionId session identifier. * @return an array of connection identifiers. */ -CFArrayRef iSCSICreateArrayOfConnectionsIds(SID sessionId); +CFArrayRef iSCSISessionCopyArrayOfConnectionIds(iSCSISessionManagerRef managerRef, + SessionIdentifier sessionId); /*! Creates a target object for the specified session. + * @param managerRef a session manager instance. * @param sessionId the session identifier. * @return target the target object. */ -iSCSITargetRef iSCSICreateTargetForSessionId(SID sessionId); +iSCSITargetRef iSCSISessionCopyTargetForId(iSCSISessionManagerRef managerRef, + SessionIdentifier sessionId); /*! Creates a connection object for the specified connection. + * @param managerRef a session manager instance. * @param sessionId the session identifier. * @param connectionId the connection identifier. * @return portal information about the portal. */ -iSCSIPortalRef iSCSICreatePortalForConnectionId(SID sessionId,CID connectionId); - - +iSCSIPortalRef iSCSISessionCopyPortalForConnectionId(iSCSISessionManagerRef managerRef, + SessionIdentifier sessionId, + ConnectionIdentifier connectionId); /*! Creates a dictionary of session parameters for the session associated with * the specified target, if one exists. The following keys are guaranteed @@ -177,11 +184,12 @@ iSCSIPortalRef iSCSICreatePortalForConnectionId(SID sessionId,CID connectionId); * kRFC3720_Key_TargetSessionId (CFNumberRef, kCFNumberSInt16Type) * kRFC3720_Key_SessionId (CFNumberRef, kCFNumberSInt16Type) * - * @param handle a handle to a daemon connection. + * @param managerRef a session manager instance. * @param target the target to check for associated sessions to generate * a dictionary of session parameters. * @return a dictionary of session properties. */ -CFDictionaryRef iSCSICreateCFPropertiesForSession(iSCSITargetRef target); +CFDictionaryRef iSCSISessionCopyCFPropertiesForTarget(iSCSISessionManagerRef managerRef, + iSCSITargetRef target); /*! Creates a dictionary of connection parameters for the connection associated * with the specified target and portal, if one exists. The following keys @@ -192,23 +200,14 @@ CFDictionaryRef iSCSICreateCFPropertiesForSession(iSCSITargetRef target); * kRFC3720_Key_MaxRecvDataSegmentLength (CFNumberRef) * kRFC3720_Key_ConnectionId (CFNumberRef) * - * @param handle a handle to a daemon connection. + * @param managerRef a session manager instance. * @param target the target associated with the the specified portal. * @param portal the portal to check for active connections to generate * a dictionary of connection parameters. * @return a dictionary of connection properties. */ -CFDictionaryRef iSCSICreateCFPropertiesForConnection(iSCSITargetRef target, - iSCSIPortalRef portal); - -/*! Sets the name of this initiator. This is the IQN-format name that is - * exchanged with a target during negotiation. - * @param initiatorIQN the initiator name. */ -void iSCSISetInitiatorName(CFStringRef initiatorIQN); - -/*! Sets the alias of this initiator. This is the IQN-format alias that is - * exchanged with a target during negotiation. - * @param initiatorAlias the initiator alias. */ -void iSCSISetInitiatorAlias(CFStringRef initiatorAlias); +CFDictionaryRef iSCSISessionCopyCFPropertiesForPortal(iSCSISessionManagerRef managerRef, + iSCSITargetRef target, + iSCSIPortalRef portal); #endif diff --git a/Source/User/iscsid/iSCSISessionManager.c b/Source/User/iscsid/iSCSISessionManager.c new file mode 100644 index 00000000..1aa32a6f --- /dev/null +++ b/Source/User/iscsid/iSCSISessionManager.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2016, Nareg Sinenian + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "iSCSISessionManager.h" + +#include "iSCSISession.h" +#include "iSCSIHBAInterface.h" + +/*! Name of the initiator. */ +CFStringRef kiSCSIInitiatorIQN = CFSTR("iqn.2015-01.com.localhost"); + +/*! Alias of the initiator. */ +CFStringRef kiSCSIInitiatorAlias = CFSTR("default"); + +struct __iSCSISessionManager +{ + CFAllocatorRef allocator; + iSCSIHBAInterfaceRef hbaInterface; + iSCSISessionManagerCallBacks callbacks; + CFStringRef initiatorName; + CFStringRef initiatorAlias; + +}; + +/*! This function is called handle session or connection network timeouts. + * When a timeout occurs the kernel deactivates the session and connection. + * The session layer (this layer) must release the connection after propogating + * the notification onto the user of the session manager. */ + void iSCSIHBANotificationTimeoutMessageHandler(iSCSISessionManagerRef managerRef, + iSCSIHBANotificationMessage * msg) + { + // Retrieve the target name and portal address associated with + // the timeout. Pass information along to pre-designated runloop + // so that clients of this layer can act + iSCSITargetRef target = iSCSISessionCopyTargetForId(managerRef,msg->sessionId); + iSCSIPortalRef portal = iSCSISessionCopyPortalForConnectionId(managerRef,msg->sessionId,msg->connectionId); + + // Release the stale session/connection + iSCSIHBAInterfaceReleaseConnection(managerRef->hbaInterface,msg->sessionId,msg->connectionId); + + // Call user-defined callback function if one exists + if(managerRef->callbacks.timeoutCallback) + managerRef->callbacks.timeoutCallback(target,portal); + } + +/*! Called to handle asynchronous events that involve dropped sessions, connections, + * logout requests and parameter negotiation. This function is not called for asynchronous + * SCSI messages or vendor-specific messages. */ +void iSCSIHBANotificationAsyncMessageHandler(iSCSISessionManagerRef managerRef, + iSCSIHBANotificationAsyncMessage * msg) +{ + enum iSCSIPDUAsyncMsgEvent asyncEvent = (enum iSCSIPDUAsyncMsgEvent)msg->asyncEvent; + enum iSCSILogoutStatusCode statusCode; + + switch (asyncEvent) { + + // We are required to issue a logout request + case kiSCSIPDUAsyncMsgLogout: + iSCSISessionRemoveConnection(managerRef,msg->sessionId,msg->connectionId,&statusCode); + break; + + // We have been asked to re-negotiate parameters for this connection + // (this is currently unsupported and we logout) + case kiSCSIPDUAsyncMsgNegotiateParams: + iSCSISessionRemoveConnection(managerRef,msg->sessionId,msg->connectionId,&statusCode); + break; + + default: + break; + } +} + +static void iSCSIHBANotificationHandler(iSCSIHBAInterfaceRef interface, + enum iSCSIHBANotificationTypes type, + iSCSIHBANotificationMessage * msg,void * info) +{ + iSCSISessionManagerRef managerRef = (iSCSISessionManagerRef)info; + + // Process an asynchronous message + switch(type) + { + // The kernel received an iSCSI asynchronous event message + case kiSCSIHBANotificationAsyncMessage: + iSCSIHBANotificationAsyncMessageHandler(managerRef,(iSCSIHBANotificationAsyncMessage *)msg); + break; + case kiSCSIHBANotificationTimeout: + iSCSIHBANotificationTimeoutMessageHandler(managerRef,(iSCSIHBANotificationMessage *)msg); + break; + case kiSCSIHBANotificationTerminate: break; + default: break; + }; +} + +/*! Call to initialize iSCSI session management functions. This function will + * initialize the kernel layer after which other session-related functions + * may be called. + * @param rl the runloop to use for executing session-related functions. + * @return an error code indicating the result of the operation. */ +iSCSISessionManagerRef iSCSISessionManagerCreate(CFAllocatorRef allocator, + iSCSISessionManagerCallBacks callbacks) +{ + iSCSISessionManagerRef managerRef = CFAllocatorAllocate(allocator,sizeof(struct __iSCSISessionManager),0); + + iSCSIHBANotificationContext notifyContext; + notifyContext.version = 0; + notifyContext.info = managerRef; + notifyContext.release = 0; + notifyContext.retain = 0; + notifyContext.copyDescription = 0; + + iSCSIHBAInterfaceRef interface = iSCSIHBAInterfaceCreate(allocator,iSCSIHBANotificationHandler,¬ifyContext); + + managerRef->allocator = allocator; + managerRef->hbaInterface = interface; + managerRef->callbacks = callbacks; + managerRef->initiatorName = kiSCSIInitiatorIQN; + managerRef->initiatorAlias = kiSCSIInitiatorAlias; + + return managerRef; +} + +/*! Called to cleanup kernel resources used by the iSCSI session management + * functions. This function will close any connections to the kernel + * and stop processing messages related to the kernel. + * @param managerRef an instance of an iSCSISessionManagerRef. */ +void iSCSISessionManagerRelease(iSCSISessionManagerRef managerRef) +{ + CFAllocatorDeallocate(managerRef->allocator,managerRef); +} + +/*! Creates a runloop source used to run the callback functions associated + * with the session manager reference. */ +/*! Schedules execution of various tasks, including handling of kernel notifications + * on for the specified interface instance over the designated runloop. + * @param managerRef an instance of an iSCSISessionManagerRef. + * @param runLoop the runloop to schedule + * @param runLoopMode the execution mode for the runLoop. */ +void iSCSISessionManagerScheduleWithRunLoop(iSCSISessionManagerRef managerRef, + CFRunLoopRef runLoop, + CFStringRef runLoopMode) +{ + iSCSIHBAInterfaceScheduleWithRunloop(managerRef->hbaInterface,runLoop,runLoopMode); +} + +/*! Unschedules execution of various tasks, including handling of session notifications + * on for the specified interface instance over the designated runloop. + * @param managerRef an instance of an iSCSISessionManagerRef. + * @param runLoop the runloop to schedule + * @param runLoopMode the execution mode for the runLoop. */ +void iSCSISessionManagerUnscheduleWithRunloop(iSCSISessionManagerRef managerRef, + CFRunLoopRef runLoop, + CFStringRef runLoopMode) +{ + iSCSIHBAInterfaceScheduleWithRunloop(managerRef->hbaInterface,runLoop,runLoopMode); +} + +/*! Returns a reference to the underlying HBA interface instance. + * @param managerRef an instance of an iSCSISessionManagerRef. + * @return a reference to the underlying HBA interface instance. */ +iSCSIHBAInterfaceRef iSCSISessionManagerGetHBAInterface(iSCSISessionManagerRef managerRef) +{ + return managerRef->hbaInterface; +} + +/*! Sets the initiator name to use for new sessions. This is the IQN-format name that is + * exchanged with a target during negotiation. + * @param managerRef an instance of an iSCSISessionManagerRef. + * @param initiatorIQN the initiator name. */ +void iSCSISessionManagerSetInitiatorName(iSCSISessionManagerRef managerRef, + CFStringRef initiatorIQN) +{ + CFRelease(managerRef->initiatorName); + managerRef->initiatorName = CFStringCreateCopy(kCFAllocatorDefault,initiatorIQN); +} + +/*! Sets the initiator alias to use for new sessions. This is the IQN-format alias that is + * exchanged with a target during negotiation. + * @param managerRef an instance of an iSCSISessionManagerRef. + * @param initiatorAlias the initiator alias. */ +void iSCSISessionManagerSetInitiatorAlias(iSCSISessionManagerRef managerRef, + CFStringRef initiatorAlias) +{ + CFRelease(managerRef->initiatorAlias); + managerRef->initiatorAlias = CFStringCreateCopy(kCFAllocatorDefault,initiatorAlias); +} diff --git a/Source/User/iscsid/iSCSISessionManager.h b/Source/User/iscsid/iSCSISessionManager.h new file mode 100644 index 00000000..16dd1122 --- /dev/null +++ b/Source/User/iscsid/iSCSISessionManager.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2016, Nareg Sinenian + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __ISCSI_SESSION_MANAGER_H__ +#define __ISCSI_SESSION_MANAGER_H__ + +#include +#include "iSCSI.h" +#include "iSCSIHBAInterface.h" + +/*! Opaque session manager reference. */ +typedef struct __iSCSISessionManager * iSCSISessionManagerRef; + +/*! Callback function called when a session or connection timeout occurs. */ +typedef void (*iSCSISessionTimeoutCallback)(iSCSITargetRef target,iSCSIPortalRef portal); + +/*! Callback types used by the session manager. */ +typedef struct iSCSISessionManagerCallBacks +{ + iSCSISessionTimeoutCallback timeoutCallback; + +} iSCSISessionManagerCallBacks; + +/*! Call to initialize iSCSI session management functions. This function will + * initialize the kernel layer after which other session-related functions + * may be called. + * @param rl the runloop to use for executing session-related functions. + * @return an error code indicating the result of the operation. */ +iSCSISessionManagerRef iSCSISessionManagerCreate(CFAllocatorRef allocator, + iSCSISessionManagerCallBacks callbacks); + +/*! Called to cleanup kernel resources used by the iSCSI session management + * functions. This function will close any connections to the kernel + * and stop processing messages related to the kernel. + * @param sessionManager an instance of an iSCSISessionManagerRef. */ +void iSCSISessionManagerRelease(iSCSISessionManagerRef managerRef); + +/*! Creates a runloop source used to run the callback functions associated + * with the session manager reference. */ +/*! Schedules execution of various tasks, including handling of kernel notifications + * on for the specified interface instance over the designated runloop. + * @param managerRef an instance of an iSCSISessionManagerRef. + * @param runLoop the runloop to schedule + * @param runLoopMode the execution mode for the runLoop. */ +void iSCSISessionManagerScheduleWithRunLoop(iSCSISessionManagerRef managerRef, + CFRunLoopRef runLoop, + CFStringRef runLoopMode); + +/*! Unschedules execution of various tasks, including handling of session notifications + * on for the specified interface instance over the designated runloop. + * @param managerRef an instance of an iSCSISessionManagerRef. + * @param runLoop the runloop to schedule + * @param runLoopMode the execution mode for the runLoop. */ +void iSCSISessionManagerUnscheduleWithRunloop(iSCSISessionManagerRef managerRef, + CFRunLoopRef runLoop, + CFStringRef runLoopMode); + +/*! Returns a reference to the underlying HBA interface instance. + * @param managerRef an instance of an iSCSISessionManagerRef. + * @return a reference to the underlying HBA interface instance. */ +iSCSIHBAInterfaceRef iSCSISessionManagerGetHBAInterface(iSCSISessionManagerRef managerRef); + +/*! Sets the initiator name to use for new sessions. This is the IQN-format name that is + * exchanged with a target during negotiation. + * @param managerRef an instance of an iSCSISessionManagerRef. + * @param initiatorIQN the initiator name. */ +void iSCSISessionManagerSetInitiatorName(iSCSISessionManagerRef managerRef, + CFStringRef initiatorIQN); + +/*! Sets the initiator aliias to use for new sessions. This is the IQN-format alias that is + * exchanged with a target during negotiation. + * @param managerRef an instance of an iSCSISessionManagerRef. + * @param initiatorAlias the initiator alias. */ +void iSCSISessionManagerSetInitiatorAlias(iSCSISessionManagerRef managerRef, + CFStringRef initiatorAlias); + + +#endif diff --git a/iSCSIInitiator.xcodeproj/project.pbxproj b/iSCSIInitiator.xcodeproj/project.pbxproj index e97979c5..8179496f 100644 --- a/iSCSIInitiator.xcodeproj/project.pbxproj +++ b/iSCSIInitiator.xcodeproj/project.pbxproj @@ -9,10 +9,11 @@ /* Begin PBXBuildFile section */ 2B171E421CE23677003C3E08 /* iSCSIAuthRights.c in Sources */ = {isa = PBXBuildFile; fileRef = 2B171E401CE23677003C3E08 /* iSCSIAuthRights.c */; }; 2B171E431CE23677003C3E08 /* iSCSIAuthRights.h in Headers */ = {isa = PBXBuildFile; fileRef = 2B171E411CE23677003C3E08 /* iSCSIAuthRights.h */; }; - 2B1EDC121CFAEC1F007BAB99 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B1EDC111CFAEC1F007BAB99 /* AppDelegate.m */; }; - 2B1EDC151CFAEC1F007BAB99 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B1EDC141CFAEC1F007BAB99 /* main.m */; }; - 2B1EDC1A1CFAEC1F007BAB99 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2B1EDC191CFAEC1F007BAB99 /* Assets.xcassets */; }; - 2B1EDC1D1CFAEC1F007BAB99 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2B1EDC1B1CFAEC1F007BAB99 /* Main.storyboard */; }; + 2B180B861D3B752A005EFF14 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B180B851D3B752A005EFF14 /* SystemConfiguration.framework */; }; + 2B1A490F1D3D40FE00D3ED0D /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 2B1A490E1D3D40FE00D3ED0D /* main.c */; }; + 2B1A49131D3D412700D3ED0D /* iSCSI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B7A0B741C8AEC47008290E9 /* iSCSI.framework */; }; + 2B1A49141D3D416000D3ED0D /* iSCSIPDUUser.c in Sources */ = {isa = PBXBuildFile; fileRef = 2BDE5E331C8B0281004BDB5F /* iSCSIPDUUser.c */; }; + 2B1A49151D3D416800D3ED0D /* iSCSIHBAInterface.c in Sources */ = {isa = PBXBuildFile; fileRef = 2BDE5E311C8B0281004BDB5F /* iSCSIHBAInterface.c */; }; 2B459D551B7B8192008F656F /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B459D541B7B8192008F656F /* Security.framework */; }; 2B4A60371A3E6F690006AFCC /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B5935C21810DF7100FFC3D3 /* IOKit.framework */; }; 2B4A60381A3E6F830006AFCC /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B9CAB9A185527EC00F3A9C6 /* CoreFoundation.framework */; }; @@ -21,18 +22,14 @@ 2B5C213C1C70871A00ED8791 /* DiskArbitration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2BC4CBB11AA55046003611F7 /* DiskArbitration.framework */; }; 2B5C213D1C70872400ED8791 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B459D541B7B8192008F656F /* Security.framework */; }; 2B5C213E1C70872E00ED8791 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B9CAB9A185527EC00F3A9C6 /* CoreFoundation.framework */; }; - 2B7762991CFF220D003093CC /* NodeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B7762921CFEF9E7003093CC /* NodeViewController.m */; }; - 2B77629A1CFF2211003093CC /* Node.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B7762941CFEF9E7003093CC /* Node.m */; }; - 2B77629B1D00B388003093CC /* iSCSI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B7A0B741C8AEC47008290E9 /* iSCSI.framework */; }; - 2B77629E1D02CC9D003093CC /* InitiatorViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B77629D1D02CC9D003093CC /* InitiatorViewController.m */; }; + 2B6BCCB91D3A7857003522BC /* iSCSISessionManager.c in Sources */ = {isa = PBXBuildFile; fileRef = 2B6BCCB61D354EA0003522BC /* iSCSISessionManager.c */; }; 2B9E3C941C493BAA00440116 /* iSCSIInitiator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2B9E3C711C493B9C00440116 /* iSCSIInitiator.cpp */; settings = {COMPILER_FLAGS = "-Wno-inconsistent-missing-override"; }; }; - 2B9E3C961C493BAA00440116 /* iSCSIInitiatorClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2B9E3C731C493B9C00440116 /* iSCSIInitiatorClient.cpp */; settings = {COMPILER_FLAGS = "-Wno-inconsistent-missing-override"; }; }; + 2B9E3C961C493BAA00440116 /* iSCSIHBAUserClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2B9E3C731C493B9C00440116 /* iSCSIHBAUserClient.cpp */; settings = {COMPILER_FLAGS = "-Wno-inconsistent-missing-override"; }; }; 2B9E3C981C493BAA00440116 /* iSCSIIOEventSource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2B9E3C751C493B9C00440116 /* iSCSIIOEventSource.cpp */; settings = {COMPILER_FLAGS = "-Wno-inconsistent-missing-override"; }; }; 2B9E3C9C1C493BAA00440116 /* iSCSIPDUKernel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2B9E3C791C493B9C00440116 /* iSCSIPDUKernel.cpp */; settings = {COMPILER_FLAGS = "-Wno-inconsistent-missing-override"; }; }; 2B9E3CA01C493BAA00440116 /* iSCSITaskQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2B9E3C7D1C493B9C00440116 /* iSCSITaskQueue.cpp */; settings = {COMPILER_FLAGS = "-Wno-inconsistent-missing-override"; }; }; 2B9E3CA31C493BAA00440116 /* iSCSIVirtualHBA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2B9E3C801C493B9C00440116 /* iSCSIVirtualHBA.cpp */; settings = {COMPILER_FLAGS = "-Wno-inconsistent-missing-override"; }; }; 2B9E3CBE1C49ED0000440116 /* crc32c.c in Sources */ = {isa = PBXBuildFile; fileRef = 2B9E3CBA1C49ECF900440116 /* crc32c.c */; }; - 2BAAB8B71D03E8EF00C01C79 /* SplitViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2BAAB8B61D03E8EF00C01C79 /* SplitViewController.m */; }; 2BC4CBB21AA55046003611F7 /* DiskArbitration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2BC4CBB11AA55046003611F7 /* DiskArbitration.framework */; }; 2BDE5E281C8B0274004BDB5F /* iscsictl.8 in Resources */ = {isa = PBXBuildFile; fileRef = 2BDE5E261C8B0274004BDB5F /* iscsictl.8 */; }; 2BDE5E6E1C8B0292004BDB5F /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 2BDE5E481C8B028B004BDB5F /* Info.plist */; }; @@ -57,7 +54,7 @@ 2BDE5E881C8B3E7D004BDB5F /* iSCSIAuth.c in Sources */ = {isa = PBXBuildFile; fileRef = 2BDE5E2B1C8B0281004BDB5F /* iSCSIAuth.c */; }; 2BDE5E891C8B3E7D004BDB5F /* iSCSIDaemon.c in Sources */ = {isa = PBXBuildFile; fileRef = 2BDE5E2E1C8B0281004BDB5F /* iSCSIDaemon.c */; }; 2BDE5E8A1C8B3E7D004BDB5F /* iSCSIDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = 2BDE5E2F1C8B0281004BDB5F /* iSCSIDiscovery.c */; }; - 2BDE5E8B1C8B3E7D004BDB5F /* iSCSIKernelInterface.c in Sources */ = {isa = PBXBuildFile; fileRef = 2BDE5E311C8B0281004BDB5F /* iSCSIKernelInterface.c */; }; + 2BDE5E8B1C8B3E7D004BDB5F /* iSCSIHBAInterface.c in Sources */ = {isa = PBXBuildFile; fileRef = 2BDE5E311C8B0281004BDB5F /* iSCSIHBAInterface.c */; }; 2BDE5E8C1C8B3E7D004BDB5F /* iSCSIPDUUser.c in Sources */ = {isa = PBXBuildFile; fileRef = 2BDE5E331C8B0281004BDB5F /* iSCSIPDUUser.c */; }; 2BDE5E8D1C8B3E7D004BDB5F /* iSCSIQueryTarget.c in Sources */ = {isa = PBXBuildFile; fileRef = 2BDE5E351C8B0281004BDB5F /* iSCSIQueryTarget.c */; }; 2BDE5E8E1C8B3E7D004BDB5F /* iSCSISession.c in Sources */ = {isa = PBXBuildFile; fileRef = 2BDE5E371C8B0281004BDB5F /* iSCSISession.c */; }; @@ -80,6 +77,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 2B1A490A1D3D40FE00D3ED0D /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; 2BA96D9318D4F60200F135E3 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 12; @@ -96,14 +102,10 @@ /* Begin PBXFileReference section */ 2B171E401CE23677003C3E08 /* iSCSIAuthRights.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = iSCSIAuthRights.c; path = "Source/User/iSCSI Framework/iSCSIAuthRights.c"; sourceTree = ""; }; 2B171E411CE23677003C3E08 /* iSCSIAuthRights.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iSCSIAuthRights.h; path = "Source/User/iSCSI Framework/iSCSIAuthRights.h"; sourceTree = ""; }; + 2B180B851D3B752A005EFF14 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; + 2B1A490C1D3D40FE00D3ED0D /* iSCSIHBATest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = iSCSIHBATest; sourceTree = BUILT_PRODUCTS_DIR; }; + 2B1A490E1D3D40FE00D3ED0D /* main.c */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.objc; path = main.c; sourceTree = ""; }; 2B1B972A1C4937B400DA5281 /* Scripts */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Scripts; sourceTree = ""; }; - 2B1EDC0E1CFAEC1F007BAB99 /* iSCSI Utility.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "iSCSI Utility.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - 2B1EDC101CFAEC1F007BAB99 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; - 2B1EDC111CFAEC1F007BAB99 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - 2B1EDC141CFAEC1F007BAB99 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 2B1EDC191CFAEC1F007BAB99 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 2B1EDC1C1CFAEC1F007BAB99 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 2B1EDC1E1CFAEC1F007BAB99 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 2B20904F18C6E43C00C43190 /* libsystem_kernel.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsystem_kernel.dylib; path = usr/lib/system/libsystem_kernel.dylib; sourceTree = SDKROOT; }; 2B459D541B7B8192008F656F /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 2B56ACEC1C4A48C800930E79 /* Distribution */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Distribution; sourceTree = ""; }; @@ -114,24 +116,20 @@ 2B59357F1810D3C800FFC3D3 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; 2B5935801810D3C800FFC3D3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 2B5935C21810DF7100FFC3D3 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; - 2B7762911CFEF9E7003093CC /* NodeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NodeViewController.h; sourceTree = ""; }; - 2B7762921CFEF9E7003093CC /* NodeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NodeViewController.m; sourceTree = ""; }; - 2B7762931CFEF9E7003093CC /* Node.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Node.h; sourceTree = ""; }; - 2B7762941CFEF9E7003093CC /* Node.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Node.m; sourceTree = ""; }; - 2B77629C1D02CC9D003093CC /* InitiatorViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InitiatorViewController.h; sourceTree = ""; }; - 2B77629D1D02CC9D003093CC /* InitiatorViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InitiatorViewController.m; sourceTree = ""; }; + 2B6BCCB61D354EA0003522BC /* iSCSISessionManager.c */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.objc; fileEncoding = 4; name = iSCSISessionManager.c; path = Source/User/iscsid/iSCSISessionManager.c; sourceTree = ""; }; + 2B6BCCB71D354EA0003522BC /* iSCSISessionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iSCSISessionManager.h; path = Source/User/iscsid/iSCSISessionManager.h; sourceTree = ""; }; 2B7A0B741C8AEC47008290E9 /* iSCSI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = iSCSI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 2B84883C1BEBCE0E0038DC53 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 2B9CAB9A185527EC00F3A9C6 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; 2B9E3C701C493B9C00440116 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Source/Kernel/Info.plist; sourceTree = ""; }; 2B9E3C711C493B9C00440116 /* iSCSIInitiator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = iSCSIInitiator.cpp; path = Source/Kernel/iSCSIInitiator.cpp; sourceTree = ""; }; 2B9E3C721C493B9C00440116 /* iSCSIInitiator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iSCSIInitiator.h; path = Source/Kernel/iSCSIInitiator.h; sourceTree = ""; }; - 2B9E3C731C493B9C00440116 /* iSCSIInitiatorClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = iSCSIInitiatorClient.cpp; path = Source/Kernel/iSCSIInitiatorClient.cpp; sourceTree = ""; }; - 2B9E3C741C493B9C00440116 /* iSCSIInitiatorClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iSCSIInitiatorClient.h; path = Source/Kernel/iSCSIInitiatorClient.h; sourceTree = ""; }; + 2B9E3C731C493B9C00440116 /* iSCSIHBAUserClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = iSCSIHBAUserClient.cpp; path = Source/Kernel/iSCSIHBAUserClient.cpp; sourceTree = ""; }; + 2B9E3C741C493B9C00440116 /* iSCSIHBAUserClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iSCSIHBAUserClient.h; path = Source/Kernel/iSCSIHBAUserClient.h; sourceTree = ""; }; 2B9E3C751C493B9C00440116 /* iSCSIIOEventSource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = iSCSIIOEventSource.cpp; path = Source/Kernel/iSCSIIOEventSource.cpp; sourceTree = ""; }; 2B9E3C761C493B9C00440116 /* iSCSIIOEventSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iSCSIIOEventSource.h; path = Source/Kernel/iSCSIIOEventSource.h; sourceTree = ""; }; 2B9E3C771C493B9C00440116 /* iSCSIKernelClasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iSCSIKernelClasses.h; path = Source/Kernel/iSCSIKernelClasses.h; sourceTree = ""; }; - 2B9E3C781C493B9C00440116 /* iSCSIKernelInterfaceShared.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iSCSIKernelInterfaceShared.h; path = Source/Kernel/iSCSIKernelInterfaceShared.h; sourceTree = ""; }; + 2B9E3C781C493B9C00440116 /* iSCSIHBATypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iSCSIHBATypes.h; path = Source/Kernel/iSCSIHBATypes.h; sourceTree = ""; }; 2B9E3C791C493B9C00440116 /* iSCSIPDUKernel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = iSCSIPDUKernel.cpp; path = Source/Kernel/iSCSIPDUKernel.cpp; sourceTree = ""; }; 2B9E3C7A1C493B9C00440116 /* iSCSIPDUKernel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iSCSIPDUKernel.h; path = Source/Kernel/iSCSIPDUKernel.h; sourceTree = ""; }; 2B9E3C7B1C493B9C00440116 /* iSCSIPDUShared.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iSCSIPDUShared.h; path = Source/Kernel/iSCSIPDUShared.h; sourceTree = ""; }; @@ -144,29 +142,23 @@ 2B9E3C821C493B9C00440116 /* Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Prefix.pch; path = Source/Kernel/Prefix.pch; sourceTree = ""; }; 2B9E3CBA1C49ECF900440116 /* crc32c.c */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = crc32c.c; path = Source/Kernel/crc32c.c; sourceTree = ""; }; 2B9E3CBB1C49ECF900440116 /* crc32c.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; name = crc32c.h; path = Source/Kernel/crc32c.h; sourceTree = ""; }; - 2BAAB8B51D03E8EF00C01C79 /* SplitViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SplitViewController.h; sourceTree = ""; }; - 2BAAB8B61D03E8EF00C01C79 /* SplitViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SplitViewController.m; sourceTree = ""; }; - 2BB321F31D087AAA0030DB94 /* libcurses.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libcurses.tbd; path = usr/lib/libcurses.tbd; sourceTree = SDKROOT; }; - 2BB321F41D087AAA0030DB94 /* libncurses.5.4.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libncurses.5.4.tbd; path = usr/lib/libncurses.5.4.tbd; sourceTree = SDKROOT; }; - 2BB321F51D087AAA0030DB94 /* libncurses.5.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libncurses.5.tbd; path = usr/lib/libncurses.5.tbd; sourceTree = SDKROOT; }; - 2BB321F61D087AAA0030DB94 /* libncurses.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libncurses.tbd; path = usr/lib/libncurses.tbd; sourceTree = SDKROOT; }; 2BC4CBB11AA55046003611F7 /* DiskArbitration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DiskArbitration.framework; path = System/Library/Frameworks/DiskArbitration.framework; sourceTree = SDKROOT; }; 2BDE5E261C8B0274004BDB5F /* iscsictl.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = iscsictl.8; path = Source/User/iscsictl/iscsictl.8; sourceTree = ""; }; 2BDE5E271C8B0274004BDB5F /* iSCSICtl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = iSCSICtl.m; path = Source/User/iscsictl/iSCSICtl.m; sourceTree = ""; }; 2BDE5E2A1C8B0281004BDB5F /* com.github.iscsi-osx.iscsid.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "com.github.iscsi-osx.iscsid.plist"; path = "Source/User/iscsid/com.github.iscsi-osx.iscsid.plist"; sourceTree = ""; }; - 2BDE5E2B1C8B0281004BDB5F /* iSCSIAuth.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = iSCSIAuth.c; path = Source/User/iscsid/iSCSIAuth.c; sourceTree = ""; }; + 2BDE5E2B1C8B0281004BDB5F /* iSCSIAuth.c */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.objc; fileEncoding = 4; name = iSCSIAuth.c; path = Source/User/iscsid/iSCSIAuth.c; sourceTree = ""; }; 2BDE5E2C1C8B0281004BDB5F /* iSCSIAuth.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iSCSIAuth.h; path = Source/User/iscsid/iSCSIAuth.h; sourceTree = ""; }; 2BDE5E2D1C8B0281004BDB5F /* iscsid.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = iscsid.8; path = Source/User/iscsid/iscsid.8; sourceTree = ""; }; - 2BDE5E2E1C8B0281004BDB5F /* iSCSIDaemon.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = iSCSIDaemon.c; path = Source/User/iscsid/iSCSIDaemon.c; sourceTree = ""; }; - 2BDE5E2F1C8B0281004BDB5F /* iSCSIDiscovery.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = iSCSIDiscovery.c; path = Source/User/iscsid/iSCSIDiscovery.c; sourceTree = ""; }; + 2BDE5E2E1C8B0281004BDB5F /* iSCSIDaemon.c */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.objc; fileEncoding = 4; name = iSCSIDaemon.c; path = Source/User/iscsid/iSCSIDaemon.c; sourceTree = ""; }; + 2BDE5E2F1C8B0281004BDB5F /* iSCSIDiscovery.c */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.objc; fileEncoding = 4; name = iSCSIDiscovery.c; path = Source/User/iscsid/iSCSIDiscovery.c; sourceTree = ""; }; 2BDE5E301C8B0281004BDB5F /* iSCSIDiscovery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iSCSIDiscovery.h; path = Source/User/iscsid/iSCSIDiscovery.h; sourceTree = ""; }; - 2BDE5E311C8B0281004BDB5F /* iSCSIKernelInterface.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = iSCSIKernelInterface.c; path = Source/User/iscsid/iSCSIKernelInterface.c; sourceTree = ""; }; - 2BDE5E321C8B0281004BDB5F /* iSCSIKernelInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iSCSIKernelInterface.h; path = Source/User/iscsid/iSCSIKernelInterface.h; sourceTree = ""; }; - 2BDE5E331C8B0281004BDB5F /* iSCSIPDUUser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = iSCSIPDUUser.c; path = Source/User/iscsid/iSCSIPDUUser.c; sourceTree = ""; }; + 2BDE5E311C8B0281004BDB5F /* iSCSIHBAInterface.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = iSCSIHBAInterface.c; path = Source/User/iscsid/iSCSIHBAInterface.c; sourceTree = ""; }; + 2BDE5E321C8B0281004BDB5F /* iSCSIHBAInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iSCSIHBAInterface.h; path = Source/User/iscsid/iSCSIHBAInterface.h; sourceTree = ""; }; + 2BDE5E331C8B0281004BDB5F /* iSCSIPDUUser.c */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.objc; fileEncoding = 4; name = iSCSIPDUUser.c; path = Source/User/iscsid/iSCSIPDUUser.c; sourceTree = ""; }; 2BDE5E341C8B0281004BDB5F /* iSCSIPDUUser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iSCSIPDUUser.h; path = Source/User/iscsid/iSCSIPDUUser.h; sourceTree = ""; }; - 2BDE5E351C8B0281004BDB5F /* iSCSIQueryTarget.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = iSCSIQueryTarget.c; path = Source/User/iscsid/iSCSIQueryTarget.c; sourceTree = ""; }; + 2BDE5E351C8B0281004BDB5F /* iSCSIQueryTarget.c */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.objc; fileEncoding = 4; name = iSCSIQueryTarget.c; path = Source/User/iscsid/iSCSIQueryTarget.c; sourceTree = ""; }; 2BDE5E361C8B0281004BDB5F /* iSCSIQueryTarget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iSCSIQueryTarget.h; path = Source/User/iscsid/iSCSIQueryTarget.h; sourceTree = ""; }; - 2BDE5E371C8B0281004BDB5F /* iSCSISession.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = iSCSISession.c; path = Source/User/iscsid/iSCSISession.c; sourceTree = ""; }; + 2BDE5E371C8B0281004BDB5F /* iSCSISession.c */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.objc; fileEncoding = 4; name = iSCSISession.c; path = Source/User/iscsid/iSCSISession.c; sourceTree = ""; }; 2BDE5E381C8B0281004BDB5F /* iSCSISession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iSCSISession.h; path = Source/User/iscsid/iSCSISession.h; sourceTree = ""; }; 2BDE5E481C8B028B004BDB5F /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = "Source/User/iSCSI Framework/Info.plist"; sourceTree = ""; }; 2BDE5E491C8B028B004BDB5F /* iSCSI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iSCSI.h; path = "Source/User/iSCSI Framework/iSCSI.h"; sourceTree = ""; }; @@ -205,11 +197,11 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 2B1EDC0B1CFAEC1F007BAB99 /* Frameworks */ = { + 2B1A49091D3D40FE00D3ED0D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 2B77629B1D00B388003093CC /* iSCSI.framework in Frameworks */, + 2B1A49131D3D412700D3ED0D /* iSCSI.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -231,6 +223,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 2B180B861D3B752A005EFF14 /* SystemConfiguration.framework in Frameworks */, 2BDE5E901C8B3ED0004BDB5F /* iSCSI.framework in Frameworks */, 2B459D551B7B8192008F656F /* Security.framework in Frameworks */, 2BC4CBB21AA55046003611F7 /* DiskArbitration.framework in Frameworks */, @@ -242,33 +235,12 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 2B1EDC0F1CFAEC1F007BAB99 /* iSCSI Utility */ = { - isa = PBXGroup; - children = ( - 2B7762911CFEF9E7003093CC /* NodeViewController.h */, - 2B7762921CFEF9E7003093CC /* NodeViewController.m */, - 2B77629C1D02CC9D003093CC /* InitiatorViewController.h */, - 2B77629D1D02CC9D003093CC /* InitiatorViewController.m */, - 2BAAB8B51D03E8EF00C01C79 /* SplitViewController.h */, - 2BAAB8B61D03E8EF00C01C79 /* SplitViewController.m */, - 2B7762931CFEF9E7003093CC /* Node.h */, - 2B7762941CFEF9E7003093CC /* Node.m */, - 2B1EDC101CFAEC1F007BAB99 /* AppDelegate.h */, - 2B1EDC111CFAEC1F007BAB99 /* AppDelegate.m */, - 2B1EDC191CFAEC1F007BAB99 /* Assets.xcassets */, - 2B1EDC1B1CFAEC1F007BAB99 /* Main.storyboard */, - 2B1EDC1E1CFAEC1F007BAB99 /* Info.plist */, - 2B1EDC131CFAEC1F007BAB99 /* Supporting Files */, - ); - path = "iSCSI Utility"; - sourceTree = ""; - }; - 2B1EDC131CFAEC1F007BAB99 /* Supporting Files */ = { + 2B1A490D1D3D40FE00D3ED0D /* iSCSIHBATest */ = { isa = PBXGroup; children = ( - 2B1EDC141CFAEC1F007BAB99 /* main.m */, + 2B1A490E1D3D40FE00D3ED0D /* main.c */, ); - name = "Supporting Files"; + path = iSCSIHBATest; sourceTree = ""; }; 2B5935541810CE6800FFC3D3 = { @@ -279,22 +251,20 @@ 2B1B972A1C4937B400DA5281 /* Scripts */, 2B56ACEC1C4A48C800930E79 /* Distribution */, 2B84883C1BEBCE0E0038DC53 /* README.md */, + 2B1A490D1D3D40FE00D3ED0D /* iSCSIHBATest */, 2B5935631810D38E00FFC3D3 /* Frameworks */, 2BDEA9401A715C7B00D5B48B /* iSCSIInitiator.kext */, 2BDEA9411A715C7B00D5B48B /* iscsid */, 2BDEA9421A715C7B00D5B48B /* iscsictl */, 2B7A0B741C8AEC47008290E9 /* iSCSI.framework */, - 2B1EDC0E1CFAEC1F007BAB99 /* iSCSI Utility.app */, + 2B1A490C1D3D40FE00D3ED0D /* iSCSIHBATest */, ); sourceTree = ""; }; 2B5935631810D38E00FFC3D3 /* Frameworks */ = { isa = PBXGroup; children = ( - 2BB321F31D087AAA0030DB94 /* libcurses.tbd */, - 2BB321F41D087AAA0030DB94 /* libncurses.5.4.tbd */, - 2BB321F51D087AAA0030DB94 /* libncurses.5.tbd */, - 2BB321F61D087AAA0030DB94 /* libncurses.tbd */, + 2B180B851D3B752A005EFF14 /* SystemConfiguration.framework */, 2B459D541B7B8192008F656F /* Security.framework */, 2BC4CBB11AA55046003611F7 /* DiskArbitration.framework */, 2B20904F18C6E43C00C43190 /* libsystem_kernel.dylib */, @@ -326,12 +296,12 @@ 2B9E3CBB1C49ECF900440116 /* crc32c.h */, 2B9E3C711C493B9C00440116 /* iSCSIInitiator.cpp */, 2B9E3C721C493B9C00440116 /* iSCSIInitiator.h */, - 2B9E3C731C493B9C00440116 /* iSCSIInitiatorClient.cpp */, - 2B9E3C741C493B9C00440116 /* iSCSIInitiatorClient.h */, + 2B9E3C731C493B9C00440116 /* iSCSIHBAUserClient.cpp */, + 2B9E3C741C493B9C00440116 /* iSCSIHBAUserClient.h */, 2B9E3C751C493B9C00440116 /* iSCSIIOEventSource.cpp */, 2B9E3C761C493B9C00440116 /* iSCSIIOEventSource.h */, 2B9E3C771C493B9C00440116 /* iSCSIKernelClasses.h */, - 2B9E3C781C493B9C00440116 /* iSCSIKernelInterfaceShared.h */, + 2B9E3C781C493B9C00440116 /* iSCSIHBATypes.h */, 2B9E3C791C493B9C00440116 /* iSCSIPDUKernel.cpp */, 2B9E3C7A1C493B9C00440116 /* iSCSIPDUKernel.h */, 2B9E3C7B1C493B9C00440116 /* iSCSIPDUShared.h */, @@ -357,7 +327,6 @@ 2BDE5E1A1C8B020A004BDB5F /* User */ = { isa = PBXGroup; children = ( - 2B1EDC0F1CFAEC1F007BAB99 /* iSCSI Utility */, 2BDE5E231C8B0255004BDB5F /* iscsictl */, 2BDE5E221C8B024A004BDB5F /* iscsid */, 2BDE5E211C8B0241004BDB5F /* iSCSI Framwork */, @@ -403,14 +372,16 @@ 2BDE5E2E1C8B0281004BDB5F /* iSCSIDaemon.c */, 2BDE5E2F1C8B0281004BDB5F /* iSCSIDiscovery.c */, 2BDE5E301C8B0281004BDB5F /* iSCSIDiscovery.h */, - 2BDE5E311C8B0281004BDB5F /* iSCSIKernelInterface.c */, - 2BDE5E321C8B0281004BDB5F /* iSCSIKernelInterface.h */, + 2BDE5E311C8B0281004BDB5F /* iSCSIHBAInterface.c */, + 2BDE5E321C8B0281004BDB5F /* iSCSIHBAInterface.h */, 2BDE5E331C8B0281004BDB5F /* iSCSIPDUUser.c */, 2BDE5E341C8B0281004BDB5F /* iSCSIPDUUser.h */, 2BDE5E351C8B0281004BDB5F /* iSCSIQueryTarget.c */, 2BDE5E361C8B0281004BDB5F /* iSCSIQueryTarget.h */, 2BDE5E371C8B0281004BDB5F /* iSCSISession.c */, 2BDE5E381C8B0281004BDB5F /* iSCSISession.h */, + 2B6BCCB61D354EA0003522BC /* iSCSISessionManager.c */, + 2B6BCCB71D354EA0003522BC /* iSCSISessionManager.h */, ); name = iscsid; sourceTree = ""; @@ -473,22 +444,22 @@ productReference = 2BDEA9421A715C7B00D5B48B /* iscsictl */; productType = "com.apple.product-type.tool"; }; - 2B1EDC0D1CFAEC1F007BAB99 /* iSCSI Utility */ = { + 2B1A490B1D3D40FE00D3ED0D /* iSCSIHBATest */ = { isa = PBXNativeTarget; - buildConfigurationList = 2B1EDC1F1CFAEC1F007BAB99 /* Build configuration list for PBXNativeTarget "iSCSI Utility" */; + buildConfigurationList = 2B1A49101D3D40FE00D3ED0D /* Build configuration list for PBXNativeTarget "iSCSIHBATest" */; buildPhases = ( - 2B1EDC0A1CFAEC1F007BAB99 /* Sources */, - 2B1EDC0B1CFAEC1F007BAB99 /* Frameworks */, - 2B1EDC0C1CFAEC1F007BAB99 /* Resources */, + 2B1A49081D3D40FE00D3ED0D /* Sources */, + 2B1A49091D3D40FE00D3ED0D /* Frameworks */, + 2B1A490A1D3D40FE00D3ED0D /* CopyFiles */, ); buildRules = ( ); dependencies = ( ); - name = "iSCSI Utility"; - productName = "iSCSI Utility"; - productReference = 2B1EDC0E1CFAEC1F007BAB99 /* iSCSI Utility.app */; - productType = "com.apple.product-type.application"; + name = iSCSIHBATest; + productName = iSCSIHBATest; + productReference = 2B1A490C1D3D40FE00D3ED0D /* iSCSIHBATest */; + productType = "com.apple.product-type.tool"; }; 2B7A0B731C8AEC47008290E9 /* iSCSI */ = { isa = PBXNativeTarget; @@ -553,7 +524,7 @@ LastSwiftUpdateCheck = 0730; LastUpgradeCheck = 0700; TargetAttributes = { - 2B1EDC0D1CFAEC1F007BAB99 = { + 2B1A490B1D3D40FE00D3ED0D = { CreatedOnToolsVersion = 7.3.1; }; 2B7A0B731C8AEC47008290E9 = { @@ -578,21 +549,12 @@ 2BA96D8818D4F60200F135E3 /* iscsid */, 2B16EEC8196085A40061E7FA /* iscsictl */, 2B7A0B731C8AEC47008290E9 /* iSCSI */, - 2B1EDC0D1CFAEC1F007BAB99 /* iSCSI Utility */, + 2B1A490B1D3D40FE00D3ED0D /* iSCSIHBATest */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 2B1EDC0C1CFAEC1F007BAB99 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 2B1EDC1A1CFAEC1F007BAB99 /* Assets.xcassets in Resources */, - 2B1EDC1D1CFAEC1F007BAB99 /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 2B7A0B721C8AEC47008290E9 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -631,16 +593,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 2B1EDC0A1CFAEC1F007BAB99 /* Sources */ = { + 2B1A49081D3D40FE00D3ED0D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 2B77629A1CFF2211003093CC /* Node.m in Sources */, - 2BAAB8B71D03E8EF00C01C79 /* SplitViewController.m in Sources */, - 2B7762991CFF220D003093CC /* NodeViewController.m in Sources */, - 2B77629E1D02CC9D003093CC /* InitiatorViewController.m in Sources */, - 2B1EDC151CFAEC1F007BAB99 /* main.m in Sources */, - 2B1EDC121CFAEC1F007BAB99 /* AppDelegate.m in Sources */, + 2B1A49141D3D416000D3ED0D /* iSCSIPDUUser.c in Sources */, + 2B1A490F1D3D40FE00D3ED0D /* main.c in Sources */, + 2B1A49151D3D416800D3ED0D /* iSCSIHBAInterface.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -664,7 +623,7 @@ buildActionMask = 2147483647; files = ( 2B9E3C941C493BAA00440116 /* iSCSIInitiator.cpp in Sources */, - 2B9E3C961C493BAA00440116 /* iSCSIInitiatorClient.cpp in Sources */, + 2B9E3C961C493BAA00440116 /* iSCSIHBAUserClient.cpp in Sources */, 2B9E3C981C493BAA00440116 /* iSCSIIOEventSource.cpp in Sources */, 2B9E3CBE1C49ED0000440116 /* crc32c.c in Sources */, 2B9E3C9C1C493BAA00440116 /* iSCSIPDUKernel.cpp in Sources */, @@ -677,29 +636,19 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 2BDE5E8E1C8B3E7D004BDB5F /* iSCSISession.c in Sources */, - 2BDE5E8B1C8B3E7D004BDB5F /* iSCSIKernelInterface.c in Sources */, - 2BDE5E881C8B3E7D004BDB5F /* iSCSIAuth.c in Sources */, - 2BDE5E891C8B3E7D004BDB5F /* iSCSIDaemon.c in Sources */, - 2BDE5E8A1C8B3E7D004BDB5F /* iSCSIDiscovery.c in Sources */, + 2BDE5E8B1C8B3E7D004BDB5F /* iSCSIHBAInterface.c in Sources */, + 2B6BCCB91D3A7857003522BC /* iSCSISessionManager.c in Sources */, 2BDE5E8C1C8B3E7D004BDB5F /* iSCSIPDUUser.c in Sources */, 2BDE5E8D1C8B3E7D004BDB5F /* iSCSIQueryTarget.c in Sources */, + 2BDE5E881C8B3E7D004BDB5F /* iSCSIAuth.c in Sources */, + 2BDE5E8E1C8B3E7D004BDB5F /* iSCSISession.c in Sources */, + 2BDE5E8A1C8B3E7D004BDB5F /* iSCSIDiscovery.c in Sources */, + 2BDE5E891C8B3E7D004BDB5F /* iSCSIDaemon.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXVariantGroup section */ - 2B1EDC1B1CFAEC1F007BAB99 /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 2B1EDC1C1CFAEC1F007BAB99 /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - /* Begin XCBuildConfiguration section */ 2B16EED0196085A40061E7FA /* Debug */ = { isa = XCBuildConfiguration; @@ -719,7 +668,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; - DYLIB_CURRENT_VERSION = "1.0.0-beta"; + DYLIB_CURRENT_VERSION = 1.0.0; GCC_C_LANGUAGE_STANDARD = "compiler-default"; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; @@ -761,7 +710,7 @@ CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DYLIB_CURRENT_VERSION = "1.0.0-beta"; + DYLIB_CURRENT_VERSION = 1.0.0; ENABLE_NS_ASSERTIONS = NO; GCC_C_LANGUAGE_STANDARD = "compiler-default"; GCC_ENABLE_OBJC_EXCEPTIONS = YES; @@ -778,11 +727,10 @@ }; name = Release; }; - 2B1EDC201CFAEC1F007BAB99 /* Debug */ = { + 2B1A49111D3D40FE00D3ED0D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -798,7 +746,6 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; - COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -816,20 +763,16 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = "iSCSI Utility/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = YES; - PRODUCT_BUNDLE_IDENTIFIER = "com.github.iscsi-osx.iSCSI-Utility"; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; - 2B1EDC211CFAEC1F007BAB99 /* Release */ = { + 2B1A49121D3D40FE00D3ED0D /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -845,7 +788,6 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; - COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; @@ -858,11 +800,8 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = "iSCSI Utility/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_BUNDLE_IDENTIFIER = "com.github.iscsi-osx.iSCSI-Utility"; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -872,7 +811,7 @@ buildSettings = { CF_PREFERENCES_APP_ID = "$(NAME_PREFIX_D).iSCSIInitiator"; CODE_SIGN_IDENTITY = ""; - CURRENT_PROJECT_VERSION = "1.0.0-beta2"; + CURRENT_PROJECT_VERSION = "1.0.0-beta4"; ENABLE_TESTABILITY = YES; GCC_PREPROCESSOR_DEFINITIONS = ( "CF_PREFERENCES_APP_ID=\\\"$(NAME_PREFIX_D).iSCSIInitiator\\\"", @@ -894,7 +833,7 @@ buildSettings = { CF_PREFERENCES_APP_ID = "$(NAME_PREFIX_D).iSCSIInitiator"; CODE_SIGN_IDENTITY = ""; - CURRENT_PROJECT_VERSION = "1.0.0-beta2"; + CURRENT_PROJECT_VERSION = "1.0.0-beta4"; GCC_PREPROCESSOR_DEFINITIONS = ( "CF_PREFERENCES_APP_ID=\\\"$(NAME_PREFIX_D).iSCSIInitiator\\\"", "NAME_PREFIX_U=$(NAME_PREFIX_U)", @@ -1029,7 +968,7 @@ CODE_SIGN_IDENTITY = ""; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; - DYLIB_CURRENT_VERSION = "1.0.0-beta"; + DYLIB_CURRENT_VERSION = 1.0.0; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; @@ -1083,7 +1022,7 @@ COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DYLIB_CURRENT_VERSION = "1.0.0-beta"; + DYLIB_CURRENT_VERSION = 1.0.0; ENABLE_NS_ASSERTIONS = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_ENABLE_OBJC_EXCEPTIONS = YES; @@ -1129,7 +1068,7 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - DYLIB_CURRENT_VERSION = "1.0.0-beta"; + DYLIB_CURRENT_VERSION = 1.0.0; GCC_C_LANGUAGE_STANDARD = "compiler-default"; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; @@ -1172,7 +1111,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DYLIB_CURRENT_VERSION = "1.0.0-beta"; + DYLIB_CURRENT_VERSION = 1.0.0; ENABLE_NS_ASSERTIONS = NO; GCC_C_LANGUAGE_STANDARD = "compiler-default"; GCC_ENABLE_OBJC_EXCEPTIONS = YES; @@ -1203,11 +1142,11 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 2B1EDC1F1CFAEC1F007BAB99 /* Build configuration list for PBXNativeTarget "iSCSI Utility" */ = { + 2B1A49101D3D40FE00D3ED0D /* Build configuration list for PBXNativeTarget "iSCSIHBATest" */ = { isa = XCConfigurationList; buildConfigurations = ( - 2B1EDC201CFAEC1F007BAB99 /* Debug */, - 2B1EDC211CFAEC1F007BAB99 /* Release */, + 2B1A49111D3D40FE00D3ED0D /* Debug */, + 2B1A49121D3D40FE00D3ED0D /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release;