Add these settings to the iscsi context structure and initialize them to sane valued. When sending login commands to the target, use these values instead of hardcoded values. Parse when the target sends a login reply back to us and update these variables if the target asks us to. This allows us to detect when our defaults are too big for the target and adjust the settings we use so we match the target. Some targets have a very small accepted default for some settings. During login, we will initially send these keys with our dafult values. These targets will then respond back by refusing to transition to the next login phase, and by telling us back what the maximum of these values should be. In this case we have to try the login again but use the smaller values we got from the target. Othervise, if we try again, ignoring the value from the target, and just repeat using our defaults the target will abort the login with a "initiator error".
885 lines
21 KiB
C
885 lines
21 KiB
C
/*
|
|
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifndef _GNU_SOURCE
|
|
#define _GNU_SOURCE
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <arpa/inet.h>
|
|
#include "iscsi.h"
|
|
#include "iscsi-private.h"
|
|
#include "md5.h"
|
|
|
|
static int
|
|
iscsi_login_add_initiatorname(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
/* We only send InitiatorName during opneg or the first leg of secneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG
|
|
&& iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) {
|
|
return 0;
|
|
}
|
|
|
|
if (iscsi_pdu_add_data(iscsi, pdu,
|
|
(unsigned char *)"InitiatorName=",
|
|
14) != 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
|
|
"failed for InitiatorName.");
|
|
return -1;
|
|
}
|
|
if (iscsi_pdu_add_data(iscsi, pdu,
|
|
(unsigned char *)iscsi->initiator_name,
|
|
strlen(iscsi->initiator_name) +1) != 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
|
|
"failed for InitiatorName.");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_alias(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
/* We only send InitiatorAlias during opneg or the first leg of secneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG
|
|
&& iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) {
|
|
return 0;
|
|
}
|
|
|
|
if (iscsi_pdu_add_data(iscsi, pdu,
|
|
(unsigned char *)"InitiatorAlias=",
|
|
15) != 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
|
|
"failed.");
|
|
return -1;
|
|
}
|
|
if (iscsi_pdu_add_data(iscsi, pdu,
|
|
(unsigned char *)iscsi->alias,
|
|
strlen(iscsi->alias) +1) != 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
|
|
"failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_targetname(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
/* We only send TargetName during opneg or the first leg of secneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG
|
|
&& iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) {
|
|
return 0;
|
|
}
|
|
|
|
if (iscsi->target_name == NULL) {
|
|
iscsi_set_error(iscsi, "Trying normal connect but "
|
|
"target name not set.");
|
|
return -1;
|
|
}
|
|
|
|
if (iscsi_pdu_add_data(iscsi, pdu,
|
|
(unsigned char *)"TargetName=",
|
|
11) != 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
|
|
"failed.");
|
|
return -1;
|
|
}
|
|
if (iscsi_pdu_add_data(iscsi, pdu,
|
|
(unsigned char *)iscsi->target_name,
|
|
strlen(iscsi->target_name) +1) != 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
|
|
"failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_sessiontype(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char *str;
|
|
|
|
/* We only send TargetName during opneg or the first leg of secneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG
|
|
&& iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) {
|
|
return 0;
|
|
}
|
|
|
|
switch (iscsi->session_type) {
|
|
case ISCSI_SESSION_DISCOVERY:
|
|
str = (char *)"SessionType=Discovery";
|
|
break;
|
|
case ISCSI_SESSION_NORMAL:
|
|
str = (char *)"SessionType=Normal";
|
|
break;
|
|
default:
|
|
iscsi_set_error(iscsi, "Can not handle sessions %d yet.",
|
|
iscsi->session_type);
|
|
return -1;
|
|
}
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_headerdigest(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char *str;
|
|
|
|
/* We only send HeaderDigest during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
switch (iscsi->want_header_digest) {
|
|
case ISCSI_HEADER_DIGEST_NONE:
|
|
str = (char *)"HeaderDigest=None";
|
|
break;
|
|
case ISCSI_HEADER_DIGEST_NONE_CRC32C:
|
|
str = (char *)"HeaderDigest=None,CRC32C";
|
|
break;
|
|
case ISCSI_HEADER_DIGEST_CRC32C_NONE:
|
|
str = (char *)"HeaderDigest=CRC32C,None";
|
|
break;
|
|
case ISCSI_HEADER_DIGEST_CRC32C:
|
|
str = (char *)"HeaderDigest=CRC32C";
|
|
break;
|
|
}
|
|
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_datadigest(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char *str;
|
|
|
|
/* We only send DataDigest during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
str = (char *)"DataDigest=None";
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_initialr2t(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char *str;
|
|
|
|
/* We only send InitialR2T during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
str = (char *)"InitialR2T=Yes";
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_immediatedata(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char *str;
|
|
|
|
/* We only send ImmediateData during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
str = (char *)"ImmediateData=Yes";
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_maxburstlength(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char *str;
|
|
|
|
/* We only send MaxBurstLength during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
asprintf(&str, "MaxBurstLength=%d", iscsi->max_burst_length);
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
free(str);
|
|
return -1;
|
|
}
|
|
free(str);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_firstburstlength(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char *str;
|
|
|
|
/* We only send FirstBurstLength during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
asprintf(&str, "FirstBurstLength=%d", iscsi->first_burst_length);
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
free(str);
|
|
return -1;
|
|
}
|
|
free(str);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_maxrecvdatasegmentlength(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char *str;
|
|
|
|
/* We only send MaxRecvDataSegmentLength during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
asprintf(&str, "MaxRecvDataSegmentLength=%d", iscsi->max_recv_data_segment_length);
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
free(str);
|
|
return -1;
|
|
}
|
|
free(str);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_datapduinorder(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char *str;
|
|
|
|
/* We only send DataPduInOrder during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
str = (char *)"DataPDUInOrder=Yes";
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_datasequenceinorder(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char *str;
|
|
|
|
/* We only send DataSequenceInOrder during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
str = (char *)"DataSequenceInOrder=Yes";
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_authmethod(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char *str;
|
|
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG
|
|
|| iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) {
|
|
return 0;
|
|
}
|
|
|
|
str = (char *)"AuthMethod=CHAP,None";
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_authalgorithm(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char *str;
|
|
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG
|
|
|| iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_SELECT_ALGORITHM) {
|
|
return 0;
|
|
}
|
|
|
|
str = (char *)"CHAP_A=5";
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_chap_username(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char *str;
|
|
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG
|
|
|| iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE) {
|
|
return 0;
|
|
}
|
|
|
|
str = (char *)"CHAP_N=";
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str))
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
if (iscsi_pdu_add_data(iscsi, pdu,
|
|
(unsigned char *)iscsi->user,
|
|
strlen(iscsi->user) +1) != 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
|
|
"failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
h2i(int h)
|
|
{
|
|
if (h >= 'a' && h <= 'f') {
|
|
return h - 'a' + 10;
|
|
}
|
|
if (h >= 'A' && h <= 'F') {
|
|
return h - 'A' + 10;
|
|
}
|
|
return h - '0';
|
|
}
|
|
static int
|
|
i2h(int i)
|
|
{
|
|
if (i >= 10) {
|
|
return i - 10 + 'A';
|
|
}
|
|
|
|
return i + '0';
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_chap_response(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char *str;
|
|
unsigned char c, cc[2];
|
|
unsigned char digest[16];
|
|
struct MD5Context ctx;
|
|
int i;
|
|
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG
|
|
|| iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE) {
|
|
return 0;
|
|
}
|
|
|
|
if (iscsi->chap_c == NULL) {
|
|
iscsi_set_error(iscsi, "No CHAP challenge found");
|
|
return -1;
|
|
}
|
|
MD5Init(&ctx);
|
|
c = iscsi->chap_i;
|
|
MD5Update(&ctx, &c, 1);
|
|
MD5Update(&ctx, (unsigned char *)iscsi->passwd, strlen(iscsi->passwd));
|
|
str = iscsi->chap_c;
|
|
while (*str != 0) {
|
|
c = (h2i(str[0]) << 4) | h2i(str[1]);
|
|
str += 2;
|
|
MD5Update(&ctx, &c, 1);
|
|
}
|
|
MD5Final(digest, &ctx);
|
|
|
|
str = (char *)"CHAP_R=0x";
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str))
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
for (i=0; i<16; i++) {
|
|
c = digest[i];
|
|
cc[0] = i2h((c >> 4)&0x0f);
|
|
cc[1] = i2h((c )&0x0f);
|
|
if (iscsi_pdu_add_data(iscsi, pdu, &cc[0], 2) != 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
|
|
"failed.");
|
|
return -1;
|
|
}
|
|
}
|
|
c = 0;
|
|
if (iscsi_pdu_add_data(iscsi, pdu, &c, 1) != 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
|
|
"failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
iscsi_login_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
|
|
void *private_data)
|
|
{
|
|
struct iscsi_pdu *pdu;
|
|
int transit;
|
|
|
|
if (iscsi->login_attempts++ > 10) {
|
|
iscsi_set_error(iscsi, "login took too many tries."
|
|
" giving up.");
|
|
return -1;
|
|
}
|
|
|
|
if (iscsi->is_loggedin != 0) {
|
|
iscsi_set_error(iscsi, "Trying to login while already logged "
|
|
"in.");
|
|
return -1;
|
|
}
|
|
|
|
switch (iscsi->session_type) {
|
|
case ISCSI_SESSION_DISCOVERY:
|
|
case ISCSI_SESSION_NORMAL:
|
|
break;
|
|
default:
|
|
iscsi_set_error(iscsi, "trying to login without setting "
|
|
"session type.");
|
|
return -1;
|
|
}
|
|
|
|
pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_LOGIN_REQUEST,
|
|
ISCSI_PDU_LOGIN_RESPONSE);
|
|
if (pdu == NULL) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: Failed to allocate "
|
|
"login pdu.");
|
|
return -1;
|
|
}
|
|
|
|
/* login request */
|
|
iscsi_pdu_set_immediate(pdu);
|
|
|
|
if (iscsi->user == NULL) {
|
|
iscsi->current_phase = ISCSI_PDU_LOGIN_CSG_OPNEG;
|
|
}
|
|
|
|
if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_SECNEG) {
|
|
iscsi->next_phase = ISCSI_PDU_LOGIN_NSG_OPNEG;
|
|
}
|
|
if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
iscsi->next_phase = ISCSI_PDU_LOGIN_NSG_FF;
|
|
}
|
|
|
|
transit = 0;
|
|
if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
transit = ISCSI_PDU_LOGIN_TRANSIT;
|
|
}
|
|
if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_SECNEG) {
|
|
if (iscsi->secneg_phase == ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) {
|
|
transit = ISCSI_PDU_LOGIN_TRANSIT;
|
|
}
|
|
if (iscsi->secneg_phase == ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE) {
|
|
transit = ISCSI_PDU_LOGIN_TRANSIT;
|
|
}
|
|
}
|
|
|
|
/* flags */
|
|
iscsi_pdu_set_pduflags(pdu, transit
|
|
| iscsi->current_phase
|
|
| iscsi->next_phase);
|
|
|
|
|
|
/* initiator name */
|
|
if (iscsi_login_add_initiatorname(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* optional alias */
|
|
if (iscsi->alias) {
|
|
if (iscsi_login_add_alias(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* target name */
|
|
if (iscsi->session_type == ISCSI_SESSION_NORMAL) {
|
|
if (iscsi_login_add_targetname(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* session type */
|
|
if (iscsi_login_add_sessiontype(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* header digest */
|
|
if (iscsi_login_add_headerdigest(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* auth method */
|
|
if (iscsi_login_add_authmethod(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* auth algorithm */
|
|
if (iscsi_login_add_authalgorithm(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* chap username */
|
|
if (iscsi_login_add_chap_username(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* chap response */
|
|
if (iscsi_login_add_chap_response(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* data digest */
|
|
if (iscsi_login_add_datadigest(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* initial r2t */
|
|
if (iscsi_login_add_initialr2t(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* immediate data */
|
|
if (iscsi_login_add_immediatedata(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* max burst length */
|
|
if (iscsi_login_add_maxburstlength(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* first burst length */
|
|
if (iscsi_login_add_firstburstlength(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* max recv data segment length */
|
|
if (iscsi_login_add_maxrecvdatasegmentlength(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* data pdu in order */
|
|
if (iscsi_login_add_datapduinorder(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* data sequence in order */
|
|
if (iscsi_login_add_datasequenceinorder(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
|
|
pdu->callback = cb;
|
|
pdu->private_data = private_data;
|
|
|
|
if (iscsi_queue_pdu(iscsi, pdu) != 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi "
|
|
"pdu.");
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const char *login_error_str(int status)
|
|
{
|
|
switch (status) {
|
|
case 0x0101: return "Target moved temporarily";
|
|
case 0x0102: return "Target moved permanently";
|
|
case 0x0200: return "Initiator error";
|
|
case 0x0201: return "Authentication failure";
|
|
case 0x0202: return "Authorization failure";
|
|
case 0x0203: return "Target not found";
|
|
case 0x0204: return "Target removed";
|
|
case 0x0205: return "Unsupported version";
|
|
case 0x0206: return "Too many connections";
|
|
case 0x0207: return "Missing parameter";
|
|
case 0x0208: return "Can't include in session";
|
|
case 0x0209: return "Session type not supported";
|
|
case 0x020a: return "Session does not exist";
|
|
case 0x020b: return "Invalid during login";
|
|
case 0x0300: return "Target error";
|
|
case 0x0301: return "Service unavailable";
|
|
case 0x0302: return "Out of resources";
|
|
}
|
|
return "Unknown login error";
|
|
}
|
|
|
|
|
|
int
|
|
iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
|
|
struct iscsi_in_pdu *in)
|
|
{
|
|
int status;
|
|
unsigned char *ptr = in->data;
|
|
int size = in->data_pos;
|
|
|
|
status = ntohs(*(uint16_t *)&in->hdr[36]);
|
|
if (status != 0) {
|
|
iscsi_set_error(iscsi, "Failed to log in to target. Status: %s(%d)\n",
|
|
login_error_str(status), status);
|
|
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
|
|
pdu->private_data);
|
|
return 0;
|
|
}
|
|
|
|
iscsi->statsn = ntohs(*(uint16_t *)&in->hdr[24]);
|
|
|
|
/* XXX here we should parse the data returned in case the target
|
|
* renegotiated some some parameters.
|
|
* we should also do proper handshaking if the target is not yet
|
|
* prepared to transition to the next stage
|
|
*/
|
|
|
|
while (size > 0) {
|
|
int len;
|
|
|
|
len = strlen((char *)ptr);
|
|
|
|
if (len == 0) {
|
|
break;
|
|
}
|
|
|
|
if (len > size) {
|
|
iscsi_set_error(iscsi, "len > size when parsing "
|
|
"login data %d>%d", len, size);
|
|
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
|
|
pdu->private_data);
|
|
return -1;
|
|
}
|
|
|
|
/* parse the strings */
|
|
if (!strncmp((char *)ptr, "HeaderDigest=", 13)) {
|
|
if (!strcmp((char *)ptr + 13, "CRC32C")) {
|
|
iscsi->header_digest
|
|
= ISCSI_HEADER_DIGEST_CRC32C;
|
|
} else {
|
|
iscsi->header_digest
|
|
= ISCSI_HEADER_DIGEST_NONE;
|
|
}
|
|
}
|
|
|
|
if (!strncmp((char *)ptr, "FirstBurstLength=", 17)) {
|
|
iscsi->first_burst_length = strtol((char *)ptr + 17, NULL, 10);
|
|
}
|
|
|
|
if (!strncmp((char *)ptr, "MaxBurstLength=", 15)) {
|
|
iscsi->max_burst_length = strtol((char *)ptr + 15, NULL, 10);
|
|
}
|
|
|
|
if (!strncmp((char *)ptr, "MaxRecvDataSegmentLength=", 25)) {
|
|
iscsi->max_recv_data_segment_length = strtol((char *)ptr + 25, NULL, 10);
|
|
}
|
|
|
|
if (!strncmp((char *)ptr, "AuthMethod=", 11)) {
|
|
if (!strcmp((char *)ptr + 11, "CHAP")) {
|
|
iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SELECT_ALGORITHM;
|
|
}
|
|
}
|
|
|
|
if (!strncmp((char *)ptr, "CHAP_A=", 7)) {
|
|
iscsi->chap_a = atoi((char *)ptr+7);
|
|
iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE;
|
|
}
|
|
|
|
if (!strncmp((char *)ptr, "CHAP_I=", 7)) {
|
|
iscsi->chap_i = atoi((char *)ptr+7);
|
|
iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE;
|
|
}
|
|
|
|
if (!strncmp((char *)ptr, "CHAP_C=0x", 9)) {
|
|
free(iscsi->chap_c);
|
|
iscsi->chap_c = strdup((char *)ptr+9);
|
|
if (iscsi->chap_c == NULL) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: Failed to strdup CHAP challenge\n");
|
|
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data);
|
|
return -1;
|
|
}
|
|
iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE;
|
|
}
|
|
|
|
ptr += len + 1;
|
|
size -= len + 1;
|
|
}
|
|
|
|
if (in->hdr[1] & ISCSI_PDU_LOGIN_TRANSIT) {
|
|
iscsi->current_phase = (in->hdr[1] & ISCSI_PDU_LOGIN_NSG_FF) << 2;
|
|
}
|
|
|
|
if ((in->hdr[1] & ISCSI_PDU_LOGIN_TRANSIT)
|
|
&& (in->hdr[1] & ISCSI_PDU_LOGIN_NSG_FF) == ISCSI_PDU_LOGIN_NSG_FF) {
|
|
iscsi->is_loggedin = 1;
|
|
pdu->callback(iscsi, SCSI_STATUS_GOOD, NULL, pdu->private_data);
|
|
} else {
|
|
if (iscsi_login_async(iscsi, pdu->callback, pdu->private_data) != 0) {
|
|
iscsi_set_error(iscsi, "Failed to send continuation login pdu");
|
|
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
iscsi_logout_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
|
|
void *private_data)
|
|
{
|
|
struct iscsi_pdu *pdu;
|
|
|
|
iscsi->login_attempts = 0;
|
|
|
|
if (iscsi->is_loggedin == 0) {
|
|
iscsi_set_error(iscsi, "Trying to logout while not logged in.");
|
|
return -1;
|
|
}
|
|
|
|
pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_LOGOUT_REQUEST,
|
|
ISCSI_PDU_LOGOUT_RESPONSE);
|
|
if (pdu == NULL) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: Failed to allocate "
|
|
"logout pdu.");
|
|
return -1;
|
|
}
|
|
|
|
/* logout request has the immediate flag set */
|
|
iscsi_pdu_set_immediate(pdu);
|
|
|
|
/* flags : close the session */
|
|
iscsi_pdu_set_pduflags(pdu, 0x80);
|
|
|
|
|
|
pdu->callback = cb;
|
|
pdu->private_data = private_data;
|
|
|
|
if (iscsi_queue_pdu(iscsi, pdu) != 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi "
|
|
"logout pdu.");
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
iscsi_process_logout_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
|
|
struct iscsi_in_pdu *in _U_)
|
|
{
|
|
iscsi->is_loggedin = 0;
|
|
pdu->callback(iscsi, SCSI_STATUS_GOOD, NULL, pdu->private_data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
iscsi_set_session_type(struct iscsi_context *iscsi,
|
|
enum iscsi_session_type session_type)
|
|
{
|
|
if (iscsi->is_loggedin) {
|
|
iscsi_set_error(iscsi, "trying to set session type while "
|
|
"logged in");
|
|
return -1;
|
|
}
|
|
|
|
iscsi->session_type = session_type;
|
|
|
|
return 0;
|
|
}
|