Add an internal libiscsiutil library.

Move some of the code duplicated between ctld(8) and iscsid(8) into a
libiscsiutil library.

Sharing the low-level PDU code did require having a
'struct connection' base class with a method table to permit separate
initiator vs target behavior (e.g. in handling proxy PDUs).

Reviewed by:	mav, emaste
Sponsored by:	Chelsio Communications
Differential Revision:	https://reviews.freebsd.org/D33544
This commit is contained in:
John Baldwin 2021-12-22 10:35:46 -08:00
parent d2ef377430
commit 6378393308
27 changed files with 582 additions and 1571 deletions

View File

@ -63,6 +63,7 @@ SUBDIR= ${SUBDIR_BOOTSTRAP} \
libgeom \
libifconfig \
libipsec \
libiscsiutil \
libjail \
libkiconv \
libkvm \

10
lib/libiscsiutil/Makefile Normal file
View File

@ -0,0 +1,10 @@
LIB= iscsiutil
INTERNALLIB=
PACKAGE= iscsi
INCS= libiscsiutil.h
SRCS= chap.c connection.c keys.c log.c pdu.c utils.c
CFLAGS+= -I${SRCTOP}/sys/dev/iscsi
.include <bsd.lib.mk>

View File

@ -26,12 +26,8 @@
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <assert.h>
#include <stdlib.h>
#include <string.h>
@ -39,7 +35,7 @@ __FBSDID("$FreeBSD$");
#include <resolv.h>
#include <md5.h>
#include "ctld.h"
#include "libiscsiutil.h"
static void
chap_compute_md5(const char id, const char *secret,

View File

@ -0,0 +1,53 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2012 The FreeBSD Foundation
*
* This software was developed by Edward Tomasz Napierala under sponsorship
* from the FreeBSD Foundation.
*
* 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 AUTHOR 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 AUTHOR 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 <string.h>
#include "libiscsiutil.h"
void
connection_init(struct connection *conn, const struct connection_ops *ops,
bool use_proxy)
{
memset(conn, 0, sizeof(*conn));
conn->conn_ops = ops;
conn->conn_use_proxy = use_proxy;
/*
* Default values, from RFC 3720, section 12.
*/
conn->conn_header_digest = CONN_DIGEST_NONE;
conn->conn_data_digest = CONN_DIGEST_NONE;
conn->conn_immediate_data = true;
conn->conn_max_recv_data_segment_length = 8192;
conn->conn_max_send_data_segment_length = 8192;
conn->conn_max_burst_length = 262144;
conn->conn_first_burst_length = 65536;
}

View File

@ -26,18 +26,14 @@
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ctld.h"
#include "libiscsiutil.h"
struct keys *
keys_new(void)

View File

@ -0,0 +1,148 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2012 The FreeBSD Foundation
*
* This software was developed by Edward Tomasz Napierala under sponsorship
* from the FreeBSD Foundation.
*
* 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 AUTHOR 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 AUTHOR 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 __LIBISCSIUTIL_H__
#define __LIBISCSIUTIL_H__
#include <sys/types.h>
#include <stdbool.h>
struct connection_ops;
#define CONN_DIGEST_NONE 0
#define CONN_DIGEST_CRC32C 1
struct connection {
const struct connection_ops *conn_ops;
int conn_socket;
uint8_t conn_isid[6];
uint16_t conn_tsih;
uint32_t conn_cmdsn;
uint32_t conn_statsn;
int conn_header_digest;
int conn_data_digest;
bool conn_immediate_data;
bool conn_use_proxy;
int conn_max_recv_data_segment_length;
int conn_max_send_data_segment_length;
int conn_max_burst_length;
int conn_first_burst_length;
};
struct pdu {
struct connection *pdu_connection;
struct iscsi_bhs *pdu_bhs;
char *pdu_data;
size_t pdu_data_len;
};
struct connection_ops {
bool (*timed_out)(void);
void (*pdu_receive_proxy)(struct pdu *);
void (*pdu_send_proxy)(struct pdu *);
void (*fail)(const struct connection *, const char *);
};
#define KEYS_MAX 1024
struct keys {
char *keys_names[KEYS_MAX];
char *keys_values[KEYS_MAX];
char *keys_data;
size_t keys_data_len;
};
#define CHAP_CHALLENGE_LEN 1024
#define CHAP_DIGEST_LEN 16 /* Equal to MD5 digest size. */
struct chap {
unsigned char chap_id;
char chap_challenge[CHAP_CHALLENGE_LEN];
char chap_response[CHAP_DIGEST_LEN];
};
struct rchap {
char *rchap_secret;
unsigned char rchap_id;
void *rchap_challenge;
size_t rchap_challenge_len;
};
struct chap *chap_new(void);
char *chap_get_id(const struct chap *chap);
char *chap_get_challenge(const struct chap *chap);
int chap_receive(struct chap *chap, const char *response);
int chap_authenticate(struct chap *chap,
const char *secret);
void chap_delete(struct chap *chap);
struct rchap *rchap_new(const char *secret);
int rchap_receive(struct rchap *rchap,
const char *id, const char *challenge);
char *rchap_get_response(struct rchap *rchap);
void rchap_delete(struct rchap *rchap);
struct keys *keys_new(void);
void keys_delete(struct keys *key);
void keys_load(struct keys *keys, const struct pdu *pdu);
void keys_save(struct keys *keys, struct pdu *pdu);
const char *keys_find(struct keys *keys, const char *name);
void keys_add(struct keys *keys,
const char *name, const char *value);
void keys_add_int(struct keys *keys,
const char *name, int value);
struct pdu *pdu_new(struct connection *ic);
struct pdu *pdu_new_response(struct pdu *request);
int pdu_ahs_length(const struct pdu *pdu);
int pdu_data_segment_length(const struct pdu *pdu);
void pdu_set_data_segment_length(struct pdu *pdu,
uint32_t len);
void pdu_receive(struct pdu *request);
void pdu_send(struct pdu *response);
void pdu_delete(struct pdu *ip);
void connection_init(struct connection *conn,
const struct connection_ops *ops, bool use_proxy);
void log_init(int level);
void log_set_peer_name(const char *name);
void log_set_peer_addr(const char *addr);
void log_err(int, const char *, ...)
__dead2 __printflike(2, 3);
void log_errx(int, const char *, ...)
__dead2 __printflike(2, 3);
void log_warn(const char *, ...) __printflike(1, 2);
void log_warnx(const char *, ...) __printflike(1, 2);
void log_debugx(const char *, ...) __printflike(1, 2);
char *checked_strdup(const char *);
#endif /* !__LIBISCSIUTIL_H__ */

View File

@ -26,12 +26,8 @@
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
@ -40,7 +36,7 @@ __FBSDID("$FreeBSD$");
#include <syslog.h>
#include <vis.h>
#include "ctld.h"
#include "libiscsiutil.h"
static int log_level = 0;
static char *peer_name = NULL;

View File

@ -35,26 +35,22 @@ __FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/uio.h>
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "ctld.h"
#include "iscsi_proto.h"
#include <iscsi_proto.h>
#include "libiscsiutil.h"
#ifdef ICL_KERNEL_PROXY
#include <sys/ioctl.h>
#endif
extern bool proxy_mode;
static int
int
pdu_ahs_length(const struct pdu *pdu)
{
return (pdu->pdu_bhs->bhs_total_ahs_len * 4);
}
static int
int
pdu_data_segment_length(const struct pdu *pdu)
{
uint32_t len = 0;
@ -68,7 +64,7 @@ pdu_data_segment_length(const struct pdu *pdu)
return (len);
}
static void
void
pdu_set_data_segment_length(struct pdu *pdu, uint32_t len)
{
@ -102,40 +98,6 @@ pdu_new_response(struct pdu *request)
return (pdu_new(request->pdu_connection));
}
#ifdef ICL_KERNEL_PROXY
static void
pdu_receive_proxy(struct pdu *pdu)
{
struct connection *conn;
size_t len;
assert(proxy_mode);
conn = pdu->pdu_connection;
kernel_receive(pdu);
len = pdu_ahs_length(pdu);
if (len > 0)
log_errx(1, "protocol error: non-empty AHS");
len = pdu_data_segment_length(pdu);
assert(len <= (size_t)conn->conn_max_recv_data_segment_length);
pdu->pdu_data_len = len;
}
static void
pdu_send_proxy(struct pdu *pdu)
{
assert(proxy_mode);
pdu_set_data_segment_length(pdu, pdu->pdu_data_len);
kernel_send(pdu);
}
#endif /* ICL_KERNEL_PROXY */
static size_t
pdu_padding(const struct pdu *pdu)
{
@ -147,18 +109,24 @@ pdu_padding(const struct pdu *pdu)
}
static void
pdu_read(int fd, char *data, size_t len)
pdu_read(const struct connection *conn, char *data, size_t len)
{
ssize_t ret;
while (len > 0) {
ret = read(fd, data, len);
ret = read(conn->conn_socket, data, len);
if (ret < 0) {
if (timed_out())
if (conn->conn_ops->timed_out()) {
conn->conn_ops->fail(conn,
"Login Phase timeout");
log_errx(1, "exiting due to timeout");
}
conn->conn_ops->fail(conn, strerror(errno));
log_err(1, "read");
} else if (ret == 0)
} else if (ret == 0) {
conn->conn_ops->fail(conn, "connection lost");
log_errx(1, "read: connection lost");
}
len -= ret;
data += ret;
}
@ -171,16 +139,11 @@ pdu_receive(struct pdu *pdu)
size_t len, padding;
char dummy[4];
#ifdef ICL_KERNEL_PROXY
if (proxy_mode)
return (pdu_receive_proxy(pdu));
#endif
assert(proxy_mode == false);
conn = pdu->pdu_connection;
if (conn->conn_use_proxy)
return (conn->conn_ops->pdu_receive_proxy(pdu));
pdu_read(conn->conn_socket, (char *)pdu->pdu_bhs,
sizeof(*pdu->pdu_bhs));
pdu_read(conn, (char *)pdu->pdu_bhs, sizeof(*pdu->pdu_bhs));
len = pdu_ahs_length(pdu);
if (len > 0)
@ -199,13 +162,12 @@ pdu_receive(struct pdu *pdu)
if (pdu->pdu_data == NULL)
log_err(1, "malloc");
pdu_read(conn->conn_socket, (char *)pdu->pdu_data,
pdu->pdu_data_len);
pdu_read(conn, (char *)pdu->pdu_data, pdu->pdu_data_len);
padding = pdu_padding(pdu);
if (padding != 0) {
assert(padding < sizeof(dummy));
pdu_read(conn->conn_socket, (char *)dummy, padding);
pdu_read(conn, (char *)dummy, padding);
}
}
}
@ -213,18 +175,16 @@ pdu_receive(struct pdu *pdu)
void
pdu_send(struct pdu *pdu)
{
struct connection *conn;
ssize_t ret, total_len;
size_t padding;
uint32_t zero = 0;
struct iovec iov[3];
int iovcnt;
#ifdef ICL_KERNEL_PROXY
if (proxy_mode)
return (pdu_send_proxy(pdu));
#endif
assert(proxy_mode == false);
conn = pdu->pdu_connection;
if (conn->conn_use_proxy)
return (conn->conn_ops->pdu_send_proxy(pdu));
pdu_set_data_segment_length(pdu, pdu->pdu_data_len);
iov[0].iov_base = pdu->pdu_bhs;
@ -248,9 +208,9 @@ pdu_send(struct pdu *pdu)
}
}
ret = writev(pdu->pdu_connection->conn_socket, iov, iovcnt);
ret = writev(conn->conn_socket, iov, iovcnt);
if (ret < 0) {
if (timed_out())
if (conn->conn_ops->timed_out())
log_errx(1, "exiting due to timeout");
log_err(1, "writev");
}

44
lib/libiscsiutil/utils.c Normal file
View File

@ -0,0 +1,44 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2012 The FreeBSD Foundation
*
* This software was developed by Edward Tomasz Napierala under sponsorship
* from the FreeBSD Foundation.
*
* 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 AUTHOR 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 AUTHOR 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 <string.h>
#include "libiscsiutil.h"
char *
checked_strdup(const char *s)
{
char *c;
c = strdup(s);
if (c == NULL)
log_err(1, "strdup");
return (c);
}

View File

@ -236,6 +236,9 @@ CRUNCH_LIBS+= -lm
.if ${MK_ISCSI} != "no"
CRUNCH_PROGS_usr.bin+= iscsictl
CRUNCH_PROGS_usr.sbin+= iscsid
CRUNCH_LIBS+= ${OBJTOP}/lib/libiscsiutil/libiscsiutil.a
CRUNCH_BUILDOPTS+= CRUNCH_CFLAGS+=-I${OBJTOP}/lib/libiscsiutil
.endif
.include <bsd.crunchgen.mk>

View File

@ -83,6 +83,7 @@ LIBIBVERBS?= ${LIBDESTDIR}${LIBDIR_BASE}/libibverbs.a
LIBICP?= ${LIBDESTDIR}${LIBDIR_BASE}/libicp.a
LIBIPSEC?= ${LIBDESTDIR}${LIBDIR_BASE}/libipsec.a
LIBIPT?= ${LIBDESTDIR}${LIBDIR_BASE}/libipt.a
LIBISCSIUTIL?= ${LIBDESTDIR}${LIBDIR_BASE}/libiscsiutil.a
LIBJAIL?= ${LIBDESTDIR}${LIBDIR_BASE}/libjail.a
LIBKADM5CLNT?= ${LIBDESTDIR}${LIBDIR_BASE}/libkadm5clnt.a
LIBKADM5SRV?= ${LIBDESTDIR}${LIBDIR_BASE}/libkadm5srv.a

View File

@ -44,6 +44,7 @@ _INTERNALLIBS= \
fifolog \
ifconfig \
ipf \
iscsiutil \
lpr \
lua \
lutok \
@ -556,6 +557,9 @@ LIBIFCONFIG?= ${LIBIFCONFIGDIR}/libifconfig${PIE_SUFFIX}.a
LIBIPFDIR= ${_LIB_OBJTOP}/sbin/ipf/libipf
LIBIPF?= ${LIBIPFDIR}/libipf${PIE_SUFFIX}.a
LIBISCSIUTILDIR= ${_LIB_OBJTOP}/lib/libiscsiutil
LIBISCSIUTIL?= ${LIBISCSIUTILDIR}/libiscsiutil${PIE_SUFFIX}.a
LIBTELNETDIR= ${_LIB_OBJTOP}/lib/libtelnet
LIBTELNET?= ${LIBTELNETDIR}/libtelnet${PIE_SUFFIX}.a

View File

@ -7,16 +7,17 @@ CFLAGS+=-I${SRCTOP}/contrib/libucl/include
PACKAGE= iscsi
PROG= ctld
SRCS= chap.c ctld.c discovery.c isns.c kernel.c keys.c log.c
SRCS+= login.c parse.y pdu.c token.l y.tab.h uclparse.c
SRCS= ctld.c discovery.c isns.c kernel.c
SRCS+= login.c parse.y token.l y.tab.h uclparse.c
CFLAGS+= -I${.CURDIR}
CFLAGS+= -I${SRCTOP}/sys
CFLAGS+= -I${SRCTOP}/sys/cam/ctl
CFLAGS+= -I${SRCTOP}/sys/dev/iscsi
CFLAGS+= -I${SRCTOP}/lib/libiscsiutil
#CFLAGS+= -DICL_KERNEL_PROXY
MAN= ctld.8 ctl.conf.5
LIBADD= bsdxml md sbuf util ucl m nv
LIBADD= bsdxml iscsiutil md sbuf util ucl m nv
YFLAGS+= -v
CLEANFILES= y.tab.c y.tab.h y.output

View File

@ -54,6 +54,13 @@ __FBSDID("$FreeBSD$");
#include "ctld.h"
#include "isns.h"
static bool timed_out(void);
#ifdef ICL_KERNEL_PROXY
static void pdu_receive_proxy(struct pdu *pdu);
static void pdu_send_proxy(struct pdu *pdu);
#endif /* ICL_KERNEL_PROXY */
static void pdu_fail(const struct connection *conn, const char *reason);
bool proxy_mode = false;
static volatile bool sighup_received = false;
@ -63,6 +70,15 @@ static volatile bool sigalrm_received = false;
static int nchildren = 0;
static uint16_t last_portal_group_tag = 0xff;
static struct connection_ops conn_ops = {
.timed_out = timed_out,
#ifdef ICL_KERNEL_PROXY
.pdu_receive_proxy = pdu_receive_proxy,
.pdu_send_proxy = pdu_send_proxy,
#endif
.fail = pdu_fail,
};
static void
usage(void)
{
@ -72,17 +88,6 @@ usage(void)
exit(1);
}
char *
checked_strdup(const char *s)
{
char *c;
c = strdup(s);
if (c == NULL)
log_err(1, "strdup");
return (c);
}
struct conf *
conf_new(void)
{
@ -1632,29 +1637,60 @@ option_set(struct option *o, const char *value)
o->o_value = checked_strdup(value);
}
static struct connection *
#ifdef ICL_KERNEL_PROXY
static void
pdu_receive_proxy(struct pdu *pdu)
{
struct connection *conn;
size_t len;
assert(proxy_mode);
conn = pdu->pdu_connection;
kernel_receive(pdu);
len = pdu_ahs_length(pdu);
if (len > 0)
log_errx(1, "protocol error: non-empty AHS");
len = pdu_data_segment_length(pdu);
assert(len <= (size_t)conn->conn_max_recv_data_segment_length);
pdu->pdu_data_len = len;
}
static void
pdu_send_proxy(struct pdu *pdu)
{
assert(proxy_mode);
pdu_set_data_segment_length(pdu, pdu->pdu_data_len);
kernel_send(pdu);
}
#endif /* ICL_KERNEL_PROXY */
static void
pdu_fail(const struct connection *conn __unused, const char *reason __unused)
{
}
static struct ctld_connection *
connection_new(struct portal *portal, int fd, const char *host,
const struct sockaddr *client_sa)
{
struct connection *conn;
struct ctld_connection *conn;
conn = calloc(1, sizeof(*conn));
if (conn == NULL)
log_err(1, "calloc");
connection_init(&conn->conn, &conn_ops, proxy_mode);
conn->conn.conn_socket = fd;
conn->conn_portal = portal;
conn->conn_socket = fd;
conn->conn_initiator_addr = checked_strdup(host);
memcpy(&conn->conn_initiator_sa, client_sa, client_sa->sa_len);
/*
* Default values, from RFC 3720, section 12.
*/
conn->conn_max_recv_data_segment_length = 8192;
conn->conn_max_send_data_segment_length = 8192;
conn->conn_max_burst_length = 262144;
conn->conn_first_burst_length = 65536;
conn->conn_immediate_data = true;
return (conn);
}
@ -2296,7 +2332,7 @@ conf_apply(struct conf *oldconf, struct conf *newconf)
return (cumulated_error);
}
bool
static bool
timed_out(void)
{
@ -2407,7 +2443,7 @@ static void
handle_connection(struct portal *portal, int fd,
const struct sockaddr *client_sa, bool dont_fork)
{
struct connection *conn;
struct ctld_connection *conn;
int error;
pid_t pid;
char host[NI_MAXHOST + 1];

View File

@ -39,6 +39,7 @@
#endif
#include <sys/socket.h>
#include <stdbool.h>
#include <libiscsiutil.h>
#include <libutil.h>
#define DEFAULT_CONFIG_PATH "/etc/ctl.conf"
@ -229,83 +230,25 @@ struct conf {
#define CONN_SESSION_TYPE_DISCOVERY 1
#define CONN_SESSION_TYPE_NORMAL 2
#define CONN_DIGEST_NONE 0
#define CONN_DIGEST_CRC32C 1
struct connection {
struct ctld_connection {
struct connection conn;
struct portal *conn_portal;
struct port *conn_port;
struct target *conn_target;
int conn_socket;
int conn_session_type;
char *conn_initiator_name;
char *conn_initiator_addr;
char *conn_initiator_alias;
uint8_t conn_initiator_isid[6];
struct sockaddr_storage conn_initiator_sa;
uint32_t conn_cmdsn;
uint32_t conn_statsn;
int conn_max_recv_data_segment_limit;
int conn_max_send_data_segment_limit;
int conn_max_burst_limit;
int conn_first_burst_limit;
int conn_max_recv_data_segment_length;
int conn_max_send_data_segment_length;
int conn_max_burst_length;
int conn_first_burst_length;
int conn_immediate_data;
int conn_header_digest;
int conn_data_digest;
const char *conn_user;
struct chap *conn_chap;
};
struct pdu {
struct connection *pdu_connection;
struct iscsi_bhs *pdu_bhs;
char *pdu_data;
size_t pdu_data_len;
};
#define KEYS_MAX 1024
struct keys {
char *keys_names[KEYS_MAX];
char *keys_values[KEYS_MAX];
char *keys_data;
size_t keys_data_len;
};
#define CHAP_CHALLENGE_LEN 1024
#define CHAP_DIGEST_LEN 16 /* Equal to MD5 digest size. */
struct chap {
unsigned char chap_id;
char chap_challenge[CHAP_CHALLENGE_LEN];
char chap_response[CHAP_DIGEST_LEN];
};
struct rchap {
char *rchap_secret;
unsigned char rchap_id;
void *rchap_challenge;
size_t rchap_challenge_len;
};
struct chap *chap_new(void);
char *chap_get_id(const struct chap *chap);
char *chap_get_challenge(const struct chap *chap);
int chap_receive(struct chap *chap, const char *response);
int chap_authenticate(struct chap *chap,
const char *secret);
void chap_delete(struct chap *chap);
struct rchap *rchap_new(const char *secret);
int rchap_receive(struct rchap *rchap,
const char *id, const char *challenge);
char *rchap_get_response(struct rchap *rchap);
void rchap_delete(struct rchap *rchap);
int parse_conf(struct conf *conf, const char *path);
int uclparse_conf(struct conf *conf, const char *path);
@ -412,7 +355,7 @@ void kernel_init(void);
int kernel_lun_add(struct lun *lun);
int kernel_lun_modify(struct lun *lun);
int kernel_lun_remove(struct lun *lun);
void kernel_handoff(struct connection *conn);
void kernel_handoff(struct ctld_connection *conn);
void kernel_limits(const char *offload,
int *max_recv_data_segment_length,
int *max_send_data_segment_length,
@ -433,40 +376,11 @@ void kernel_send(struct pdu *pdu);
void kernel_receive(struct pdu *pdu);
#endif
struct keys *keys_new(void);
void keys_delete(struct keys *keys);
void keys_load(struct keys *keys, const struct pdu *pdu);
void keys_save(struct keys *keys, struct pdu *pdu);
const char *keys_find(struct keys *keys, const char *name);
void keys_add(struct keys *keys,
const char *name, const char *value);
void keys_add_int(struct keys *keys,
const char *name, int value);
void login(struct ctld_connection *conn);
struct pdu *pdu_new(struct connection *conn);
struct pdu *pdu_new_response(struct pdu *request);
void pdu_delete(struct pdu *pdu);
void pdu_receive(struct pdu *request);
void pdu_send(struct pdu *response);
void discovery(struct ctld_connection *conn);
void login(struct connection *conn);
void discovery(struct connection *conn);
void log_init(int level);
void log_set_peer_name(const char *name);
void log_set_peer_addr(const char *addr);
void log_err(int, const char *, ...)
__dead2 __printflike(2, 3);
void log_errx(int, const char *, ...)
__dead2 __printflike(2, 3);
void log_warn(const char *, ...) __printflike(1, 2);
void log_warnx(const char *, ...) __printflike(1, 2);
void log_debugx(const char *, ...) __printflike(1, 2);
char *checked_strdup(const char *);
bool valid_iscsi_name(const char *name);
void set_timeout(int timeout, int fatal);
bool timed_out(void);
#endif /* !CTLD_H */

View File

@ -211,7 +211,7 @@ discovery_add_target(struct keys *response_keys, const struct target *targ)
}
static bool
discovery_target_filtered_out(const struct connection *conn,
discovery_target_filtered_out(const struct ctld_connection *conn,
const struct port *port)
{
const struct auth_group *ag;
@ -274,7 +274,7 @@ discovery_target_filtered_out(const struct connection *conn,
}
void
discovery(struct connection *conn)
discovery(struct ctld_connection *conn)
{
struct pdu *request, *response;
struct keys *request_keys, *response_keys;
@ -285,7 +285,7 @@ discovery(struct connection *conn)
pg = conn->conn_portal->p_portal_group;
log_debugx("beginning discovery session; waiting for Text PDU");
request = text_receive(conn);
request = text_receive(&conn->conn);
request_keys = keys_new();
keys_load(request_keys, request);
@ -326,7 +326,7 @@ discovery(struct connection *conn)
keys_delete(request_keys);
log_debugx("done sending targets; waiting for Logout PDU");
request = logout_receive(conn);
request = logout_receive(&conn->conn);
response = logout_new_response(request);
pdu_send(response);

View File

@ -893,7 +893,7 @@ kernel_lun_remove(struct lun *lun)
}
void
kernel_handoff(struct connection *conn)
kernel_handoff(struct ctld_connection *conn)
{
struct ctl_iscsi req;
@ -919,27 +919,28 @@ kernel_handoff(struct connection *conn)
}
#ifdef ICL_KERNEL_PROXY
if (proxy_mode)
req.data.handoff.connection_id = conn->conn_socket;
req.data.handoff.connection_id = conn->conn.conn_socket;
else
req.data.handoff.socket = conn->conn_socket;
req.data.handoff.socket = conn->conn.conn_socket;
#else
req.data.handoff.socket = conn->conn_socket;
req.data.handoff.socket = conn->conn.conn_socket;
#endif
req.data.handoff.portal_group_tag =
conn->conn_portal->p_portal_group->pg_tag;
if (conn->conn_header_digest == CONN_DIGEST_CRC32C)
if (conn->conn.conn_header_digest == CONN_DIGEST_CRC32C)
req.data.handoff.header_digest = CTL_ISCSI_DIGEST_CRC32C;
if (conn->conn_data_digest == CONN_DIGEST_CRC32C)
if (conn->conn.conn_data_digest == CONN_DIGEST_CRC32C)
req.data.handoff.data_digest = CTL_ISCSI_DIGEST_CRC32C;
req.data.handoff.cmdsn = conn->conn_cmdsn;
req.data.handoff.statsn = conn->conn_statsn;
req.data.handoff.cmdsn = conn->conn.conn_cmdsn;
req.data.handoff.statsn = conn->conn.conn_statsn;
req.data.handoff.max_recv_data_segment_length =
conn->conn_max_recv_data_segment_length;
conn->conn.conn_max_recv_data_segment_length;
req.data.handoff.max_send_data_segment_length =
conn->conn_max_send_data_segment_length;
req.data.handoff.max_burst_length = conn->conn_max_burst_length;
req.data.handoff.first_burst_length = conn->conn_first_burst_length;
req.data.handoff.immediate_data = conn->conn_immediate_data;
conn->conn.conn_max_send_data_segment_length;
req.data.handoff.max_burst_length = conn->conn.conn_max_burst_length;
req.data.handoff.first_burst_length =
conn->conn.conn_first_burst_length;
req.data.handoff.immediate_data = conn->conn.conn_immediate_data;
if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) {
log_err(1, "error issuing CTL_ISCSI ioctl; "

View File

@ -401,7 +401,7 @@ login_send_chap_success(struct pdu *request,
}
static void
login_chap(struct connection *conn, struct auth_group *ag)
login_chap(struct ctld_connection *conn, struct auth_group *ag)
{
const struct auth *auth;
struct chap *chap;
@ -411,7 +411,7 @@ login_chap(struct connection *conn, struct auth_group *ag)
* Receive CHAP_A PDU.
*/
log_debugx("beginning CHAP authentication; waiting for CHAP_A");
request = login_receive_chap_a(conn);
request = login_receive_chap_a(&conn->conn);
/*
* Generate the challenge.
@ -430,7 +430,7 @@ login_chap(struct connection *conn, struct auth_group *ag)
* Receive CHAP_N/CHAP_R PDU and authenticate.
*/
log_debugx("waiting for CHAP_N/CHAP_R");
request = login_receive_chap_r(conn, ag, chap, &auth);
request = login_receive_chap_r(&conn->conn, ag, chap, &auth);
/*
* Yay, authentication succeeded!
@ -453,9 +453,9 @@ login_negotiate_key(struct pdu *request, const char *name,
{
int which;
size_t tmp;
struct connection *conn;
struct ctld_connection *conn;
conn = request->pdu_connection;
conn = (struct ctld_connection *)request->pdu_connection;
if (strcmp(name, "InitiatorName") == 0) {
if (!skipped_security)
@ -487,7 +487,7 @@ login_negotiate_key(struct pdu *request, const char *name,
case 1:
log_debugx("initiator prefers CRC32C "
"for header digest; we'll use it");
conn->conn_header_digest = CONN_DIGEST_CRC32C;
conn->conn.conn_header_digest = CONN_DIGEST_CRC32C;
keys_add(response_keys, name, "CRC32C");
break;
case 2:
@ -513,7 +513,7 @@ login_negotiate_key(struct pdu *request, const char *name,
case 1:
log_debugx("initiator prefers CRC32C "
"for data digest; we'll use it");
conn->conn_data_digest = CONN_DIGEST_CRC32C;
conn->conn.conn_data_digest = CONN_DIGEST_CRC32C;
keys_add(response_keys, name, "CRC32C");
break;
case 2:
@ -537,10 +537,10 @@ login_negotiate_key(struct pdu *request, const char *name,
keys_add(response_keys, name, "Irrelevant");
} else {
if (strcmp(value, "Yes") == 0) {
conn->conn_immediate_data = true;
conn->conn.conn_immediate_data = true;
keys_add(response_keys, name, "Yes");
} else {
conn->conn_immediate_data = false;
conn->conn.conn_immediate_data = false;
keys_add(response_keys, name, "No");
}
}
@ -564,7 +564,7 @@ login_negotiate_key(struct pdu *request, const char *name,
conn->conn_max_send_data_segment_limit);
tmp = conn->conn_max_send_data_segment_limit;
}
conn->conn_max_send_data_segment_length = tmp;
conn->conn.conn_max_send_data_segment_length = tmp;
} else if (strcmp(name, "MaxBurstLength") == 0) {
tmp = strtoul(value, NULL, 10);
if (tmp <= 0) {
@ -576,7 +576,7 @@ login_negotiate_key(struct pdu *request, const char *name,
tmp, conn->conn_max_burst_limit);
tmp = conn->conn_max_burst_limit;
}
conn->conn_max_burst_length = tmp;
conn->conn.conn_max_burst_length = tmp;
keys_add_int(response_keys, name, tmp);
} else if (strcmp(name, "FirstBurstLength") == 0) {
tmp = strtoul(value, NULL, 10);
@ -589,7 +589,7 @@ login_negotiate_key(struct pdu *request, const char *name,
tmp, conn->conn_first_burst_limit);
tmp = conn->conn_first_burst_limit;
}
conn->conn_first_burst_length = tmp;
conn->conn.conn_first_burst_length = tmp;
keys_add_int(response_keys, name, tmp);
} else if (strcmp(name, "DefaultTime2Wait") == 0) {
keys_add(response_keys, name, value);
@ -642,7 +642,7 @@ login_redirect(struct pdu *request, const char *target_address)
}
static bool
login_portal_redirect(struct connection *conn, struct pdu *request)
login_portal_redirect(struct ctld_connection *conn, struct pdu *request)
{
const struct portal_group *pg;
@ -658,7 +658,7 @@ login_portal_redirect(struct connection *conn, struct pdu *request)
}
static bool
login_target_redirect(struct connection *conn, struct pdu *request)
login_target_redirect(struct ctld_connection *conn, struct pdu *request)
{
const char *target_address;
@ -679,7 +679,7 @@ login_target_redirect(struct connection *conn, struct pdu *request)
}
static void
login_negotiate(struct connection *conn, struct pdu *request)
login_negotiate(struct ctld_connection *conn, struct pdu *request)
{
struct pdu *response;
struct iscsi_bhs_login_response *bhslr2;
@ -721,8 +721,8 @@ login_negotiate(struct connection *conn, struct pdu *request)
* sender and receiver operation, and we must obey defaults.
*/
if (conn->conn_max_send_data_segment_limit <
conn->conn_max_send_data_segment_length) {
conn->conn_max_send_data_segment_length =
conn->conn.conn_max_send_data_segment_length) {
conn->conn.conn_max_send_data_segment_length =
conn->conn_max_send_data_segment_limit;
}
} else {
@ -735,7 +735,7 @@ login_negotiate(struct connection *conn, struct pdu *request)
if (request == NULL) {
log_debugx("beginning operational parameter negotiation; "
"waiting for Login PDU");
request = login_receive(conn, false);
request = login_receive(&conn->conn, false);
skipped_security = false;
} else
skipped_security = true;
@ -788,14 +788,15 @@ login_negotiate(struct connection *conn, struct pdu *request)
* with illegal values here.
*/
if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL &&
conn->conn_first_burst_length > conn->conn_max_burst_length) {
conn->conn.conn_first_burst_length >
conn->conn.conn_max_burst_length) {
log_errx(1, "initiator sent FirstBurstLength > MaxBurstLength");
}
conn->conn_max_recv_data_segment_length =
conn->conn.conn_max_recv_data_segment_length =
conn->conn_max_recv_data_segment_limit;
keys_add_int(response_keys, "MaxRecvDataSegmentLength",
conn->conn_max_recv_data_segment_length);
conn->conn.conn_max_recv_data_segment_length);
log_debugx("operational parameter negotiation done; "
"transitioning to Full Feature Phase");
@ -809,13 +810,13 @@ login_negotiate(struct connection *conn, struct pdu *request)
}
static void
login_wait_transition(struct connection *conn)
login_wait_transition(struct ctld_connection *conn)
{
struct pdu *request, *response;
struct iscsi_bhs_login_request *bhslr;
log_debugx("waiting for state transition request");
request = login_receive(conn, false);
request = login_receive(&conn->conn, false);
bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs;
if ((bhslr->bhslr_flags & BHSLR_FLAGS_TRANSIT) == 0) {
login_send_error(request, 0x02, 0x00);
@ -833,7 +834,7 @@ login_wait_transition(struct connection *conn)
}
void
login(struct connection *conn)
login(struct ctld_connection *conn)
{
struct pdu *request, *response;
struct iscsi_bhs_login_request *bhslr;
@ -850,7 +851,7 @@ login(struct connection *conn)
* is required, or call appropriate authentication code.
*/
log_debugx("beginning Login Phase; waiting for Login PDU");
request = login_receive(conn, true);
request = login_receive(&conn->conn, true);
bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs;
if (bhslr->bhslr_tsih != 0) {
login_send_error(request, 0x02, 0x0a);

View File

@ -2,13 +2,14 @@
PACKAGE= iscsi
PROG= iscsid
SRCS= chap.c discovery.c iscsid.c keys.c log.c login.c pdu.c
SRCS= discovery.c iscsid.c login.c
CFLAGS+= -I${.CURDIR}
CFLAGS+= -I${SRCTOP}/sys/cam
CFLAGS+= -I${SRCTOP}/sys/dev/iscsi
CFLAGS+= -I${SRCTOP}/lib/libiscsiutil
CFLAGS+= -DICL_KERNEL_PROXY
MAN= iscsid.8
LIBADD= md util
LIBADD= iscsiutil md util
.include <bsd.prog.mk>

View File

@ -1,423 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2014 The FreeBSD Foundation
*
* This software was developed by Edward Tomasz Napierala under sponsorship
* from the FreeBSD Foundation.
*
* 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 AUTHOR 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 AUTHOR 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <resolv.h>
#include <md5.h>
#include "iscsid.h"
static void
chap_compute_md5(const char id, const char *secret,
const void *challenge, size_t challenge_len, void *response,
size_t response_len)
{
MD5_CTX ctx;
assert(response_len == CHAP_DIGEST_LEN);
MD5Init(&ctx);
MD5Update(&ctx, &id, sizeof(id));
MD5Update(&ctx, secret, strlen(secret));
MD5Update(&ctx, challenge, challenge_len);
MD5Final(response, &ctx);
}
static int
chap_hex2int(const char hex)
{
switch (hex) {
case '0':
return (0x00);
case '1':
return (0x01);
case '2':
return (0x02);
case '3':
return (0x03);
case '4':
return (0x04);
case '5':
return (0x05);
case '6':
return (0x06);
case '7':
return (0x07);
case '8':
return (0x08);
case '9':
return (0x09);
case 'a':
case 'A':
return (0x0a);
case 'b':
case 'B':
return (0x0b);
case 'c':
case 'C':
return (0x0c);
case 'd':
case 'D':
return (0x0d);
case 'e':
case 'E':
return (0x0e);
case 'f':
case 'F':
return (0x0f);
default:
return (-1);
}
}
static int
chap_b642bin(const char *b64, void **binp, size_t *bin_lenp)
{
char *bin;
int b64_len, bin_len;
b64_len = strlen(b64);
bin_len = (b64_len + 3) / 4 * 3;
bin = calloc(bin_len, 1);
if (bin == NULL)
log_err(1, "calloc");
bin_len = b64_pton(b64, bin, bin_len);
if (bin_len < 0) {
log_warnx("malformed base64 variable");
free(bin);
return (-1);
}
*binp = bin;
*bin_lenp = bin_len;
return (0);
}
/*
* XXX: Review this _carefully_.
*/
static int
chap_hex2bin(const char *hex, void **binp, size_t *bin_lenp)
{
int i, hex_len, nibble;
bool lo = true; /* As opposed to 'hi'. */
char *bin;
size_t bin_off, bin_len;
if (strncasecmp(hex, "0b", strlen("0b")) == 0)
return (chap_b642bin(hex + 2, binp, bin_lenp));
if (strncasecmp(hex, "0x", strlen("0x")) != 0) {
log_warnx("malformed variable, should start with \"0x\""
" or \"0b\"");
return (-1);
}
hex += strlen("0x");
hex_len = strlen(hex);
if (hex_len < 1) {
log_warnx("malformed variable; doesn't contain anything "
"but \"0x\"");
return (-1);
}
bin_len = hex_len / 2 + hex_len % 2;
bin = calloc(bin_len, 1);
if (bin == NULL)
log_err(1, "calloc");
bin_off = bin_len - 1;
for (i = hex_len - 1; i >= 0; i--) {
nibble = chap_hex2int(hex[i]);
if (nibble < 0) {
log_warnx("malformed variable, invalid char \"%c\"",
hex[i]);
free(bin);
return (-1);
}
assert(bin_off < bin_len);
if (lo) {
bin[bin_off] = nibble;
lo = false;
} else {
bin[bin_off] |= nibble << 4;
bin_off--;
lo = true;
}
}
*binp = bin;
*bin_lenp = bin_len;
return (0);
}
#ifdef USE_BASE64
static char *
chap_bin2hex(const char *bin, size_t bin_len)
{
unsigned char *b64, *tmp;
size_t b64_len;
b64_len = (bin_len + 2) / 3 * 4 + 3; /* +2 for "0b", +1 for '\0'. */
b64 = malloc(b64_len);
if (b64 == NULL)
log_err(1, "malloc");
tmp = b64;
tmp += sprintf(tmp, "0b");
b64_ntop(bin, bin_len, tmp, b64_len - 2);
return (b64);
}
#else
static char *
chap_bin2hex(const char *bin, size_t bin_len)
{
unsigned char *hex, *tmp, ch;
size_t hex_len;
size_t i;
hex_len = bin_len * 2 + 3; /* +2 for "0x", +1 for '\0'. */
hex = malloc(hex_len);
if (hex == NULL)
log_err(1, "malloc");
tmp = hex;
tmp += sprintf(tmp, "0x");
for (i = 0; i < bin_len; i++) {
ch = bin[i];
tmp += sprintf(tmp, "%02x", ch);
}
return (hex);
}
#endif /* !USE_BASE64 */
struct chap *
chap_new(void)
{
struct chap *chap;
chap = calloc(1, sizeof(*chap));
if (chap == NULL)
log_err(1, "calloc");
/*
* Generate the challenge.
*/
arc4random_buf(chap->chap_challenge, sizeof(chap->chap_challenge));
arc4random_buf(&chap->chap_id, sizeof(chap->chap_id));
return (chap);
}
char *
chap_get_id(const struct chap *chap)
{
char *chap_i;
int ret;
ret = asprintf(&chap_i, "%d", chap->chap_id);
if (ret < 0)
log_err(1, "asprintf");
return (chap_i);
}
char *
chap_get_challenge(const struct chap *chap)
{
char *chap_c;
chap_c = chap_bin2hex(chap->chap_challenge,
sizeof(chap->chap_challenge));
return (chap_c);
}
static int
chap_receive_bin(struct chap *chap, void *response, size_t response_len)
{
if (response_len != sizeof(chap->chap_response)) {
log_debugx("got CHAP response with invalid length; "
"got %zd, should be %zd",
response_len, sizeof(chap->chap_response));
return (1);
}
memcpy(chap->chap_response, response, response_len);
return (0);
}
int
chap_receive(struct chap *chap, const char *response)
{
void *response_bin;
size_t response_bin_len;
int error;
error = chap_hex2bin(response, &response_bin, &response_bin_len);
if (error != 0) {
log_debugx("got incorrectly encoded CHAP response \"%s\"",
response);
return (1);
}
error = chap_receive_bin(chap, response_bin, response_bin_len);
free(response_bin);
return (error);
}
int
chap_authenticate(struct chap *chap, const char *secret)
{
char expected_response[CHAP_DIGEST_LEN];
chap_compute_md5(chap->chap_id, secret,
chap->chap_challenge, sizeof(chap->chap_challenge),
expected_response, sizeof(expected_response));
if (memcmp(chap->chap_response,
expected_response, sizeof(expected_response)) != 0) {
return (-1);
}
return (0);
}
void
chap_delete(struct chap *chap)
{
free(chap);
}
struct rchap *
rchap_new(const char *secret)
{
struct rchap *rchap;
rchap = calloc(1, sizeof(*rchap));
if (rchap == NULL)
log_err(1, "calloc");
rchap->rchap_secret = checked_strdup(secret);
return (rchap);
}
static void
rchap_receive_bin(struct rchap *rchap, const unsigned char id,
const void *challenge, size_t challenge_len)
{
rchap->rchap_id = id;
rchap->rchap_challenge = calloc(challenge_len, 1);
if (rchap->rchap_challenge == NULL)
log_err(1, "calloc");
memcpy(rchap->rchap_challenge, challenge, challenge_len);
rchap->rchap_challenge_len = challenge_len;
}
int
rchap_receive(struct rchap *rchap, const char *id, const char *challenge)
{
unsigned char id_bin;
void *challenge_bin;
size_t challenge_bin_len;
int error;
id_bin = strtoul(id, NULL, 10);
error = chap_hex2bin(challenge, &challenge_bin, &challenge_bin_len);
if (error != 0) {
log_debugx("got incorrectly encoded CHAP challenge \"%s\"",
challenge);
return (1);
}
rchap_receive_bin(rchap, id_bin, challenge_bin, challenge_bin_len);
free(challenge_bin);
return (0);
}
static void
rchap_get_response_bin(struct rchap *rchap,
void **responsep, size_t *response_lenp)
{
void *response_bin;
size_t response_bin_len = CHAP_DIGEST_LEN;
response_bin = calloc(response_bin_len, 1);
if (response_bin == NULL)
log_err(1, "calloc");
chap_compute_md5(rchap->rchap_id, rchap->rchap_secret,
rchap->rchap_challenge, rchap->rchap_challenge_len,
response_bin, response_bin_len);
*responsep = response_bin;
*response_lenp = response_bin_len;
}
char *
rchap_get_response(struct rchap *rchap)
{
void *response;
size_t response_len;
char *chap_r;
rchap_get_response_bin(rchap, &response, &response_len);
chap_r = chap_bin2hex(response, response_len);
free(response);
return (chap_r);
}
void
rchap_delete(struct rchap *rchap)
{
free(rchap->rchap_secret);
free(rchap->rchap_challenge);
free(rchap);
}

View File

@ -138,7 +138,7 @@ logout_new_request(struct connection *conn)
}
static void
kernel_add(const struct connection *conn, const char *target)
kernel_add(const struct iscsid_connection *conn, const char *target)
{
struct iscsi_session_add isa;
int error;
@ -154,7 +154,7 @@ kernel_add(const struct connection *conn, const char *target)
}
static void
kernel_remove(const struct connection *conn)
kernel_remove(const struct iscsid_connection *conn)
{
struct iscsi_session_remove isr;
int error;
@ -167,14 +167,14 @@ kernel_remove(const struct connection *conn)
}
void
discovery(struct connection *conn)
discovery(struct iscsid_connection *conn)
{
struct pdu *request, *response;
struct keys *request_keys, *response_keys;
int i;
log_debugx("beginning discovery session");
request = text_new_request(conn);
request = text_new_request(&conn->conn);
request_keys = keys_new();
keys_add(request_keys, "SendTargets", "All");
keys_save(request_keys, request);
@ -185,7 +185,7 @@ discovery(struct connection *conn)
request = NULL;
log_debugx("waiting for Text Response");
response = text_receive(conn);
response = text_receive(&conn->conn);
response_keys = keys_new();
keys_load(response_keys, response);
for (i = 0; i < KEYS_MAX; i++) {
@ -220,13 +220,13 @@ discovery(struct connection *conn)
#endif
log_debugx("discovery done; logging out");
request = logout_new_request(conn);
request = logout_new_request(&conn->conn);
pdu_send(request);
pdu_delete(request);
request = NULL;
log_debugx("waiting for Logout Response");
response = logout_receive(conn);
response = logout_receive(&conn->conn);
pdu_delete(response);
log_debugx("discovery session done");

View File

@ -57,10 +57,25 @@ __FBSDID("$FreeBSD$");
#include "iscsid.h"
static bool timed_out(void);
#ifdef ICL_KERNEL_PROXY
static void pdu_receive_proxy(struct pdu *pdu);
static void pdu_send_proxy(struct pdu *pdu);
#endif /* ICL_KERNEL_PROXY */
static volatile bool sigalrm_received = false;
static int nchildren = 0;
static struct connection_ops conn_ops = {
.timed_out = timed_out,
#ifdef ICL_KERNEL_PROXY
.pdu_receive_proxy = pdu_receive_proxy,
.pdu_send_proxy = pdu_send_proxy,
#endif
.fail = fail,
};
static void
usage(void)
{
@ -69,17 +84,67 @@ usage(void)
exit(1);
}
char *
checked_strdup(const char *s)
{
char *c;
#ifdef ICL_KERNEL_PROXY
c = strdup(s);
if (c == NULL)
log_err(1, "strdup");
return (c);
static void
pdu_receive_proxy(struct pdu *pdu)
{
struct iscsid_connection *conn;
struct iscsi_daemon_receive idr;
size_t len;
int error;
conn = (struct iscsid_connection *)pdu->pdu_connection;
assert(conn->conn_conf.isc_iser != 0);
pdu->pdu_data = malloc(conn->conn.conn_max_recv_data_segment_length);
if (pdu->pdu_data == NULL)
log_err(1, "malloc");
memset(&idr, 0, sizeof(idr));
idr.idr_session_id = conn->conn_session_id;
idr.idr_bhs = pdu->pdu_bhs;
idr.idr_data_segment_len = conn->conn.conn_max_recv_data_segment_length;
idr.idr_data_segment = pdu->pdu_data;
error = ioctl(conn->conn_iscsi_fd, ISCSIDRECEIVE, &idr);
if (error != 0)
log_err(1, "ISCSIDRECEIVE");
len = pdu_ahs_length(pdu);
if (len > 0)
log_errx(1, "protocol error: non-empty AHS");
len = pdu_data_segment_length(pdu);
assert(len <= (size_t)conn->conn.conn_max_recv_data_segment_length);
pdu->pdu_data_len = len;
}
static void
pdu_send_proxy(struct pdu *pdu)
{
struct iscsid_connection *conn;
struct iscsi_daemon_send ids;
int error;
conn = (struct iscsid_connection *)pdu->pdu_connection;
assert(conn->conn_conf.isc_iser != 0);
pdu_set_data_segment_length(pdu, pdu->pdu_data_len);
memset(&ids, 0, sizeof(ids));
ids.ids_session_id = conn->conn_session_id;
ids.ids_bhs = pdu->pdu_bhs;
ids.ids_data_segment_len = pdu->pdu_data_len;
ids.ids_data_segment = pdu->pdu_data;
error = ioctl(conn->conn_iscsi_fd, ISCSIDSEND, &ids);
if (error != 0)
log_err(1, "ISCSIDSEND");
}
#endif /* ICL_KERNEL_PROXY */
static void
resolve_addr(const struct connection *conn, const char *address,
struct addrinfo **ai, bool initiator_side)
@ -154,10 +219,10 @@ resolve_addr(const struct connection *conn, const char *address,
free(addr);
}
static struct connection *
static struct iscsid_connection *
connection_new(int iscsi_fd, const struct iscsi_daemon_request *request)
{
struct connection *conn;
struct iscsid_connection *conn;
struct iscsi_session_limits *isl;
struct addrinfo *from_ai, *to_ai;
const char *from_addr, *to_addr;
@ -170,24 +235,17 @@ connection_new(int iscsi_fd, const struct iscsi_daemon_request *request)
if (conn == NULL)
log_err(1, "calloc");
/*
* Default values, from RFC 3720, section 12.
*/
connection_init(&conn->conn, &conn_ops,
request->idr_conf.isc_iser != 0);
conn->conn_protocol_level = 0;
conn->conn_header_digest = CONN_DIGEST_NONE;
conn->conn_data_digest = CONN_DIGEST_NONE;
conn->conn_initial_r2t = true;
conn->conn_immediate_data = true;
conn->conn_max_recv_data_segment_length = 8192;
conn->conn_max_send_data_segment_length = 8192;
conn->conn_max_burst_length = 262144;
conn->conn_first_burst_length = 65536;
conn->conn_iscsi_fd = iscsi_fd;
conn->conn_session_id = request->idr_session_id;
memcpy(&conn->conn_conf, &request->idr_conf, sizeof(conn->conn_conf));
memcpy(&conn->conn_isid, &request->idr_isid, sizeof(conn->conn_isid));
conn->conn_tsih = request->idr_tsih;
memcpy(&conn->conn.conn_isid, &request->idr_isid,
sizeof(conn->conn.conn_isid));
conn->conn.conn_tsih = request->idr_tsih;
/*
* Read the driver limits and provide reasonable defaults for the ones
@ -215,9 +273,9 @@ connection_new(int iscsi_fd, const struct iscsi_daemon_request *request)
* We can't do it for other limits, since they may affect both
* sender and receiver operation, and we must obey defaults.
*/
if (conn->conn_max_send_data_segment_length >
if (conn->conn.conn_max_send_data_segment_length >
isl->isl_max_send_data_segment_length) {
conn->conn_max_send_data_segment_length =
conn->conn.conn_max_send_data_segment_length =
isl->isl_max_send_data_segment_length;
}
@ -225,11 +283,11 @@ connection_new(int iscsi_fd, const struct iscsi_daemon_request *request)
to_addr = conn->conn_conf.isc_target_addr;
if (from_addr[0] != '\0')
resolve_addr(conn, from_addr, &from_ai, true);
resolve_addr(&conn->conn, from_addr, &from_ai, true);
else
from_ai = NULL;
resolve_addr(conn, to_addr, &to_ai, false);
resolve_addr(&conn->conn, to_addr, &to_ai, false);
#ifdef ICL_KERNEL_PROXY
if (conn->conn_conf.isc_iser) {
@ -250,7 +308,7 @@ connection_new(int iscsi_fd, const struct iscsi_daemon_request *request)
log_debugx("connecting to %s using ICL kernel proxy", to_addr);
error = ioctl(iscsi_fd, ISCSIDCONNECT, &idc);
if (error != 0) {
fail(conn, strerror(errno));
fail(&conn->conn, strerror(errno));
log_err(1, "failed to connect to %s "
"using ICL kernel proxy: ISCSIDCONNECT", to_addr);
}
@ -264,33 +322,33 @@ connection_new(int iscsi_fd, const struct iscsi_daemon_request *request)
#endif /* ICL_KERNEL_PROXY */
if (conn->conn_conf.isc_iser) {
fail(conn, "iSER not supported");
fail(&conn->conn, "iSER not supported");
log_errx(1, "iscsid(8) compiled without ICL_KERNEL_PROXY "
"does not support iSER");
}
conn->conn_socket = socket(to_ai->ai_family, to_ai->ai_socktype,
conn->conn.conn_socket = socket(to_ai->ai_family, to_ai->ai_socktype,
to_ai->ai_protocol);
if (conn->conn_socket < 0) {
fail(conn, strerror(errno));
if (conn->conn.conn_socket < 0) {
fail(&conn->conn, strerror(errno));
log_err(1, "failed to create socket for %s", from_addr);
}
optval = SOCKBUF_SIZE;
if (setsockopt(conn->conn_socket, SOL_SOCKET, SO_RCVBUF,
if (setsockopt(conn->conn.conn_socket, SOL_SOCKET, SO_RCVBUF,
&optval, sizeof(optval)) == -1)
log_warn("setsockopt(SO_RCVBUF) failed");
optval = SOCKBUF_SIZE;
if (setsockopt(conn->conn_socket, SOL_SOCKET, SO_SNDBUF,
if (setsockopt(conn->conn.conn_socket, SOL_SOCKET, SO_SNDBUF,
&optval, sizeof(optval)) == -1)
log_warn("setsockopt(SO_SNDBUF) failed");
optval = 1;
if (setsockopt(conn->conn_socket, SOL_SOCKET, SO_NO_DDP,
if (setsockopt(conn->conn.conn_socket, SOL_SOCKET, SO_NO_DDP,
&optval, sizeof(optval)) == -1)
log_warn("setsockopt(SO_NO_DDP) failed");
if (conn->conn_conf.isc_dscp != -1) {
int tos = conn->conn_conf.isc_dscp << 2;
if (to_ai->ai_family == AF_INET) {
if (setsockopt(conn->conn_socket,
if (setsockopt(conn->conn.conn_socket,
IPPROTO_IP, IP_TOS,
&tos, sizeof(tos)) == -1)
log_warn("setsockopt(IP_TOS) "
@ -298,7 +356,7 @@ connection_new(int iscsi_fd, const struct iscsi_daemon_request *request)
from_addr);
} else
if (to_ai->ai_family == AF_INET6) {
if (setsockopt(conn->conn_socket,
if (setsockopt(conn->conn.conn_socket,
IPPROTO_IPV6, IPV6_TCLASS,
&tos, sizeof(tos)) == -1)
log_warn("setsockopt(IPV6_TCLASS) "
@ -309,7 +367,7 @@ connection_new(int iscsi_fd, const struct iscsi_daemon_request *request)
if (conn->conn_conf.isc_pcp != -1) {
int pcp = conn->conn_conf.isc_pcp;
if (to_ai->ai_family == AF_INET) {
if (setsockopt(conn->conn_socket,
if (setsockopt(conn->conn.conn_socket,
IPPROTO_IP, IP_VLAN_PCP,
&pcp, sizeof(pcp)) == -1)
log_warn("setsockopt(IP_VLAN_PCP) "
@ -317,7 +375,7 @@ connection_new(int iscsi_fd, const struct iscsi_daemon_request *request)
from_addr);
} else
if (to_ai->ai_family == AF_INET6) {
if (setsockopt(conn->conn_socket,
if (setsockopt(conn->conn.conn_socket,
IPPROTO_IPV6, IPV6_VLAN_PCP,
&pcp, sizeof(pcp)) == -1)
log_warn("setsockopt(IPV6_VLAN_PCP) "
@ -326,17 +384,18 @@ connection_new(int iscsi_fd, const struct iscsi_daemon_request *request)
}
}
if (from_ai != NULL) {
error = bind(conn->conn_socket, from_ai->ai_addr,
error = bind(conn->conn.conn_socket, from_ai->ai_addr,
from_ai->ai_addrlen);
if (error != 0) {
fail(conn, strerror(errno));
fail(&conn->conn, strerror(errno));
log_err(1, "failed to bind to %s", from_addr);
}
}
log_debugx("connecting to %s", to_addr);
error = connect(conn->conn_socket, to_ai->ai_addr, to_ai->ai_addrlen);
error = connect(conn->conn.conn_socket, to_ai->ai_addr,
to_ai->ai_addrlen);
if (error != 0) {
fail(conn, strerror(errno));
fail(&conn->conn, strerror(errno));
log_err(1, "failed to connect to %s", to_addr);
}
@ -348,7 +407,7 @@ connection_new(int iscsi_fd, const struct iscsi_daemon_request *request)
}
static void
handoff(struct connection *conn)
handoff(struct iscsid_connection *conn)
{
struct iscsi_daemon_handoff idh;
int error;
@ -357,22 +416,22 @@ handoff(struct connection *conn)
memset(&idh, 0, sizeof(idh));
idh.idh_session_id = conn->conn_session_id;
idh.idh_socket = conn->conn_socket;
idh.idh_socket = conn->conn.conn_socket;
strlcpy(idh.idh_target_alias, conn->conn_target_alias,
sizeof(idh.idh_target_alias));
idh.idh_tsih = conn->conn_tsih;
idh.idh_statsn = conn->conn_statsn;
idh.idh_tsih = conn->conn.conn_tsih;
idh.idh_statsn = conn->conn.conn_statsn;
idh.idh_protocol_level = conn->conn_protocol_level;
idh.idh_header_digest = conn->conn_header_digest;
idh.idh_data_digest = conn->conn_data_digest;
idh.idh_header_digest = conn->conn.conn_header_digest;
idh.idh_data_digest = conn->conn.conn_data_digest;
idh.idh_initial_r2t = conn->conn_initial_r2t;
idh.idh_immediate_data = conn->conn_immediate_data;
idh.idh_immediate_data = conn->conn.conn_immediate_data;
idh.idh_max_recv_data_segment_length =
conn->conn_max_recv_data_segment_length;
conn->conn.conn_max_recv_data_segment_length;
idh.idh_max_send_data_segment_length =
conn->conn_max_send_data_segment_length;
idh.idh_max_burst_length = conn->conn_max_burst_length;
idh.idh_first_burst_length = conn->conn_first_burst_length;
conn->conn.conn_max_send_data_segment_length;
idh.idh_max_burst_length = conn->conn.conn_max_burst_length;
idh.idh_first_burst_length = conn->conn.conn_first_burst_length;
error = ioctl(conn->conn_iscsi_fd, ISCSIDHANDOFF, &idh);
if (error != 0)
@ -380,11 +439,13 @@ handoff(struct connection *conn)
}
void
fail(const struct connection *conn, const char *reason)
fail(const struct connection *base_conn, const char *reason)
{
const struct iscsid_connection *conn;
struct iscsi_daemon_fail idf;
int error, saved_errno;
conn = (const struct iscsid_connection *)base_conn;
saved_errno = errno;
memset(&idf, 0, sizeof(idf));
@ -402,7 +463,7 @@ fail(const struct connection *conn, const char *reason)
* XXX: I CANT INTO LATIN
*/
static void
capsicate(struct connection *conn)
capsicate(struct iscsid_connection *conn)
{
cap_rights_t rights;
#ifdef ICL_KERNEL_PROXY
@ -429,7 +490,7 @@ capsicate(struct connection *conn)
log_warnx("Capsicum capability mode not supported");
}
bool
static bool
timed_out(void)
{
@ -519,7 +580,7 @@ register_sigchld(void)
static void
handle_request(int iscsi_fd, const struct iscsi_daemon_request *request, int timeout)
{
struct connection *conn;
struct iscsid_connection *conn;
log_set_peer_addr(request->idr_conf.isc_target_addr);
if (request->idr_conf.isc_target[0] != '\0') {

View File

@ -37,116 +37,29 @@
#include <stdint.h>
#include <iscsi_ioctl.h>
#include <libiscsiutil.h>
#define DEFAULT_PIDFILE "/var/run/iscsid.pid"
#define CONN_DIGEST_NONE 0
#define CONN_DIGEST_CRC32C 1
#define CONN_MUTUAL_CHALLENGE_LEN 1024
#define SOCKBUF_SIZE 1048576
struct connection {
struct iscsid_connection {
struct connection conn;
int conn_iscsi_fd;
int conn_socket;
unsigned int conn_session_id;
struct iscsi_session_conf conn_conf;
struct iscsi_session_limits conn_limits;
char conn_target_alias[ISCSI_ADDR_LEN];
uint8_t conn_isid[6];
uint16_t conn_tsih;
uint32_t conn_statsn;
int conn_protocol_level;
int conn_header_digest;
int conn_data_digest;
bool conn_initial_r2t;
bool conn_immediate_data;
int conn_max_recv_data_segment_length;
int conn_max_send_data_segment_length;
int conn_max_burst_length;
int conn_first_burst_length;
struct chap *conn_mutual_chap;
};
struct pdu {
struct connection *pdu_connection;
struct iscsi_bhs *pdu_bhs;
char *pdu_data;
size_t pdu_data_len;
};
void login(struct iscsid_connection *ic);
#define KEYS_MAX 1024
void discovery(struct iscsid_connection *ic);
struct keys {
char *keys_names[KEYS_MAX];
char *keys_values[KEYS_MAX];
char *keys_data;
size_t keys_data_len;
};
#define CHAP_CHALLENGE_LEN 1024
#define CHAP_DIGEST_LEN 16 /* Equal to MD5 digest size. */
struct chap {
unsigned char chap_id;
char chap_challenge[CHAP_CHALLENGE_LEN];
char chap_response[CHAP_DIGEST_LEN];
};
struct rchap {
char *rchap_secret;
unsigned char rchap_id;
void *rchap_challenge;
size_t rchap_challenge_len;
};
struct chap *chap_new(void);
char *chap_get_id(const struct chap *chap);
char *chap_get_challenge(const struct chap *chap);
int chap_receive(struct chap *chap, const char *response);
int chap_authenticate(struct chap *chap,
const char *secret);
void chap_delete(struct chap *chap);
struct rchap *rchap_new(const char *secret);
int rchap_receive(struct rchap *rchap,
const char *id, const char *challenge);
char *rchap_get_response(struct rchap *rchap);
void rchap_delete(struct rchap *rchap);
struct keys *keys_new(void);
void keys_delete(struct keys *key);
void keys_load(struct keys *keys, const struct pdu *pdu);
void keys_save(struct keys *keys, struct pdu *pdu);
const char *keys_find(struct keys *keys, const char *name);
void keys_add(struct keys *keys,
const char *name, const char *value);
void keys_add_int(struct keys *keys,
const char *name, int value);
struct pdu *pdu_new(struct connection *ic);
struct pdu *pdu_new_response(struct pdu *request);
void pdu_receive(struct pdu *request);
void pdu_send(struct pdu *response);
void pdu_delete(struct pdu *ip);
void login(struct connection *ic);
void discovery(struct connection *ic);
void log_init(int level);
void log_set_peer_name(const char *name);
void log_set_peer_addr(const char *addr);
void log_err(int, const char *, ...)
__dead2 __printflike(2, 3);
void log_errx(int, const char *, ...)
__dead2 __printflike(2, 3);
void log_warn(const char *, ...) __printflike(1, 2);
void log_warnx(const char *, ...) __printflike(1, 2);
void log_debugx(const char *, ...) __printflike(1, 2);
char *checked_strdup(const char *);
bool timed_out(void);
void fail(const struct connection *, const char *);
#endif /* !ISCSID_H */

View File

@ -1,199 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2012 The FreeBSD Foundation
*
* This software was developed by Edward Tomasz Napierala under sponsorship
* from the FreeBSD Foundation.
*
* 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 AUTHOR 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 AUTHOR 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "iscsid.h"
struct keys *
keys_new(void)
{
struct keys *keys;
keys = calloc(1, sizeof(*keys));
if (keys == NULL)
log_err(1, "calloc");
return (keys);
}
void
keys_delete(struct keys *keys)
{
free(keys->keys_data);
free(keys);
}
void
keys_load(struct keys *keys, const struct pdu *pdu)
{
int i;
char *pair;
size_t pair_len;
if (pdu->pdu_data_len == 0)
return;
if (pdu->pdu_data[pdu->pdu_data_len - 1] != '\0')
log_errx(1, "protocol error: key not NULL-terminated\n");
assert(keys->keys_data == NULL);
keys->keys_data_len = pdu->pdu_data_len;
keys->keys_data = malloc(keys->keys_data_len);
if (keys->keys_data == NULL)
log_err(1, "malloc");
memcpy(keys->keys_data, pdu->pdu_data, keys->keys_data_len);
/*
* XXX: Review this carefully.
*/
pair = keys->keys_data;
for (i = 0;; i++) {
if (i >= KEYS_MAX)
log_errx(1, "too many keys received");
pair_len = strlen(pair);
keys->keys_values[i] = pair;
keys->keys_names[i] = strsep(&keys->keys_values[i], "=");
if (keys->keys_names[i] == NULL || keys->keys_values[i] == NULL)
log_errx(1, "malformed keys");
log_debugx("key received: \"%s=%s\"",
keys->keys_names[i], keys->keys_values[i]);
pair += pair_len + 1; /* +1 to skip the terminating '\0'. */
if (pair == keys->keys_data + keys->keys_data_len)
break;
assert(pair < keys->keys_data + keys->keys_data_len);
}
}
void
keys_save(struct keys *keys, struct pdu *pdu)
{
char *data;
size_t len;
int i;
/*
* XXX: Not particularly efficient.
*/
len = 0;
for (i = 0; i < KEYS_MAX; i++) {
if (keys->keys_names[i] == NULL)
break;
/*
* +1 for '=', +1 for '\0'.
*/
len += strlen(keys->keys_names[i]) +
strlen(keys->keys_values[i]) + 2;
}
if (len == 0)
return;
data = malloc(len);
if (data == NULL)
log_err(1, "malloc");
pdu->pdu_data = data;
pdu->pdu_data_len = len;
for (i = 0; i < KEYS_MAX; i++) {
if (keys->keys_names[i] == NULL)
break;
data += sprintf(data, "%s=%s",
keys->keys_names[i], keys->keys_values[i]);
data += 1; /* for '\0'. */
}
}
const char *
keys_find(struct keys *keys, const char *name)
{
int i;
/*
* Note that we don't handle duplicated key names here,
* as they are not supposed to happen in requests, and if they do,
* it's an initiator error.
*/
for (i = 0; i < KEYS_MAX; i++) {
if (keys->keys_names[i] == NULL)
return (NULL);
if (strcmp(keys->keys_names[i], name) == 0)
return (keys->keys_values[i]);
}
return (NULL);
}
void
keys_add(struct keys *keys, const char *name, const char *value)
{
int i;
log_debugx("key to send: \"%s=%s\"", name, value);
/*
* Note that we don't check for duplicates here, as they are perfectly
* fine in responses, e.g. the "TargetName" keys in discovery sesion
* response.
*/
for (i = 0; i < KEYS_MAX; i++) {
if (keys->keys_names[i] == NULL) {
keys->keys_names[i] = checked_strdup(name);
keys->keys_values[i] = checked_strdup(value);
return;
}
}
log_errx(1, "too many keys");
}
void
keys_add_int(struct keys *keys, const char *name, int value)
{
char *str;
int ret;
ret = asprintf(&str, "%d", value);
if (ret <= 0)
log_err(1, "asprintf");
keys_add(keys, name, str);
free(str);
}

View File

@ -1,202 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2012 The FreeBSD Foundation
*
* This software was developed by Edward Tomasz Napierala under sponsorship
* from the FreeBSD Foundation.
*
* 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 AUTHOR 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 AUTHOR 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <vis.h>
#include "iscsid.h"
static int log_level = 0;
static char *peer_name = NULL;
static char *peer_addr = NULL;
#define MSGBUF_LEN 1024
void
log_init(int level)
{
log_level = level;
openlog(getprogname(), LOG_NDELAY | LOG_PID, LOG_DAEMON);
}
void
log_set_peer_name(const char *name)
{
/*
* XXX: Turn it into assertion?
*/
if (peer_name != NULL)
log_errx(1, "%s called twice", __func__);
if (peer_addr == NULL)
log_errx(1, "%s called before log_set_peer_addr", __func__);
peer_name = checked_strdup(name);
}
void
log_set_peer_addr(const char *addr)
{
/*
* XXX: Turn it into assertion?
*/
if (peer_addr != NULL)
log_errx(1, "%s called twice", __func__);
peer_addr = checked_strdup(addr);
}
static void
log_common(int priority, int log_errno, const char *fmt, va_list ap)
{
static char msgbuf[MSGBUF_LEN];
static char msgbuf_strvised[MSGBUF_LEN * 4 + 1];
char *errstr;
int ret;
ret = vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
if (ret < 0) {
fprintf(stderr, "%s: snprintf failed", getprogname());
syslog(LOG_CRIT, "snprintf failed");
exit(1);
}
ret = strnvis(msgbuf_strvised, sizeof(msgbuf_strvised), msgbuf, VIS_NL);
if (ret < 0) {
fprintf(stderr, "%s: strnvis failed", getprogname());
syslog(LOG_CRIT, "strnvis failed");
exit(1);
}
if (log_errno == -1) {
if (peer_name != NULL) {
fprintf(stderr, "%s: %s (%s): %s\n", getprogname(),
peer_addr, peer_name, msgbuf_strvised);
syslog(priority, "%s (%s): %s",
peer_addr, peer_name, msgbuf_strvised);
} else if (peer_addr != NULL) {
fprintf(stderr, "%s: %s: %s\n", getprogname(),
peer_addr, msgbuf_strvised);
syslog(priority, "%s: %s",
peer_addr, msgbuf_strvised);
} else {
fprintf(stderr, "%s: %s\n", getprogname(), msgbuf_strvised);
syslog(priority, "%s", msgbuf_strvised);
}
} else {
errstr = strerror(log_errno);
if (peer_name != NULL) {
fprintf(stderr, "%s: %s (%s): %s: %s\n", getprogname(),
peer_addr, peer_name, msgbuf_strvised, errstr);
syslog(priority, "%s (%s): %s: %s",
peer_addr, peer_name, msgbuf_strvised, errstr);
} else if (peer_addr != NULL) {
fprintf(stderr, "%s: %s: %s: %s\n", getprogname(),
peer_addr, msgbuf_strvised, errstr);
syslog(priority, "%s: %s: %s",
peer_addr, msgbuf_strvised, errstr);
} else {
fprintf(stderr, "%s: %s: %s\n", getprogname(),
msgbuf_strvised, errstr);
syslog(priority, "%s: %s",
msgbuf_strvised, errstr);
}
}
}
void
log_err(int eval, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
log_common(LOG_CRIT, errno, fmt, ap);
va_end(ap);
exit(eval);
}
void
log_errx(int eval, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
log_common(LOG_CRIT, -1, fmt, ap);
va_end(ap);
exit(eval);
}
void
log_warn(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
log_common(LOG_WARNING, errno, fmt, ap);
va_end(ap);
}
void
log_warnx(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
log_common(LOG_WARNING, -1, fmt, ap);
va_end(ap);
}
void
log_debugx(const char *fmt, ...)
{
va_list ap;
if (log_level == 0)
return;
va_start(ap, fmt);
log_common(LOG_DEBUG, -1, fmt, ap);
va_end(ap);
}

View File

@ -160,7 +160,7 @@ login_target_error_str(int class, int detail)
}
static void
kernel_modify(const struct connection *conn, const char *target_address)
kernel_modify(const struct iscsid_connection *conn, const char *target_address)
{
struct iscsi_session_modify ism;
int error;
@ -188,7 +188,7 @@ kernel_modify(const struct connection *conn, const char *target_address)
* as described in draft.
*/
static void
login_handle_redirection(struct connection *conn, struct pdu *response)
login_handle_redirection(struct iscsid_connection *conn, struct pdu *response)
{
struct iscsi_bhs_login_response *bhslr;
struct keys *response_keys;
@ -241,7 +241,8 @@ login_receive(struct connection *conn)
log_errx(1, "received Login PDU with unsupported "
"Version-active 0x%x", bhslr->bhslr_version_active);
if (bhslr->bhslr_status_class == 1) {
login_handle_redirection(conn, response);
login_handle_redirection((struct iscsid_connection *)conn,
response);
log_debugx("redirection handled; exiting");
exit(0);
}
@ -328,7 +329,7 @@ login_list_prefers(const char *list,
}
static void
login_negotiate_key(struct connection *conn, const char *name,
login_negotiate_key(struct iscsid_connection *conn, const char *name,
const char *value)
{
struct iscsi_session_limits *isl;
@ -351,7 +352,7 @@ login_negotiate_key(struct connection *conn, const char *name,
case 1:
log_debugx("target prefers CRC32C "
"for header digest; we'll use it");
conn->conn_header_digest = CONN_DIGEST_CRC32C;
conn->conn.conn_header_digest = CONN_DIGEST_CRC32C;
break;
case 2:
log_debugx("target prefers not to do "
@ -368,7 +369,7 @@ login_negotiate_key(struct connection *conn, const char *name,
case 1:
log_debugx("target prefers CRC32C "
"for data digest; we'll use it");
conn->conn_data_digest = CONN_DIGEST_CRC32C;
conn->conn.conn_data_digest = CONN_DIGEST_CRC32C;
break;
case 2:
log_debugx("target prefers not to do "
@ -388,9 +389,9 @@ login_negotiate_key(struct connection *conn, const char *name,
conn->conn_initial_r2t = false;
} else if (strcmp(name, "ImmediateData") == 0) {
if (strcmp(value, "Yes") == 0)
conn->conn_immediate_data = true;
conn->conn.conn_immediate_data = true;
else
conn->conn_immediate_data = false;
conn->conn.conn_immediate_data = false;
} else if (strcmp(name, "MaxRecvDataSegmentLength") == 0) {
tmp = strtoul(value, NULL, 10);
if (tmp <= 0)
@ -402,7 +403,7 @@ login_negotiate_key(struct connection *conn, const char *name,
isl->isl_max_send_data_segment_length);
tmp = isl->isl_max_send_data_segment_length;
}
conn->conn_max_send_data_segment_length = tmp;
conn->conn.conn_max_send_data_segment_length = tmp;
} else if (strcmp(name, "MaxBurstLength") == 0) {
tmp = strtoul(value, NULL, 10);
if (tmp <= 0)
@ -412,7 +413,7 @@ login_negotiate_key(struct connection *conn, const char *name,
"from %d to %d", tmp, isl->isl_max_burst_length);
tmp = isl->isl_max_burst_length;
}
conn->conn_max_burst_length = tmp;
conn->conn.conn_max_burst_length = tmp;
} else if (strcmp(name, "FirstBurstLength") == 0) {
tmp = strtoul(value, NULL, 10);
if (tmp <= 0)
@ -422,7 +423,7 @@ login_negotiate_key(struct connection *conn, const char *name,
"from %d to %d", tmp, isl->isl_first_burst_length);
tmp = isl->isl_first_burst_length;
}
conn->conn_first_burst_length = tmp;
conn->conn.conn_first_burst_length = tmp;
} else if (strcmp(name, "DefaultTime2Wait") == 0) {
/* Ignore */
} else if (strcmp(name, "DefaultTime2Retain") == 0) {
@ -455,7 +456,7 @@ login_negotiate_key(struct connection *conn, const char *name,
isl->isl_max_recv_data_segment_length);
tmp = isl->isl_max_recv_data_segment_length;
}
conn->conn_max_recv_data_segment_length = tmp;
conn->conn.conn_max_recv_data_segment_length = tmp;
} else if (strcmp(name, "TargetPortalGroupTag") == 0) {
/* Ignore */
} else if (strcmp(name, "TargetRecvDataSegmentLength") == 0) {
@ -470,14 +471,14 @@ login_negotiate_key(struct connection *conn, const char *name,
isl->isl_max_send_data_segment_length);
tmp = isl->isl_max_send_data_segment_length;
}
conn->conn_max_send_data_segment_length = tmp;
conn->conn.conn_max_send_data_segment_length = tmp;
} else {
log_debugx("unknown key \"%s\"; ignoring", name);
}
}
static void
login_negotiate(struct connection *conn)
login_negotiate(struct iscsid_connection *conn)
{
struct pdu *request, *response;
struct keys *request_keys, *response_keys;
@ -486,7 +487,8 @@ login_negotiate(struct connection *conn)
struct iscsi_session_limits *isl;
log_debugx("beginning operational parameter negotiation");
request = login_new_request(conn, BHSLR_STAGE_OPERATIONAL_NEGOTIATION);
request = login_new_request(&conn->conn,
BHSLR_STAGE_OPERATIONAL_NEGOTIATION);
request_keys = keys_new();
isl = &conn->conn_limits;
@ -535,7 +537,7 @@ login_negotiate(struct connection *conn)
isl->isl_max_recv_data_segment_length);
}
conn->conn_max_recv_data_segment_length =
conn->conn.conn_max_recv_data_segment_length =
isl->isl_max_recv_data_segment_length;
keys_add(request_keys, "DefaultTime2Wait", "0");
@ -548,7 +550,7 @@ login_negotiate(struct connection *conn)
pdu_delete(request);
request = NULL;
response = login_receive(conn);
response = login_receive(&conn->conn);
response_keys = keys_new();
keys_load(response_keys, response);
for (i = 0; i < KEYS_MAX; i++) {
@ -579,12 +581,12 @@ login_negotiate(struct connection *conn)
pdu_delete(response);
request = login_new_request(conn,
request = login_new_request(&conn->conn,
BHSLR_STAGE_OPERATIONAL_NEGOTIATION);
pdu_send(request);
pdu_delete(request);
response = login_receive(conn);
response = login_receive(&conn->conn);
}
if (login_nsg(response) != BHSLR_STAGE_FULL_FEATURE_PHASE)
@ -614,7 +616,7 @@ login_send_chap_a(struct connection *conn)
static void
login_send_chap_r(struct pdu *response)
{
struct connection *conn;
struct iscsid_connection *conn;
struct pdu *request;
struct keys *request_keys, *response_keys;
struct rchap *rchap;
@ -631,7 +633,7 @@ login_send_chap_r(struct pdu *response)
* CHAP challenge; our CHAP response goes into 'request'.
*/
conn = response->pdu_connection;
conn = (struct iscsid_connection *)response->pdu_connection;
response_keys = keys_new();
keys_load(response_keys, response);
@ -665,7 +667,8 @@ login_send_chap_r(struct pdu *response)
keys_delete(response_keys);
request = login_new_request(conn, BHSLR_STAGE_SECURITY_NEGOTIATION);
request = login_new_request(&conn->conn,
BHSLR_STAGE_SECURITY_NEGOTIATION);
request_keys = keys_new();
keys_add(request_keys, "CHAP_N", conn->conn_conf.isc_user);
keys_add(request_keys, "CHAP_R", chap_r);
@ -699,12 +702,12 @@ login_send_chap_r(struct pdu *response)
static void
login_verify_mutual(const struct pdu *response)
{
struct connection *conn;
struct iscsid_connection *conn;
struct keys *response_keys;
const char *chap_n, *chap_r;
int error;
conn = response->pdu_connection;
conn = (struct iscsid_connection *)response->pdu_connection;
response_keys = keys_new();
keys_load(response_keys, response);
@ -721,14 +724,14 @@ login_verify_mutual(const struct pdu *response)
log_errx(1, "received CHAP Response PDU with invalid CHAP_R");
if (strcmp(chap_n, conn->conn_conf.isc_mutual_user) != 0) {
fail(conn, "Mutual CHAP failed");
fail(&conn->conn, "Mutual CHAP failed");
log_errx(1, "mutual CHAP authentication failed: wrong user");
}
error = chap_authenticate(conn->conn_mutual_chap,
conn->conn_conf.isc_mutual_secret);
if (error != 0) {
fail(conn, "Mutual CHAP failed");
fail(&conn->conn, "Mutual CHAP failed");
log_errx(1, "mutual CHAP authentication failed: wrong secret");
}
@ -740,15 +743,15 @@ login_verify_mutual(const struct pdu *response)
}
static void
login_chap(struct connection *conn)
login_chap(struct iscsid_connection *conn)
{
struct pdu *response;
log_debugx("beginning CHAP authentication; sending CHAP_A");
login_send_chap_a(conn);
login_send_chap_a(&conn->conn);
log_debugx("waiting for CHAP_A/CHAP_C/CHAP_I");
response = login_receive(conn);
response = login_receive(&conn->conn);
log_debugx("sending CHAP_N/CHAP_R");
login_send_chap_r(response);
@ -759,7 +762,7 @@ login_chap(struct connection *conn)
*/
log_debugx("waiting for CHAP result");
response = login_receive(conn);
response = login_receive(&conn->conn);
if (conn->conn_conf.isc_mutual_user[0] != '\0')
login_verify_mutual(response);
pdu_delete(response);
@ -768,7 +771,7 @@ login_chap(struct connection *conn)
}
void
login(struct connection *conn)
login(struct iscsid_connection *conn)
{
struct pdu *request, *response;
struct keys *request_keys, *response_keys;
@ -777,7 +780,8 @@ login(struct connection *conn)
int i;
log_debugx("beginning Login phase; sending Login PDU");
request = login_new_request(conn, BHSLR_STAGE_SECURITY_NEGOTIATION);
request = login_new_request(&conn->conn,
BHSLR_STAGE_SECURITY_NEGOTIATION);
request_keys = keys_new();
if (conn->conn_conf.isc_mutual_user[0] != '\0') {
keys_add(request_keys, "AuthMethod", "CHAP");
@ -817,7 +821,7 @@ login(struct connection *conn)
pdu_send(request);
pdu_delete(request);
response = login_receive(conn);
response = login_receive(&conn->conn);
response_keys = keys_new();
keys_load(response_keys, response);
@ -875,14 +879,14 @@ login(struct connection *conn)
}
if (strcmp(auth_method, "CHAP") != 0) {
fail(conn, "Unsupported AuthMethod");
fail(&conn->conn, "Unsupported AuthMethod");
log_errx(1, "received response "
"with unsupported AuthMethod \"%s\"", auth_method);
}
if (conn->conn_conf.isc_user[0] == '\0' ||
conn->conn_conf.isc_secret[0] == '\0') {
fail(conn, "Authentication required");
fail(&conn->conn, "Authentication required");
log_errx(1, "target requests CHAP authentication, but we don't "
"have user and secret");
}

View File

@ -1,309 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2012 The FreeBSD Foundation
*
* This software was developed by Edward Tomasz Napierala under sponsorship
* from the FreeBSD Foundation.
*
* 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 AUTHOR 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 AUTHOR 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/uio.h>
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "iscsid.h"
#include "iscsi_proto.h"
#ifdef ICL_KERNEL_PROXY
#include <sys/ioctl.h>
#endif
static int
pdu_ahs_length(const struct pdu *pdu)
{
return (pdu->pdu_bhs->bhs_total_ahs_len * 4);
}
static int
pdu_data_segment_length(const struct pdu *pdu)
{
uint32_t len = 0;
len += pdu->pdu_bhs->bhs_data_segment_len[0];
len <<= 8;
len += pdu->pdu_bhs->bhs_data_segment_len[1];
len <<= 8;
len += pdu->pdu_bhs->bhs_data_segment_len[2];
return (len);
}
static void
pdu_set_data_segment_length(struct pdu *pdu, uint32_t len)
{
pdu->pdu_bhs->bhs_data_segment_len[2] = len;
pdu->pdu_bhs->bhs_data_segment_len[1] = len >> 8;
pdu->pdu_bhs->bhs_data_segment_len[0] = len >> 16;
}
struct pdu *
pdu_new(struct connection *conn)
{
struct pdu *pdu;
pdu = calloc(1, sizeof(*pdu));
if (pdu == NULL)
log_err(1, "calloc");
pdu->pdu_bhs = calloc(1, sizeof(*pdu->pdu_bhs));
if (pdu->pdu_bhs == NULL)
log_err(1, "calloc");
pdu->pdu_connection = conn;
return (pdu);
}
struct pdu *
pdu_new_response(struct pdu *request)
{
return (pdu_new(request->pdu_connection));
}
#ifdef ICL_KERNEL_PROXY
static void
pdu_receive_proxy(struct pdu *pdu)
{
struct connection *conn;
struct iscsi_daemon_receive *idr;
size_t len;
int error;
conn = pdu->pdu_connection;
assert(conn->conn_conf.isc_iser != 0);
pdu->pdu_data = malloc(conn->conn_max_recv_data_segment_length);
if (pdu->pdu_data == NULL)
log_err(1, "malloc");
idr = calloc(1, sizeof(*idr));
if (idr == NULL)
log_err(1, "calloc");
idr->idr_session_id = conn->conn_session_id;
idr->idr_bhs = pdu->pdu_bhs;
idr->idr_data_segment_len = conn->conn_max_recv_data_segment_length;
idr->idr_data_segment = pdu->pdu_data;
error = ioctl(conn->conn_iscsi_fd, ISCSIDRECEIVE, idr);
if (error != 0)
log_err(1, "ISCSIDRECEIVE");
len = pdu_ahs_length(pdu);
if (len > 0)
log_errx(1, "protocol error: non-empty AHS");
len = pdu_data_segment_length(pdu);
assert(len <= (size_t)conn->conn_max_recv_data_segment_length);
pdu->pdu_data_len = len;
free(idr);
}
static void
pdu_send_proxy(struct pdu *pdu)
{
struct connection *conn;
struct iscsi_daemon_send *ids;
int error;
conn = pdu->pdu_connection;
assert(conn->conn_conf.isc_iser != 0);
pdu_set_data_segment_length(pdu, pdu->pdu_data_len);
ids = calloc(1, sizeof(*ids));
if (ids == NULL)
log_err(1, "calloc");
ids->ids_session_id = conn->conn_session_id;
ids->ids_bhs = pdu->pdu_bhs;
ids->ids_data_segment_len = pdu->pdu_data_len;
ids->ids_data_segment = pdu->pdu_data;
error = ioctl(conn->conn_iscsi_fd, ISCSIDSEND, ids);
if (error != 0)
log_err(1, "ISCSIDSEND");
free(ids);
}
#endif /* ICL_KERNEL_PROXY */
static size_t
pdu_padding(const struct pdu *pdu)
{
if ((pdu->pdu_data_len % 4) != 0)
return (4 - (pdu->pdu_data_len % 4));
return (0);
}
static void
pdu_read(const struct connection *conn, char *data, size_t len)
{
ssize_t ret;
while (len > 0) {
ret = read(conn->conn_socket, data, len);
if (ret < 0) {
if (timed_out()) {
fail(conn, "Login Phase timeout");
log_errx(1, "exiting due to timeout");
}
fail(conn, strerror(errno));
log_err(1, "read");
} else if (ret == 0) {
fail(conn, "connection lost");
log_errx(1, "read: connection lost");
}
len -= ret;
data += ret;
}
}
void
pdu_receive(struct pdu *pdu)
{
struct connection *conn;
size_t len, padding;
char dummy[4];
conn = pdu->pdu_connection;
#ifdef ICL_KERNEL_PROXY
if (conn->conn_conf.isc_iser != 0)
return (pdu_receive_proxy(pdu));
#endif
assert(conn->conn_conf.isc_iser == 0);
pdu_read(conn, (char *)pdu->pdu_bhs, sizeof(*pdu->pdu_bhs));
len = pdu_ahs_length(pdu);
if (len > 0)
log_errx(1, "protocol error: non-empty AHS");
len = pdu_data_segment_length(pdu);
if (len > 0) {
if (len > (size_t)conn->conn_max_recv_data_segment_length) {
log_errx(1, "protocol error: received PDU "
"with DataSegmentLength exceeding %d",
conn->conn_max_recv_data_segment_length);
}
pdu->pdu_data_len = len;
pdu->pdu_data = malloc(len);
if (pdu->pdu_data == NULL)
log_err(1, "malloc");
pdu_read(conn, (char *)pdu->pdu_data, pdu->pdu_data_len);
padding = pdu_padding(pdu);
if (padding != 0) {
assert(padding < sizeof(dummy));
pdu_read(conn, (char *)dummy, padding);
}
}
}
void
pdu_send(struct pdu *pdu)
{
struct connection *conn;
ssize_t ret, total_len;
size_t padding;
uint32_t zero = 0;
struct iovec iov[3];
int iovcnt;
conn = pdu->pdu_connection;
#ifdef ICL_KERNEL_PROXY
if (conn->conn_conf.isc_iser != 0)
return (pdu_send_proxy(pdu));
#endif
assert(conn->conn_conf.isc_iser == 0);
pdu_set_data_segment_length(pdu, pdu->pdu_data_len);
iov[0].iov_base = pdu->pdu_bhs;
iov[0].iov_len = sizeof(*pdu->pdu_bhs);
total_len = iov[0].iov_len;
iovcnt = 1;
if (pdu->pdu_data_len > 0) {
iov[1].iov_base = pdu->pdu_data;
iov[1].iov_len = pdu->pdu_data_len;
total_len += iov[1].iov_len;
iovcnt = 2;
padding = pdu_padding(pdu);
if (padding > 0) {
assert(padding < sizeof(zero));
iov[2].iov_base = &zero;
iov[2].iov_len = padding;
total_len += iov[2].iov_len;
iovcnt = 3;
}
}
ret = writev(conn->conn_socket, iov, iovcnt);
if (ret < 0) {
if (timed_out())
log_errx(1, "exiting due to timeout");
log_err(1, "writev");
}
if (ret != total_len)
log_errx(1, "short write");
}
void
pdu_delete(struct pdu *pdu)
{
free(pdu->pdu_data);
free(pdu->pdu_bhs);
free(pdu);
}