| #ifndef _GPXE_ISCSI_H |
| #define _GPXE_ISCSI_H |
| |
| /** @file |
| * |
| * iSCSI protocol |
| * |
| */ |
| |
| FILE_LICENCE ( GPL2_OR_LATER ); |
| |
| #include <stdint.h> |
| #include <gpxe/socket.h> |
| #include <gpxe/scsi.h> |
| #include <gpxe/chap.h> |
| #include <gpxe/refcnt.h> |
| #include <gpxe/xfer.h> |
| #include <gpxe/process.h> |
| |
| /** Default iSCSI port */ |
| #define ISCSI_PORT 3260 |
| |
| /** |
| * iSCSI segment lengths |
| * |
| * iSCSI uses an icky structure with one one-byte field (a dword |
| * count) and one three-byte field (a byte count). This structure, |
| * and the accompanying macros, relieve some of the pain. |
| */ |
| union iscsi_segment_lengths { |
| struct { |
| /** The AHS length (measured in dwords) */ |
| uint8_t ahs_len; |
| /** The data length (measured in bytes), in network |
| * byte order |
| */ |
| uint8_t data_len[3]; |
| } bytes; |
| /** Ths data length (measured in bytes), in network byte |
| * order, with ahs_len as the first byte. |
| */ |
| uint32_t ahs_and_data_len; |
| }; |
| |
| /** The length of the additional header segment, in dwords */ |
| #define ISCSI_AHS_LEN( segment_lengths ) \ |
| ( (segment_lengths).bytes.ahs_len ) |
| |
| /** The length of the data segment, in bytes, excluding any padding */ |
| #define ISCSI_DATA_LEN( segment_lengths ) \ |
| ( ntohl ( (segment_lengths).ahs_and_data_len ) & 0xffffff ) |
| |
| /** The padding of the data segment, in bytes */ |
| #define ISCSI_DATA_PAD_LEN( segment_lengths ) \ |
| ( ( 0 - (segment_lengths).bytes.data_len[2] ) & 0x03 ) |
| |
| /** Set additional header and data segment lengths */ |
| #define ISCSI_SET_LENGTHS( segment_lengths, ahs_len, data_len ) do { \ |
| (segment_lengths).ahs_and_data_len = \ |
| htonl ( data_len | ( ahs_len << 24 ) ); \ |
| } while ( 0 ) |
| |
| /** |
| * iSCSI basic header segment common fields |
| * |
| */ |
| struct iscsi_bhs_common { |
| /** Opcode */ |
| uint8_t opcode; |
| /** Flags */ |
| uint8_t flags; |
| /** Fields specific to the PDU type */ |
| uint8_t other_a[2]; |
| /** Segment lengths */ |
| union iscsi_segment_lengths lengths; |
| /** Fields specific to the PDU type */ |
| uint8_t other_b[8]; |
| /** Initiator Task Tag */ |
| uint32_t itt; |
| /** Fields specific to the PDU type */ |
| uint8_t other_c[28]; |
| }; |
| |
| /** Opcode mask */ |
| #define ISCSI_OPCODE_MASK 0x3f |
| |
| /** Immediate delivery */ |
| #define ISCSI_FLAG_IMMEDIATE 0x40 |
| |
| /** Final PDU of a sequence */ |
| #define ISCSI_FLAG_FINAL 0x80 |
| |
| /** |
| * iSCSI basic header segment common request fields |
| * |
| */ |
| struct iscsi_bhs_common_response { |
| /** Opcode */ |
| uint8_t opcode; |
| /** Flags */ |
| uint8_t flags; |
| /** Fields specific to the PDU type */ |
| uint8_t other_a[2]; |
| /** Segment lengths */ |
| union iscsi_segment_lengths lengths; |
| /** Fields specific to the PDU type */ |
| uint8_t other_b[8]; |
| /** Initiator Task Tag */ |
| uint32_t itt; |
| /** Fields specific to the PDU type */ |
| uint8_t other_c[4]; |
| /** Status sequence number */ |
| uint32_t statsn; |
| /** Expected command sequence number */ |
| uint32_t expcmdsn; |
| /** Fields specific to the PDU type */ |
| uint8_t other_d[16]; |
| }; |
| |
| /** |
| * iSCSI login request basic header segment |
| * |
| */ |
| struct iscsi_bhs_login_request { |
| /** Opcode */ |
| uint8_t opcode; |
| /** Flags */ |
| uint8_t flags; |
| /** Maximum supported version number */ |
| uint8_t version_max; |
| /** Minimum supported version number */ |
| uint8_t version_min; |
| /** Segment lengths */ |
| union iscsi_segment_lengths lengths; |
| /** Initiator session ID (IANA format) enterprise number and flags */ |
| uint32_t isid_iana_en; |
| /** Initiator session ID (IANA format) qualifier */ |
| uint16_t isid_iana_qual; |
| /** Target session identifying handle */ |
| uint16_t tsih; |
| /** Initiator Task Tag */ |
| uint32_t itt; |
| /** Connection ID */ |
| uint16_t cid; |
| /** Reserved */ |
| uint16_t reserved_a; |
| /** Command sequence number */ |
| uint32_t cmdsn; |
| /** Expected status sequence number */ |
| uint32_t expstatsn; |
| /** Reserved */ |
| uint8_t reserved_b[16]; |
| }; |
| |
| /** Login request opcode */ |
| #define ISCSI_OPCODE_LOGIN_REQUEST 0x03 |
| |
| /** Willingness to transition to next stage */ |
| #define ISCSI_LOGIN_FLAG_TRANSITION 0x80 |
| |
| /** Key=value pairs continued in subsequent request */ |
| #define ISCSI_LOGIN_FLAG_CONTINUE 0x40 |
| |
| /* Current stage values and mask */ |
| #define ISCSI_LOGIN_CSG_MASK 0x0c |
| #define ISCSI_LOGIN_CSG_SECURITY_NEGOTIATION 0x00 |
| #define ISCSI_LOGIN_CSG_OPERATIONAL_NEGOTIATION 0x04 |
| #define ISCSI_LOGIN_CSG_FULL_FEATURE_PHASE 0x0c |
| |
| /* Next stage values and mask */ |
| #define ISCSI_LOGIN_NSG_MASK 0x03 |
| #define ISCSI_LOGIN_NSG_SECURITY_NEGOTIATION 0x00 |
| #define ISCSI_LOGIN_NSG_OPERATIONAL_NEGOTIATION 0x01 |
| #define ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE 0x03 |
| |
| /** ISID IANA format marker */ |
| #define ISCSI_ISID_IANA 0x40000000 |
| |
| /** Fen Systems Ltd. IANA enterprise number |
| * |
| * Permission is hereby granted to use Fen Systems Ltd.'s IANA |
| * enterprise number with this iSCSI implementation. |
| */ |
| #define IANA_EN_FEN_SYSTEMS 10019 |
| |
| /** |
| * iSCSI login response basic header segment |
| * |
| */ |
| struct iscsi_bhs_login_response { |
| /** Opcode */ |
| uint8_t opcode; |
| /** Flags */ |
| uint8_t flags; |
| /** Maximum supported version number */ |
| uint8_t version_max; |
| /** Minimum supported version number */ |
| uint8_t version_min; |
| /** Segment lengths */ |
| union iscsi_segment_lengths lengths; |
| /** Initiator session ID (IANA format) enterprise number and flags */ |
| uint32_t isid_iana_en; |
| /** Initiator session ID (IANA format) qualifier */ |
| uint16_t isid_iana_qual; |
| /** Target session identifying handle */ |
| uint16_t tsih; |
| /** Initiator Task Tag */ |
| uint32_t itt; |
| /** Reserved */ |
| uint32_t reserved_a; |
| /** Status sequence number */ |
| uint32_t statsn; |
| /** Expected command sequence number */ |
| uint32_t expcmdsn; |
| /** Maximum command sequence number */ |
| uint32_t maxcmdsn; |
| /** Status class */ |
| uint8_t status_class; |
| /** Status detail */ |
| uint8_t status_detail; |
| /** Reserved */ |
| uint8_t reserved_b[10]; |
| }; |
| |
| /** Login response opcode */ |
| #define ISCSI_OPCODE_LOGIN_RESPONSE 0x23 |
| |
| /* Login response status codes */ |
| #define ISCSI_STATUS_SUCCESS 0x00 |
| #define ISCSI_STATUS_REDIRECT 0x01 |
| #define ISCSI_STATUS_INITIATOR_ERROR 0x02 |
| #define ISCSI_STATUS_INITIATOR_ERROR_AUTHENTICATION 0x01 |
| #define ISCSI_STATUS_INITIATOR_ERROR_AUTHORISATION 0x02 |
| #define ISCSI_STATUS_INITIATOR_ERROR_NOT_FOUND 0x03 |
| #define ISCSI_STATUS_INITIATOR_ERROR_REMOVED 0x04 |
| #define ISCSI_STATUS_TARGET_ERROR 0x03 |
| |
| /** |
| * iSCSI SCSI command basic header segment |
| * |
| */ |
| struct iscsi_bhs_scsi_command { |
| /** Opcode */ |
| uint8_t opcode; |
| /** Flags */ |
| uint8_t flags; |
| /** Reserved */ |
| uint16_t reserved_a; |
| /** Segment lengths */ |
| union iscsi_segment_lengths lengths; |
| /** SCSI Logical Unit Number */ |
| uint64_t lun; |
| /** Initiator Task Tag */ |
| uint32_t itt; |
| /** Expected data transfer length */ |
| uint32_t exp_len; |
| /** Command sequence number */ |
| uint32_t cmdsn; |
| /** Expected status sequence number */ |
| uint32_t expstatsn; |
| /** SCSI Command Descriptor Block (CDB) */ |
| union scsi_cdb cdb; |
| }; |
| |
| /** SCSI command opcode */ |
| #define ISCSI_OPCODE_SCSI_COMMAND 0x01 |
| |
| /** Command will read data */ |
| #define ISCSI_COMMAND_FLAG_READ 0x40 |
| |
| /** Command will write data */ |
| #define ISCSI_COMMAND_FLAG_WRITE 0x20 |
| |
| /* Task attributes */ |
| #define ISCSI_COMMAND_ATTR_UNTAGGED 0x00 |
| #define ISCSI_COMMAND_ATTR_SIMPLE 0x01 |
| #define ISCSI_COMMAND_ATTR_ORDERED 0x02 |
| #define ISCSI_COMMAND_ATTR_HEAD_OF_QUEUE 0x03 |
| #define ISCSI_COMMAND_ATTR_ACA 0x04 |
| |
| /** |
| * iSCSI SCSI response basic header segment |
| * |
| */ |
| struct iscsi_bhs_scsi_response { |
| /** Opcode */ |
| uint8_t opcode; |
| /** Flags */ |
| uint8_t flags; |
| /** Response code */ |
| uint8_t response; |
| /** SCSI status code */ |
| uint8_t status; |
| /** Segment lengths */ |
| union iscsi_segment_lengths lengths; |
| /** Reserved */ |
| uint8_t reserved_a[8]; |
| /** Initiator Task Tag */ |
| uint32_t itt; |
| /** SNACK tag */ |
| uint32_t snack; |
| /** Status sequence number */ |
| uint32_t statsn; |
| /** Expected command sequence number */ |
| uint32_t expcmdsn; |
| /** Maximum command sequence number */ |
| uint32_t maxcmdsn; |
| /** Expected data sequence number */ |
| uint32_t expdatasn; |
| /** Reserved */ |
| uint8_t reserved_b[8]; |
| }; |
| |
| /** SCSI response opcode */ |
| #define ISCSI_OPCODE_SCSI_RESPONSE 0x21 |
| |
| /** SCSI command completed at target */ |
| #define ISCSI_RESPONSE_COMMAND_COMPLETE 0x00 |
| |
| /** SCSI target failure */ |
| #define ISCSI_RESPONSE_TARGET_FAILURE 0x01 |
| |
| /** SCSI sense response code offset |
| * |
| * The SCSI response may contain unsolicited sense data in the data |
| * segment. If it does, this is the offset to the sense response code |
| * byte, which is the only byte we care about. |
| */ |
| #define ISCSI_SENSE_RESPONSE_CODE_OFFSET 2 |
| |
| /** |
| * iSCSI data-in basic header segment |
| * |
| */ |
| struct iscsi_bhs_data_in { |
| /** Opcode */ |
| uint8_t opcode; |
| /** Flags */ |
| uint8_t flags; |
| /** Reserved */ |
| uint8_t reserved_a; |
| /** SCSI status code */ |
| uint8_t status; |
| /** Segment lengths */ |
| union iscsi_segment_lengths lengths; |
| /** Logical Unit Number */ |
| uint64_t lun; |
| /** Initiator Task Tag */ |
| uint32_t itt; |
| /** Target Transfer Tag */ |
| uint32_t ttt; |
| /** Status sequence number */ |
| uint32_t statsn; |
| /** Expected command sequence number */ |
| uint32_t expcmdsn; |
| /** Maximum command sequence number */ |
| uint32_t maxcmdsn; |
| /** Data sequence number */ |
| uint32_t datasn; |
| /** Buffer offset */ |
| uint32_t offset; |
| /** Residual count */ |
| uint32_t residual_count; |
| }; |
| |
| /** Data-in opcode */ |
| #define ISCSI_OPCODE_DATA_IN 0x25 |
| |
| /** Data requires acknowledgement */ |
| #define ISCSI_DATA_FLAG_ACKNOWLEDGE 0x40 |
| |
| /** Data overflow occurred */ |
| #define ISCSI_DATA_FLAG_OVERFLOW 0x04 |
| |
| /** Data underflow occurred */ |
| #define ISCSI_DATA_FLAG_UNDERFLOW 0x02 |
| |
| /** SCSI status code and overflow/underflow flags are valid */ |
| #define ISCSI_DATA_FLAG_STATUS 0x01 |
| |
| /** |
| * iSCSI data-out basic header segment |
| * |
| */ |
| struct iscsi_bhs_data_out { |
| /** Opcode */ |
| uint8_t opcode; |
| /** Flags */ |
| uint8_t flags; |
| /** Reserved */ |
| uint16_t reserved_a; |
| /** Segment lengths */ |
| union iscsi_segment_lengths lengths; |
| /** Logical Unit Number */ |
| uint64_t lun; |
| /** Initiator Task Tag */ |
| uint32_t itt; |
| /** Target Transfer Tag */ |
| uint32_t ttt; |
| /** Reserved */ |
| uint32_t reserved_b; |
| /** Expected status sequence number */ |
| uint32_t expstatsn; |
| /** Reserved */ |
| uint32_t reserved_c; |
| /** Data sequence number */ |
| uint32_t datasn; |
| /** Buffer offset */ |
| uint32_t offset; |
| /** Reserved */ |
| uint32_t reserved_d; |
| }; |
| |
| /** Data-out opcode */ |
| #define ISCSI_OPCODE_DATA_OUT 0x05 |
| |
| /** |
| * iSCSI request to transfer basic header segment |
| * |
| */ |
| struct iscsi_bhs_r2t { |
| /** Opcode */ |
| uint8_t opcode; |
| /** Flags */ |
| uint8_t flags; |
| /** Reserved */ |
| uint16_t reserved_a; |
| /** Segment lengths */ |
| union iscsi_segment_lengths lengths; |
| /** Logical Unit Number */ |
| uint64_t lun; |
| /** Initiator Task Tag */ |
| uint32_t itt; |
| /** Target Transfer Tag */ |
| uint32_t ttt; |
| /** Status sequence number */ |
| uint32_t statsn; |
| /** Expected command sequence number */ |
| uint32_t expcmdsn; |
| /** Maximum command sequence number */ |
| uint32_t maxcmdsn; |
| /** R2T sequence number */ |
| uint32_t r2tsn; |
| /** Buffer offset */ |
| uint32_t offset; |
| /** Desired data transfer length */ |
| uint32_t len; |
| }; |
| |
| /** R2T opcode */ |
| #define ISCSI_OPCODE_R2T 0x31 |
| |
| /** |
| * An iSCSI basic header segment |
| */ |
| union iscsi_bhs { |
| struct iscsi_bhs_common common; |
| struct iscsi_bhs_common_response common_response; |
| struct iscsi_bhs_login_request login_request; |
| struct iscsi_bhs_login_response login_response; |
| struct iscsi_bhs_scsi_command scsi_command; |
| struct iscsi_bhs_scsi_response scsi_response; |
| struct iscsi_bhs_data_in data_in; |
| struct iscsi_bhs_data_out data_out; |
| struct iscsi_bhs_r2t r2t; |
| unsigned char bytes[ sizeof ( struct iscsi_bhs_common ) ]; |
| }; |
| |
| /** State of an iSCSI TX engine */ |
| enum iscsi_tx_state { |
| /** Nothing to send */ |
| ISCSI_TX_IDLE = 0, |
| /** Sending the basic header segment */ |
| ISCSI_TX_BHS, |
| /** Sending the additional header segment */ |
| ISCSI_TX_AHS, |
| /** Sending the data segment */ |
| ISCSI_TX_DATA, |
| /** Sending the data segment padding */ |
| ISCSI_TX_DATA_PADDING, |
| }; |
| |
| /** State of an iSCSI RX engine */ |
| enum iscsi_rx_state { |
| /** Receiving the basic header segment */ |
| ISCSI_RX_BHS = 0, |
| /** Receiving the additional header segment */ |
| ISCSI_RX_AHS, |
| /** Receiving the data segment */ |
| ISCSI_RX_DATA, |
| /** Receiving the data segment padding */ |
| ISCSI_RX_DATA_PADDING, |
| }; |
| |
| /** An iSCSI session */ |
| struct iscsi_session { |
| /** Reference counter */ |
| struct refcnt refcnt; |
| |
| /** Transport-layer socket */ |
| struct xfer_interface socket; |
| |
| /** Target address */ |
| char *target_address; |
| /** Target port */ |
| unsigned int target_port; |
| /** Target IQN */ |
| char *target_iqn; |
| /** Logical Unit Number (LUN) */ |
| uint64_t lun; |
| /** Target socket address (recorded only for iBFT) */ |
| struct sockaddr target_sockaddr; |
| |
| /** Session status |
| * |
| * This is the bitwise-OR of zero or more ISCSI_STATUS_XXX |
| * constants. |
| */ |
| int status; |
| /** Retry count |
| * |
| * Number of times that the connection has been retried. |
| * Reset upon a successful connection. |
| */ |
| int retry_count; |
| |
| /** Initiator username (if any) */ |
| char *initiator_username; |
| /** Initiator password (if any) */ |
| char *initiator_password; |
| /** Target username (if any) */ |
| char *target_username; |
| /** Target password (if any) */ |
| char *target_password; |
| /** CHAP challenge (for target auth only) |
| * |
| * This is a block of random data; the first byte is used as |
| * the CHAP identifier (CHAP_I) and the remainder as the CHAP |
| * challenge (CHAP_C). |
| */ |
| unsigned char chap_challenge[17]; |
| /** CHAP response (used for both initiator and target auth) */ |
| struct chap_response chap; |
| |
| /** Target session identifying handle |
| * |
| * This is assigned by the target when we first log in, and |
| * must be reused on subsequent login attempts. |
| */ |
| uint16_t tsih; |
| /** Initiator task tag |
| * |
| * This is the tag of the current command. It is incremented |
| * whenever a new command is started. |
| */ |
| uint32_t itt; |
| /** Target transfer tag |
| * |
| * This is the tag attached to a sequence of data-out PDUs in |
| * response to an R2T. |
| */ |
| uint32_t ttt; |
| /** |
| * Transfer offset |
| * |
| * This is the offset for an in-progress sequence of data-out |
| * PDUs in response to an R2T. |
| */ |
| uint32_t transfer_offset; |
| /** |
| * Transfer length |
| * |
| * This is the length for an in-progress sequence of data-out |
| * PDUs in response to an R2T. |
| */ |
| uint32_t transfer_len; |
| /** Command sequence number |
| * |
| * This is the sequence number of the current command, used to |
| * fill out the CmdSN field in iSCSI request PDUs. It is |
| * updated with the value of the ExpCmdSN field whenever we |
| * receive an iSCSI response PDU containing such a field. |
| */ |
| uint32_t cmdsn; |
| /** Status sequence number |
| * |
| * This is the most recent status sequence number present in |
| * the StatSN field of an iSCSI response PDU containing such a |
| * field. Whenever we send an iSCSI request PDU, we fill out |
| * the ExpStatSN field with this value plus one. |
| */ |
| uint32_t statsn; |
| |
| /** Basic header segment for current TX PDU */ |
| union iscsi_bhs tx_bhs; |
| /** State of the TX engine */ |
| enum iscsi_tx_state tx_state; |
| /** TX process */ |
| struct process process; |
| |
| /** Basic header segment for current RX PDU */ |
| union iscsi_bhs rx_bhs; |
| /** State of the RX engine */ |
| enum iscsi_rx_state rx_state; |
| /** Byte offset within the current RX state */ |
| size_t rx_offset; |
| /** Length of the current RX state */ |
| size_t rx_len; |
| /** Buffer for received data (not always used) */ |
| void *rx_buffer; |
| |
| /** Current SCSI command |
| * |
| * Set to NULL when command is complete. |
| */ |
| struct scsi_command *command; |
| /** SCSI command return code |
| * |
| * Set to -EINPROGRESS while command is processing. |
| */ |
| int rc; |
| /** Instant return code |
| * |
| * Set to a non-zero value if all requests should return |
| * immediately. This can be used to e.g. avoid retrying |
| * logins that are doomed to fail authentication. |
| */ |
| int instant_rc; |
| }; |
| |
| /** iSCSI session is currently in the security negotiation phase */ |
| #define ISCSI_STATUS_SECURITY_NEGOTIATION_PHASE \ |
| ( ISCSI_LOGIN_CSG_SECURITY_NEGOTIATION | \ |
| ISCSI_LOGIN_NSG_OPERATIONAL_NEGOTIATION ) |
| |
| /** iSCSI session is currently in the operational parameter |
| * negotiation phase |
| */ |
| #define ISCSI_STATUS_OPERATIONAL_NEGOTIATION_PHASE \ |
| ( ISCSI_LOGIN_CSG_OPERATIONAL_NEGOTIATION | \ |
| ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE ) |
| |
| /** iSCSI session is currently in the full feature phase */ |
| #define ISCSI_STATUS_FULL_FEATURE_PHASE ISCSI_LOGIN_CSG_FULL_FEATURE_PHASE |
| |
| /** Mask for all iSCSI session phases */ |
| #define ISCSI_STATUS_PHASE_MASK ( ISCSI_LOGIN_CSG_MASK | ISCSI_LOGIN_NSG_MASK ) |
| |
| /** iSCSI session needs to send the initial security negotiation strings */ |
| #define ISCSI_STATUS_STRINGS_SECURITY 0x0100 |
| |
| /** iSCSI session needs to send the CHAP_A string */ |
| #define ISCSI_STATUS_STRINGS_CHAP_ALGORITHM 0x0200 |
| |
| /** iSCSI session needs to send the CHAP response */ |
| #define ISCSI_STATUS_STRINGS_CHAP_RESPONSE 0x0400 |
| |
| /** iSCSI session needs to send the mutual CHAP challenge */ |
| #define ISCSI_STATUS_STRINGS_CHAP_CHALLENGE 0x0800 |
| |
| /** iSCSI session needs to send the operational negotiation strings */ |
| #define ISCSI_STATUS_STRINGS_OPERATIONAL 0x1000 |
| |
| /** Mask for all iSCSI "needs to send" flags */ |
| #define ISCSI_STATUS_STRINGS_MASK 0xff00 |
| |
| /** Target has requested forward (initiator) authentication */ |
| #define ISCSI_STATUS_AUTH_FORWARD_REQUIRED 0x00010000 |
| |
| /** Initiator requires target (reverse) authentication */ |
| #define ISCSI_STATUS_AUTH_REVERSE_REQUIRED 0x00020000 |
| |
| /** Target authenticated itself correctly */ |
| #define ISCSI_STATUS_AUTH_REVERSE_OK 0x00040000 |
| |
| /** Maximum number of retries at connecting */ |
| #define ISCSI_MAX_RETRIES 2 |
| |
| extern int iscsi_attach ( struct scsi_device *scsi, const char *root_path ); |
| extern void iscsi_detach ( struct scsi_device *scsi ); |
| extern const char * iscsi_initiator_iqn ( void ); |
| |
| #endif /* _GPXE_ISCSI_H */ |