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".
385 lines
8.2 KiB
C
385 lines
8.2 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/>.
|
|
*/
|
|
#define _GNU_SOURCE
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include "iscsi.h"
|
|
#include "iscsi-private.h"
|
|
#include "slist.h"
|
|
|
|
|
|
struct iscsi_context *
|
|
iscsi_create_context(const char *initiator_name)
|
|
{
|
|
struct iscsi_context *iscsi;
|
|
|
|
iscsi = malloc(sizeof(struct iscsi_context));
|
|
if (iscsi == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
bzero(iscsi, sizeof(struct iscsi_context));
|
|
|
|
iscsi->initiator_name = strdup(initiator_name);
|
|
if (iscsi->initiator_name == NULL) {
|
|
free(iscsi);
|
|
return NULL;
|
|
}
|
|
|
|
iscsi->fd = -1;
|
|
|
|
/* initialize to a "random" isid */
|
|
iscsi_set_isid_random(iscsi, getpid() ^ time(NULL));
|
|
|
|
/* assume we start in security negotiation phase */
|
|
iscsi->current_phase = ISCSI_PDU_LOGIN_CSG_SECNEG;
|
|
iscsi->next_phase = ISCSI_PDU_LOGIN_NSG_OPNEG;
|
|
iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP;
|
|
|
|
iscsi->max_burst_length = 262144;
|
|
iscsi->first_burst_length = 262144;
|
|
iscsi->max_recv_data_segment_length = 262144;
|
|
|
|
return iscsi;
|
|
}
|
|
|
|
int
|
|
iscsi_set_isid_random(struct iscsi_context *iscsi, int rnd)
|
|
{
|
|
iscsi->isid[0] = 0x80;
|
|
iscsi->isid[1] = rnd&0xff;
|
|
iscsi->isid[2] = rnd&0xff;
|
|
iscsi->isid[3] = rnd&0xff;
|
|
iscsi->isid[4] = 0;
|
|
iscsi->isid[5] = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
iscsi_set_alias(struct iscsi_context *iscsi, const char *alias)
|
|
{
|
|
if (iscsi->is_loggedin != 0) {
|
|
iscsi_set_error(iscsi, "Already logged in when adding alias");
|
|
return -1;
|
|
}
|
|
|
|
free(discard_const(iscsi->alias));
|
|
|
|
iscsi->alias = strdup(alias);
|
|
if (iscsi->alias == NULL) {
|
|
iscsi_set_error(iscsi, "Failed to allocate alias name");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
iscsi_set_targetname(struct iscsi_context *iscsi, const char *target_name)
|
|
{
|
|
if (iscsi->is_loggedin != 0) {
|
|
iscsi_set_error(iscsi, "Already logged in when adding "
|
|
"targetname");
|
|
return -1;
|
|
}
|
|
|
|
free(discard_const(iscsi->target_name));
|
|
|
|
iscsi->target_name = strdup(target_name);
|
|
if (iscsi->target_name == NULL) {
|
|
iscsi_set_error(iscsi, "Failed to allocate target name");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
iscsi_destroy_context(struct iscsi_context *iscsi)
|
|
{
|
|
struct iscsi_pdu *pdu;
|
|
|
|
if (iscsi == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (iscsi->fd != -1) {
|
|
iscsi_disconnect(iscsi);
|
|
}
|
|
|
|
while ((pdu = iscsi->outqueue)) {
|
|
SLIST_REMOVE(&iscsi->outqueue, pdu);
|
|
pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL,
|
|
pdu->private_data);
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
}
|
|
while ((pdu = iscsi->waitpdu)) {
|
|
SLIST_REMOVE(&iscsi->waitpdu, pdu);
|
|
pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL,
|
|
pdu->private_data);
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
}
|
|
|
|
free(discard_const(iscsi->initiator_name));
|
|
iscsi->initiator_name = NULL;
|
|
|
|
free(discard_const(iscsi->target_name));
|
|
iscsi->target_name = NULL;
|
|
|
|
free(discard_const(iscsi->alias));
|
|
iscsi->alias = NULL;
|
|
|
|
if (iscsi->incoming != NULL) {
|
|
iscsi_free_iscsi_in_pdu(iscsi->incoming);
|
|
}
|
|
if (iscsi->inqueue != NULL) {
|
|
iscsi_free_iscsi_inqueue(iscsi->inqueue);
|
|
}
|
|
|
|
free(iscsi->error_string);
|
|
iscsi->error_string = NULL;
|
|
|
|
free(discard_const(iscsi->user));
|
|
iscsi->user = NULL;
|
|
|
|
free(discard_const(iscsi->passwd));
|
|
iscsi->passwd = NULL;
|
|
|
|
|
|
free(discard_const(iscsi->chap_c));
|
|
iscsi->chap_c = NULL;
|
|
|
|
free(iscsi);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
iscsi_set_error(struct iscsi_context *iscsi, const char *error_string, ...)
|
|
{
|
|
va_list ap;
|
|
char *str;
|
|
|
|
va_start(ap, error_string);
|
|
if (vasprintf(&str, error_string, ap) < 0) {
|
|
/* not much we can do here */
|
|
str = NULL;
|
|
}
|
|
|
|
free(iscsi->error_string);
|
|
|
|
iscsi->error_string = str;
|
|
va_end(ap);
|
|
}
|
|
|
|
|
|
const char *
|
|
iscsi_get_error(struct iscsi_context *iscsi)
|
|
{
|
|
return iscsi->error_string;
|
|
}
|
|
|
|
int
|
|
iscsi_set_header_digest(struct iscsi_context *iscsi,
|
|
enum iscsi_header_digest header_digest)
|
|
{
|
|
if (iscsi->is_loggedin) {
|
|
iscsi_set_error(iscsi, "trying to set header digest while "
|
|
"logged in");
|
|
return -1;
|
|
}
|
|
|
|
iscsi->want_header_digest = header_digest;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
iscsi_is_logged_in(struct iscsi_context *iscsi)
|
|
{
|
|
return iscsi->is_loggedin;
|
|
}
|
|
|
|
struct iscsi_url *
|
|
iscsi_parse_full_url(struct iscsi_context *iscsi, const char *url)
|
|
{
|
|
struct iscsi_url *iscsi_url;
|
|
char *str;
|
|
char *portal;
|
|
char *user = NULL;
|
|
char *passwd = NULL;
|
|
char *target;
|
|
char *lun;
|
|
char *tmp;
|
|
int l;
|
|
|
|
if (strncmp(url, "iscsi://", 8)) {
|
|
iscsi_set_error(iscsi, "Invalid URL %s\niSCSI URL must be of "
|
|
"the form "
|
|
"\"iscsi://[<username>[%%<password>]@]"
|
|
"<host>[:<port>]/<target-iqn>/<lun>\"\n", url);
|
|
return NULL;
|
|
}
|
|
|
|
str = strdup(url + 8);
|
|
if (str == NULL) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: Failed to strdup url %s\n", url);
|
|
return NULL;
|
|
}
|
|
portal = str;
|
|
|
|
tmp = index(portal, '@');
|
|
if (tmp != NULL) {
|
|
user = portal;
|
|
*tmp++ = 0;
|
|
portal = tmp;
|
|
|
|
tmp = index(user, '%');
|
|
if (tmp != NULL) {
|
|
*tmp++ = 0;
|
|
passwd = tmp;
|
|
}
|
|
}
|
|
|
|
target = index(portal, '/');
|
|
if (target == NULL) {
|
|
iscsi_set_error(iscsi, "Invalid URL %s\nCould not parse "
|
|
"'<target-iqn>'\niSCSI URL must be of the form "
|
|
"\"iscsi://[<username>[%%<password>]@]"
|
|
"<host>[:<port>]/<target-iqn>/<lun>\"\n", url);
|
|
free(str);
|
|
return NULL;
|
|
}
|
|
*target++ = 0;
|
|
|
|
lun = index(target, '/');
|
|
if (lun == NULL) {
|
|
iscsi_set_error(iscsi, "Invalid URL %s\nCould not parse <lun>\n"
|
|
"iSCSI URL must be of the form \"iscsi://"
|
|
"<host>[:<port>]/<target-iqn>/<lun>\"\n", url);
|
|
free(str);
|
|
return NULL;
|
|
}
|
|
*lun++ = 0;
|
|
|
|
l = strtol(lun, &tmp, 10);
|
|
if (*lun == 0 || *tmp != 0) {
|
|
iscsi_set_error(iscsi, "Invalid URL %s\nCould not parse <lun>\n"
|
|
"iSCSI URL must be of the form \"iscsi://"
|
|
"<host>[:<port>]/<target-iqn>/<lun>\"\n",
|
|
url);
|
|
free(str);
|
|
return NULL;
|
|
}
|
|
|
|
iscsi_url = malloc(sizeof(struct iscsi_url));
|
|
if (iscsi_url == NULL) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: Failed to allocate iscsi_url structure\n");
|
|
free(str);
|
|
return NULL;
|
|
}
|
|
memset(iscsi_url, 0, sizeof(struct iscsi_url));
|
|
|
|
iscsi_url->portal = strdup(portal);
|
|
if (iscsi_url->portal == NULL) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: Failed to strdup portal string\n");
|
|
iscsi_destroy_url(iscsi_url);
|
|
free(str);
|
|
return NULL;
|
|
}
|
|
|
|
iscsi_url->target = strdup(target);
|
|
if (iscsi_url->target == NULL) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: Failed to strdup target string\n");
|
|
iscsi_destroy_url(iscsi_url);
|
|
free(str);
|
|
return NULL;
|
|
}
|
|
|
|
if (user != NULL) {
|
|
iscsi_url->user = strdup(user);
|
|
if (iscsi_url->user == NULL) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: Failed to strdup username string\n");
|
|
iscsi_destroy_url(iscsi_url);
|
|
free(str);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (passwd != NULL) {
|
|
iscsi_url->passwd = strdup(passwd);
|
|
if (iscsi_url->passwd == NULL) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: Failed to strdup password string\n");
|
|
iscsi_destroy_url(iscsi_url);
|
|
free(str);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
iscsi_url->lun = l;
|
|
free(str);
|
|
return iscsi_url;
|
|
}
|
|
|
|
void
|
|
iscsi_destroy_url(struct iscsi_url *iscsi_url)
|
|
{
|
|
if (iscsi_url == NULL) {
|
|
return;
|
|
}
|
|
|
|
free(discard_const(iscsi_url->portal));
|
|
free(discard_const(iscsi_url->target));
|
|
free(discard_const(iscsi_url->user));
|
|
free(discard_const(iscsi_url->passwd));
|
|
free(iscsi_url);
|
|
}
|
|
|
|
|
|
int
|
|
iscsi_set_initiator_username_pwd(struct iscsi_context *iscsi,
|
|
const char *user,
|
|
const char *passwd)
|
|
{
|
|
free(discard_const(iscsi->user));
|
|
iscsi->user = strdup(user);
|
|
if (iscsi->user == NULL) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: failed to strdup username\n");
|
|
return -1;
|
|
}
|
|
|
|
free(discard_const(iscsi->passwd));
|
|
iscsi->passwd = strdup(passwd);
|
|
if (iscsi->passwd == NULL) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: failed to strdup password\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|