Bring in the new iSCSI target and initiator.
Reviewed by: ken (parts) Approved by: re (delphij) Sponsored by: FreeBSD Foundation
This commit is contained in:
parent
196beb5359
commit
009ea47eb2
@ -263,9 +263,13 @@ syslogd_flags="-s" # Flags to syslogd (if enabled).
|
||||
inetd_enable="NO" # Run the network daemon dispatcher (YES/NO).
|
||||
inetd_program="/usr/sbin/inetd" # path to inetd, if you want a different one.
|
||||
inetd_flags="-wW -C 60" # Optional flags to inetd
|
||||
iscsid_enable="NO" # iSCSI initiator daemon.
|
||||
iscsictl_enable="NO" # iSCSI initiator autostart.
|
||||
iscsictl_flags="-Aa" # Optional flags to iscsictl.
|
||||
hastd_enable="NO" # Run the HAST daemon (YES/NO).
|
||||
hastd_program="/sbin/hastd" # path to hastd, if you want a different one.
|
||||
hastd_flags="" # Optional flags to hastd.
|
||||
ctld_enable="NO" # CAM Target Layer / iSCSI target daemon.
|
||||
#
|
||||
# named. It may be possible to run named in a sandbox, man security for
|
||||
# details.
|
||||
|
@ -30,6 +30,7 @@ FILES= DAEMON \
|
||||
cleanvar \
|
||||
cleartmp \
|
||||
cron \
|
||||
ctld \
|
||||
ddb \
|
||||
defaultroute \
|
||||
devd \
|
||||
@ -62,6 +63,8 @@ FILES= DAEMON \
|
||||
ipnat \
|
||||
ipsec \
|
||||
${_ipxrouted} \
|
||||
iscsictl \
|
||||
iscsid \
|
||||
jail \
|
||||
kadmind \
|
||||
kerberos \
|
||||
|
22
etc/rc.d/ctld
Executable file
22
etc/rc.d/ctld
Executable file
@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# $FreeBSD$
|
||||
#
|
||||
|
||||
# PROVIDE: ctld
|
||||
# REQUIRE: FILESYSTEMS
|
||||
# BEFORE: DAEMON
|
||||
# KEYWORD: nojail
|
||||
|
||||
. /etc/rc.subr
|
||||
|
||||
name="ctld"
|
||||
rcvar="ctld_enable"
|
||||
pidfile="/var/run/${name}.pid"
|
||||
command="/usr/sbin/${name}"
|
||||
required_files="/etc/ctl.conf"
|
||||
required_modules="ctl"
|
||||
extra_commands="reload"
|
||||
|
||||
load_rc_config $name
|
||||
run_rc_command "$1"
|
20
etc/rc.d/iscsictl
Executable file
20
etc/rc.d/iscsictl
Executable file
@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# $FreeBSD$
|
||||
#
|
||||
|
||||
# PROVIDE: iscsictl
|
||||
# REQUIRE: NETWORK iscsid
|
||||
# BEFORE: DAEMON
|
||||
# KEYWORD: nojail
|
||||
|
||||
. /etc/rc.subr
|
||||
|
||||
name="iscsictl"
|
||||
rcvar="iscsictl_enable"
|
||||
command="/usr/bin/${name}"
|
||||
command_args="${iscsictl_flags}"
|
||||
required_modules="iscsi"
|
||||
|
||||
load_rc_config $name
|
||||
run_rc_command "$1"
|
20
etc/rc.d/iscsid
Executable file
20
etc/rc.d/iscsid
Executable file
@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# $FreeBSD$
|
||||
#
|
||||
|
||||
# PROVIDE: iscsid
|
||||
# REQUIRE: NETWORK
|
||||
# BEFORE: DAEMON
|
||||
# KEYWORD: nojail
|
||||
|
||||
. /etc/rc.subr
|
||||
|
||||
name="iscsid"
|
||||
rcvar="iscsid_enable"
|
||||
pidfile="/var/run/${name}.pid"
|
||||
command="/usr/sbin/${name}"
|
||||
required_modules="iscsi"
|
||||
|
||||
load_rc_config $name
|
||||
run_rc_command "$1"
|
@ -112,6 +112,7 @@ whatever options are specified, and start an iscsi-session.
|
||||
.Xr iscsi_initiator 4 ,
|
||||
.Xr sa 4 ,
|
||||
.Xr iscsi.conf 5 ,
|
||||
.Xr iscsictl 8 ,
|
||||
.Xr camcontrol 8
|
||||
.Sh STANDARDS
|
||||
RFC 3720
|
||||
|
@ -201,6 +201,7 @@ The parsing is very primitive, so do not expect - at the moment - any
|
||||
error messages.
|
||||
.Sh SEE ALSO
|
||||
.Xr iscsi_initiator 4 ,
|
||||
.Xr iscsictl 8 ,
|
||||
.Xr iscontrol 8
|
||||
.Sh STANDARDS
|
||||
ISCSI RFC 3720
|
||||
|
@ -75,8 +75,11 @@ Error injection support
|
||||
.It
|
||||
All I/O handled in-kernel, no userland context switch overhead
|
||||
.El
|
||||
.Pp
|
||||
It also serves as a kernel component of the native iSCSI target.
|
||||
.Sh SEE ALSO
|
||||
.Xr ctladm 8 ,
|
||||
.Xr ctld 8 ,
|
||||
.Xr ctlstat 8
|
||||
.Sh HISTORY
|
||||
The
|
||||
|
@ -3148,6 +3148,28 @@ ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
|
||||
sbuf_delete(sb);
|
||||
break;
|
||||
}
|
||||
case CTL_ISCSI: {
|
||||
struct ctl_iscsi *ci;
|
||||
struct ctl_frontend *fe;
|
||||
|
||||
ci = (struct ctl_iscsi *)addr;
|
||||
|
||||
mtx_lock(&softc->ctl_lock);
|
||||
STAILQ_FOREACH(fe, &softc->fe_list, links) {
|
||||
if (strcmp(fe->port_name, "iscsi") == 0)
|
||||
break;
|
||||
}
|
||||
mtx_unlock(&softc->ctl_lock);
|
||||
|
||||
if (fe == NULL) {
|
||||
ci->status = CTL_ISCSI_ERROR;
|
||||
snprintf(ci->error_str, sizeof(ci->error_str), "Backend \"iscsi\" not found.");
|
||||
break;
|
||||
}
|
||||
|
||||
retval = fe->ioctl(dev, cmd, addr, flag, td);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
/* XXX KDM should we fix this? */
|
||||
#if 0
|
||||
|
2638
sys/cam/ctl/ctl_frontend_iscsi.c
Normal file
2638
sys/cam/ctl/ctl_frontend_iscsi.c
Normal file
File diff suppressed because it is too large
Load Diff
112
sys/cam/ctl/ctl_frontend_iscsi.h
Normal file
112
sys/cam/ctl/ctl_frontend_iscsi.h
Normal file
@ -0,0 +1,112 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef CTL_FRONTEND_ISCSI_H
|
||||
#define CTL_FRONTEND_ISCSI_H
|
||||
|
||||
struct cfiscsi_target {
|
||||
TAILQ_ENTRY(cfiscsi_target) ct_next;
|
||||
int ct_luns[CTL_MAX_LUNS];
|
||||
struct cfiscsi_softc *ct_softc;
|
||||
volatile u_int ct_refcount;
|
||||
char ct_name[CTL_ISCSI_NAME_LEN];
|
||||
char ct_alias[CTL_ISCSI_ALIAS_LEN];
|
||||
};
|
||||
|
||||
struct cfiscsi_data_wait {
|
||||
TAILQ_ENTRY(cfiscsi_data_wait) cdw_next;
|
||||
union ctl_io *cdw_ctl_io;
|
||||
uint32_t cdw_target_transfer_tag;
|
||||
uint32_t cdw_initiator_task_tag;
|
||||
int cdw_sg_index;
|
||||
char *cdw_sg_addr;
|
||||
size_t cdw_sg_len;
|
||||
};
|
||||
|
||||
#define CFISCSI_SESSION_STATE_INVALID 0
|
||||
#define CFISCSI_SESSION_STATE_BHS 1
|
||||
#define CFISCSI_SESSION_STATE_AHS 2
|
||||
#define CFISCSI_SESSION_STATE_HEADER_DIGEST 3
|
||||
#define CFISCSI_SESSION_STATE_DATA 4
|
||||
#define CFISCSI_SESSION_STATE_DATA_DIGEST 5
|
||||
|
||||
struct cfiscsi_session {
|
||||
TAILQ_ENTRY(cfiscsi_session) cs_next;
|
||||
struct mtx cs_lock;
|
||||
struct icl_conn *cs_conn;
|
||||
uint32_t cs_cmdsn;
|
||||
uint32_t cs_statsn;
|
||||
uint32_t cs_target_transfer_tag;
|
||||
volatile u_int cs_outstanding_ctl_pdus;
|
||||
TAILQ_HEAD(, cfiscsi_data_wait) cs_waiting_for_data_out;
|
||||
struct cfiscsi_target *cs_target;
|
||||
struct callout cs_callout;
|
||||
int cs_timeout;
|
||||
int cs_portal_group_tag;
|
||||
struct cv cs_maintenance_cv;
|
||||
int cs_terminating;
|
||||
size_t cs_max_data_segment_length;
|
||||
size_t cs_max_burst_length;
|
||||
bool cs_immediate_data;
|
||||
char cs_initiator_name[CTL_ISCSI_NAME_LEN];
|
||||
char cs_initiator_addr[CTL_ISCSI_ADDR_LEN];
|
||||
char cs_initiator_alias[CTL_ISCSI_ALIAS_LEN];
|
||||
unsigned int cs_id;
|
||||
int cs_ctl_initid;
|
||||
#ifdef ICL_KERNEL_PROXY
|
||||
bool cs_login_phase;
|
||||
bool cs_waiting_for_ctld;
|
||||
struct cv cs_login_cv;
|
||||
struct icl_pdu *cs_login_pdu;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef ICL_KERNEL_PROXY
|
||||
struct icl_listen;
|
||||
#endif
|
||||
|
||||
struct cfiscsi_softc {
|
||||
struct ctl_frontend fe;
|
||||
struct mtx lock;
|
||||
char port_name[32];
|
||||
int online;
|
||||
unsigned int last_session_id;
|
||||
TAILQ_HEAD(, cfiscsi_target) targets;
|
||||
TAILQ_HEAD(, cfiscsi_session) sessions;
|
||||
char ctl_initids[CTL_MAX_INIT_PER_PORT];
|
||||
int max_initiators;
|
||||
#ifdef ICL_KERNEL_PROXY
|
||||
struct icl_listen *listener;
|
||||
struct cv accept_cv;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* !CTL_FRONTEND_ISCSI_H */
|
@ -40,6 +40,12 @@
|
||||
#ifndef _CTL_IOCTL_H_
|
||||
#define _CTL_IOCTL_H_
|
||||
|
||||
#ifdef ICL_KERNEL_PROXY
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#include <sys/ioccom.h>
|
||||
|
||||
#define CTL_DEFAULT_DEV "/dev/cam/ctl"
|
||||
/*
|
||||
* Maximum number of targets we support.
|
||||
@ -588,6 +594,168 @@ struct ctl_lun_list {
|
||||
/* passed to userland */
|
||||
};
|
||||
|
||||
/*
|
||||
* iSCSI status
|
||||
*
|
||||
* OK: Request completed successfully.
|
||||
*
|
||||
* ERROR: An error occured, look at the error string for a
|
||||
* description of the error.
|
||||
*
|
||||
* CTL_ISCSI_LIST_NEED_MORE_SPACE:
|
||||
* User has to pass larger buffer for CTL_ISCSI_LIST ioctl.
|
||||
*/
|
||||
typedef enum {
|
||||
CTL_ISCSI_OK,
|
||||
CTL_ISCSI_ERROR,
|
||||
CTL_ISCSI_LIST_NEED_MORE_SPACE,
|
||||
CTL_ISCSI_SESSION_NOT_FOUND
|
||||
} ctl_iscsi_status;
|
||||
|
||||
typedef enum {
|
||||
CTL_ISCSI_HANDOFF,
|
||||
CTL_ISCSI_LIST,
|
||||
CTL_ISCSI_LOGOUT,
|
||||
CTL_ISCSI_TERMINATE,
|
||||
#ifdef ICL_KERNEL_PROXY
|
||||
CTL_ISCSI_LISTEN,
|
||||
CTL_ISCSI_ACCEPT,
|
||||
CTL_ISCSI_SEND,
|
||||
CTL_ISCSI_RECEIVE,
|
||||
CTL_ISCSI_CLOSE,
|
||||
#endif
|
||||
} ctl_iscsi_type;
|
||||
|
||||
typedef enum {
|
||||
CTL_ISCSI_DIGEST_NONE,
|
||||
CTL_ISCSI_DIGEST_CRC32C
|
||||
} ctl_iscsi_digest;
|
||||
|
||||
#define CTL_ISCSI_NAME_LEN 224 /* 223 bytes, by RFC 3720, + '\0' */
|
||||
#define CTL_ISCSI_ADDR_LEN 47 /* INET6_ADDRSTRLEN + '\0' */
|
||||
#define CTL_ISCSI_ALIAS_LEN 128 /* Arbitrary. */
|
||||
|
||||
struct ctl_iscsi_handoff_params {
|
||||
char initiator_name[CTL_ISCSI_NAME_LEN];
|
||||
char initiator_addr[CTL_ISCSI_ADDR_LEN];
|
||||
char initiator_alias[CTL_ISCSI_ALIAS_LEN];
|
||||
char target_name[CTL_ISCSI_NAME_LEN];
|
||||
#ifdef ICL_KERNEL_PROXY
|
||||
int connection_id;
|
||||
/*
|
||||
* XXX
|
||||
*/
|
||||
int socket;
|
||||
#else
|
||||
int socket;
|
||||
#endif
|
||||
int portal_group_tag;
|
||||
|
||||
/*
|
||||
* Connection parameters negotiated by ctld(8).
|
||||
*/
|
||||
ctl_iscsi_digest header_digest;
|
||||
ctl_iscsi_digest data_digest;
|
||||
uint32_t cmdsn;
|
||||
uint32_t statsn;
|
||||
uint32_t max_recv_data_segment_length;
|
||||
uint32_t max_burst_length;
|
||||
uint32_t first_burst_length;
|
||||
uint32_t immediate_data;
|
||||
};
|
||||
|
||||
struct ctl_iscsi_list_params {
|
||||
uint32_t alloc_len; /* passed to kernel */
|
||||
char *conn_xml; /* filled in kernel */
|
||||
uint32_t fill_len; /* passed to userland */
|
||||
};
|
||||
|
||||
struct ctl_iscsi_logout_params {
|
||||
int connection_id; /* passed to kernel */
|
||||
char initiator_name[CTL_ISCSI_NAME_LEN];
|
||||
/* passed to kernel */
|
||||
char initiator_addr[CTL_ISCSI_ADDR_LEN];
|
||||
/* passed to kernel */
|
||||
int all; /* passed to kernel */
|
||||
};
|
||||
|
||||
struct ctl_iscsi_terminate_params {
|
||||
int connection_id; /* passed to kernel */
|
||||
char initiator_name[CTL_ISCSI_NAME_LEN];
|
||||
/* passed to kernel */
|
||||
char initiator_addr[CTL_ISCSI_NAME_LEN];
|
||||
/* passed to kernel */
|
||||
int all; /* passed to kernel */
|
||||
};
|
||||
|
||||
#ifdef ICL_KERNEL_PROXY
|
||||
struct ctl_iscsi_listen_params {
|
||||
int iser;
|
||||
int domain;
|
||||
int socktype;
|
||||
int protocol;
|
||||
struct sockaddr *addr;
|
||||
socklen_t addrlen;
|
||||
};
|
||||
|
||||
struct ctl_iscsi_accept_params {
|
||||
int connection_id;
|
||||
};
|
||||
|
||||
struct ctl_iscsi_send_params {
|
||||
int connection_id;
|
||||
void *bhs;
|
||||
size_t spare;
|
||||
void *spare2;
|
||||
size_t data_segment_len;
|
||||
void *data_segment;
|
||||
};
|
||||
|
||||
struct ctl_iscsi_receive_params {
|
||||
int connection_id;
|
||||
void *bhs;
|
||||
size_t spare;
|
||||
void *spare2;
|
||||
size_t data_segment_len;
|
||||
void *data_segment;
|
||||
};
|
||||
|
||||
struct ctl_iscsi_close_params {
|
||||
int connection_id;
|
||||
};
|
||||
#endif /* ICL_KERNEL_PROXY */
|
||||
|
||||
union ctl_iscsi_data {
|
||||
struct ctl_iscsi_handoff_params handoff;
|
||||
struct ctl_iscsi_list_params list;
|
||||
struct ctl_iscsi_logout_params logout;
|
||||
struct ctl_iscsi_terminate_params terminate;
|
||||
#ifdef ICL_KERNEL_PROXY
|
||||
struct ctl_iscsi_listen_params listen;
|
||||
struct ctl_iscsi_accept_params accept;
|
||||
struct ctl_iscsi_send_params send;
|
||||
struct ctl_iscsi_receive_params receive;
|
||||
struct ctl_iscsi_close_params close;
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* iSCSI interface
|
||||
*
|
||||
* status: The status of the request. See above for the
|
||||
* description of the values of this field.
|
||||
*
|
||||
* error_str: If the status indicates an error, this string will
|
||||
* be filled in to describe the error.
|
||||
*/
|
||||
struct ctl_iscsi {
|
||||
ctl_iscsi_type type; /* passed to kernel */
|
||||
union ctl_iscsi_data data; /* passed to kernel */
|
||||
ctl_iscsi_status status; /* passed to userland */
|
||||
char error_str[CTL_ERROR_STR_LEN];
|
||||
/* passed to userland */
|
||||
};
|
||||
|
||||
#define CTL_IO _IOWR(CTL_MINOR, 0x00, union ctl_io)
|
||||
#define CTL_ENABLE_PORT _IOW(CTL_MINOR, 0x04, struct ctl_port_entry)
|
||||
#define CTL_DISABLE_PORT _IOW(CTL_MINOR, 0x05, struct ctl_port_entry)
|
||||
@ -612,6 +780,7 @@ struct ctl_lun_list {
|
||||
#define CTL_LUN_LIST _IOWR(CTL_MINOR, 0x22, struct ctl_lun_list)
|
||||
#define CTL_ERROR_INJECT_DELETE _IOW(CTL_MINOR, 0x23, struct ctl_error_desc)
|
||||
#define CTL_SET_PORT_WWNS _IOW(CTL_MINOR, 0x24, struct ctl_port_entry)
|
||||
#define CTL_ISCSI _IOWR(CTL_MINOR, 0x25, struct ctl_iscsi)
|
||||
|
||||
#endif /* _CTL_IOCTL_H_ */
|
||||
|
||||
|
@ -124,6 +124,7 @@ cam/ctl/ctl_cmd_table.c optional ctl
|
||||
cam/ctl/ctl_frontend.c optional ctl
|
||||
cam/ctl/ctl_frontend_cam_sim.c optional ctl
|
||||
cam/ctl/ctl_frontend_internal.c optional ctl
|
||||
cam/ctl/ctl_frontend_iscsi.c optional ctl
|
||||
cam/ctl/ctl_mem_pool.c optional ctl
|
||||
cam/ctl/ctl_scsi_all.c optional ctl
|
||||
cam/ctl/ctl_error.c optional ctl
|
||||
@ -1531,6 +1532,9 @@ ipw_monitor.fw optional ipwmonitorfw | ipwfw \
|
||||
compile-with "${NORMAL_FW}" \
|
||||
no-obj no-implicit-rule \
|
||||
clean "ipw_monitor.fw"
|
||||
dev/iscsi/icl.c optional iscsi | ctl
|
||||
dev/iscsi/icl_proxy.c optional iscsi | ctl
|
||||
dev/iscsi/iscsi.c optional iscsi scbus
|
||||
dev/iscsi_initiator/iscsi.c optional iscsi_initiator scbus
|
||||
dev/iscsi_initiator/iscsi_subr.c optional iscsi_initiator scbus
|
||||
dev/iscsi_initiator/isc_cam.c optional iscsi_initiator scbus
|
||||
|
1292
sys/dev/iscsi/icl.c
Normal file
1292
sys/dev/iscsi/icl.c
Normal file
File diff suppressed because it is too large
Load Diff
151
sys/dev/iscsi/icl.h
Normal file
151
sys/dev/iscsi/icl.h
Normal file
@ -0,0 +1,151 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef ICL_H
|
||||
#define ICL_H
|
||||
|
||||
/*
|
||||
* iSCSI Common Layer. It's used by both the initiator and target to send
|
||||
* and receive iSCSI PDUs.
|
||||
*/
|
||||
|
||||
struct icl_conn;
|
||||
|
||||
struct icl_pdu {
|
||||
TAILQ_ENTRY(icl_pdu) ip_next;
|
||||
struct icl_conn *ip_conn;
|
||||
struct iscsi_bhs *ip_bhs;
|
||||
struct mbuf *ip_bhs_mbuf;
|
||||
size_t ip_ahs_len;
|
||||
struct mbuf *ip_ahs_mbuf;
|
||||
size_t ip_data_len;
|
||||
struct mbuf *ip_data_mbuf;
|
||||
|
||||
/*
|
||||
* User (initiator or provider) private fields.
|
||||
*/
|
||||
uint32_t ip_prv0;
|
||||
uint32_t ip_prv1;
|
||||
uint32_t ip_prv2;
|
||||
};
|
||||
|
||||
struct icl_pdu *icl_pdu_new_bhs(struct icl_conn *ic, int flags);
|
||||
size_t icl_pdu_data_segment_length(const struct icl_pdu *ip);
|
||||
int icl_pdu_append_data(struct icl_pdu *ip, const void *addr, size_t len, int flags);
|
||||
void icl_pdu_get_data(struct icl_pdu *ip, size_t off, void *addr, size_t len);
|
||||
void icl_pdu_queue(struct icl_pdu *ip);
|
||||
void icl_pdu_free(struct icl_pdu *ip);
|
||||
|
||||
#define ICL_CONN_STATE_INVALID 0
|
||||
#define ICL_CONN_STATE_BHS 1
|
||||
#define ICL_CONN_STATE_AHS 2
|
||||
#define ICL_CONN_STATE_HEADER_DIGEST 3
|
||||
#define ICL_CONN_STATE_DATA 4
|
||||
#define ICL_CONN_STATE_DATA_DIGEST 5
|
||||
|
||||
#define ICL_MAX_DATA_SEGMENT_LENGTH (128 * 1024)
|
||||
|
||||
struct icl_conn {
|
||||
struct mtx ic_lock;
|
||||
struct socket *ic_socket;
|
||||
volatile u_int ic_outstanding_pdus;
|
||||
TAILQ_HEAD(, icl_pdu) ic_to_send;
|
||||
size_t ic_receive_len;
|
||||
int ic_receive_state;
|
||||
struct icl_pdu *ic_receive_pdu;
|
||||
struct cv ic_send_cv;
|
||||
struct cv ic_receive_cv;
|
||||
bool ic_header_crc32c;
|
||||
bool ic_data_crc32c;
|
||||
bool ic_send_running;
|
||||
bool ic_receive_running;
|
||||
size_t ic_max_data_segment_length;
|
||||
bool ic_disconnecting;
|
||||
bool ic_iser;
|
||||
|
||||
void (*ic_receive)(struct icl_pdu *);
|
||||
void (*ic_error)(struct icl_conn *);
|
||||
|
||||
/*
|
||||
* User (initiator or provider) private fields.
|
||||
*/
|
||||
void *ic_prv0;
|
||||
};
|
||||
|
||||
struct icl_conn *icl_conn_new(void);
|
||||
void icl_conn_free(struct icl_conn *ic);
|
||||
int icl_conn_handoff(struct icl_conn *ic, int fd);
|
||||
void icl_conn_shutdown(struct icl_conn *ic);
|
||||
void icl_conn_close(struct icl_conn *ic);
|
||||
bool icl_conn_connected(struct icl_conn *ic);
|
||||
|
||||
#ifdef ICL_KERNEL_PROXY
|
||||
|
||||
struct sockaddr;
|
||||
struct icl_listen;
|
||||
|
||||
struct icl_listen_sock {
|
||||
TAILQ_ENTRY(icl_listen_sock) ils_next;
|
||||
struct icl_listen *ils_listen;
|
||||
struct socket *ils_socket;
|
||||
bool ils_running;
|
||||
bool ils_disconnecting;
|
||||
};
|
||||
|
||||
struct icl_listen {
|
||||
TAILQ_HEAD(, icl_listen_sock) il_sockets;
|
||||
struct sx il_lock;
|
||||
void (*il_accept)(struct socket *);
|
||||
};
|
||||
|
||||
/*
|
||||
* Initiator part.
|
||||
*/
|
||||
int icl_conn_connect(struct icl_conn *ic, bool rdma,
|
||||
int domain, int socktype, int protocol,
|
||||
struct sockaddr *from_sa, struct sockaddr *to_sa);
|
||||
/*
|
||||
* Target part.
|
||||
*/
|
||||
struct icl_listen *icl_listen_new(void (*accept_cb)(struct socket *));
|
||||
void icl_listen_free(struct icl_listen *il);
|
||||
int icl_listen_add(struct icl_listen *il, bool rdma, int domain,
|
||||
int socktype, int protocol, struct sockaddr *sa);
|
||||
int icl_listen_remove(struct icl_listen *il, struct sockaddr *sa);
|
||||
|
||||
/*
|
||||
* This one is not a public API; only to be used by icl_proxy.c.
|
||||
*/
|
||||
int icl_conn_handoff_sock(struct icl_conn *ic, struct socket *so);
|
||||
|
||||
#endif /* ICL_KERNEL_PROXY */
|
||||
|
||||
#endif /* !ICL_H */
|
397
sys/dev/iscsi/icl_proxy.c
Normal file
397
sys/dev/iscsi/icl_proxy.c
Normal file
@ -0,0 +1,397 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
/*-
|
||||
* Copyright (c) 1982, 1986, 1989, 1990, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* sendfile(2) and related extensions:
|
||||
* Copyright (c) 1998, David Greenman. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
|
||||
*
|
||||
* @(#)uipc_syscalls.c 8.4 (Berkeley) 2/21/94
|
||||
*/
|
||||
|
||||
/*
|
||||
* iSCSI Common Layer, kernel proxy part.
|
||||
*/
|
||||
|
||||
#ifdef ICL_KERNEL_PROXY
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/capability.h>
|
||||
#include <sys/condvar.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/kthread.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/socketvar.h>
|
||||
#include <sys/sx.h>
|
||||
#include <sys/systm.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <linux/types.h>
|
||||
#include <rdma/rdma_cm.h>
|
||||
|
||||
#include "icl.h"
|
||||
|
||||
static int debug = 1;
|
||||
|
||||
#define ICL_DEBUG(X, ...) \
|
||||
if (debug > 1) { \
|
||||
printf("%s: " X "\n", __func__, ## __VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define ICL_WARN(X, ...) \
|
||||
if (debug > 0) { \
|
||||
printf("WARNING: %s: " X "\n", \
|
||||
__func__, ## __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
static MALLOC_DEFINE(M_ICL_PROXY, "ICL_PROXY", "iSCSI common layer proxy");
|
||||
|
||||
#ifdef ICL_RDMA
|
||||
static int icl_conn_connect_rdma(struct icl_conn *ic, int domain, int socktype,
|
||||
int protocol, struct sockaddr *from_sa, struct sockaddr *to_sa);
|
||||
static int icl_listen_add_rdma(struct icl_listen *il, int domain, int socktype, int protocol,
|
||||
struct sockaddr *sa);
|
||||
#endif /* ICL_RDMA */
|
||||
|
||||
static int
|
||||
icl_conn_connect_tcp(struct icl_conn *ic, int domain, int socktype,
|
||||
int protocol, struct sockaddr *from_sa, struct sockaddr *to_sa)
|
||||
{
|
||||
struct socket *so;
|
||||
int error;
|
||||
int interrupted = 0;
|
||||
|
||||
error = socreate(domain, &so, socktype, protocol,
|
||||
curthread->td_ucred, curthread);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
if (from_sa != NULL) {
|
||||
error = sobind(so, from_sa, curthread);
|
||||
if (error != 0) {
|
||||
soclose(so);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
|
||||
error = soconnect(so, to_sa, curthread);
|
||||
if (error != 0) {
|
||||
soclose(so);
|
||||
return (error);
|
||||
}
|
||||
|
||||
SOCK_LOCK(so);
|
||||
while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
|
||||
error = msleep(&so->so_timeo, SOCK_MTX(so), PSOCK | PCATCH,
|
||||
"icl_connect", 0);
|
||||
if (error) {
|
||||
if (error == EINTR || error == ERESTART)
|
||||
interrupted = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (error == 0) {
|
||||
error = so->so_error;
|
||||
so->so_error = 0;
|
||||
}
|
||||
SOCK_UNLOCK(so);
|
||||
|
||||
if (error != 0) {
|
||||
soclose(so);
|
||||
return (error);
|
||||
}
|
||||
|
||||
error = icl_conn_handoff_sock(ic, so);
|
||||
if (error != 0)
|
||||
soclose(so);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
icl_conn_connect(struct icl_conn *ic, bool rdma, int domain, int socktype,
|
||||
int protocol, struct sockaddr *from_sa, struct sockaddr *to_sa)
|
||||
{
|
||||
|
||||
if (rdma) {
|
||||
#ifdef ICL_RDMA
|
||||
return (icl_conn_connect_rdma(ic, domain, socktype, protocol, from_sa, to_sa));
|
||||
#else
|
||||
ICL_DEBUG("RDMA not supported");
|
||||
return (EOPNOTSUPP);
|
||||
#endif
|
||||
}
|
||||
|
||||
return (icl_conn_connect_tcp(ic, domain, socktype, protocol, from_sa, to_sa));
|
||||
}
|
||||
|
||||
struct icl_listen *
|
||||
icl_listen_new(void (*accept_cb)(struct socket *))
|
||||
{
|
||||
struct icl_listen *il;
|
||||
|
||||
il = malloc(sizeof(*il), M_ICL_PROXY, M_ZERO | M_WAITOK);
|
||||
TAILQ_INIT(&il->il_sockets);
|
||||
sx_init(&il->il_lock, "icl_listen");
|
||||
il->il_accept = accept_cb;
|
||||
|
||||
return (il);
|
||||
}
|
||||
|
||||
void
|
||||
icl_listen_free(struct icl_listen *il)
|
||||
{
|
||||
struct icl_listen_sock *ils;
|
||||
|
||||
sx_xlock(&il->il_lock);
|
||||
while (!TAILQ_EMPTY(&il->il_sockets)) {
|
||||
ils = TAILQ_FIRST(&il->il_sockets);
|
||||
while (ils->ils_running) {
|
||||
ICL_DEBUG("waiting for accept thread to terminate");
|
||||
sx_xunlock(&il->il_lock);
|
||||
ils->ils_disconnecting = true;
|
||||
wakeup(&ils->ils_socket->so_timeo);
|
||||
pause("icl_unlisten", 1 * hz);
|
||||
sx_xlock(&il->il_lock);
|
||||
}
|
||||
|
||||
TAILQ_REMOVE(&il->il_sockets, ils, ils_next);
|
||||
soclose(ils->ils_socket);
|
||||
free(ils, M_ICL_PROXY);
|
||||
}
|
||||
sx_xunlock(&il->il_lock);
|
||||
|
||||
free(il, M_ICL_PROXY);
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: Doing accept in a separate thread in each socket might not be the best way
|
||||
* to do stuff, but it's pretty clean and debuggable - and you probably won't
|
||||
* have hundreds of listening sockets anyway.
|
||||
*/
|
||||
static void
|
||||
icl_accept_thread(void *arg)
|
||||
{
|
||||
struct icl_listen_sock *ils;
|
||||
struct socket *head, *so;
|
||||
struct sockaddr *sa;
|
||||
int error;
|
||||
|
||||
ils = arg;
|
||||
head = ils->ils_socket;
|
||||
|
||||
ils->ils_running = true;
|
||||
|
||||
for (;;) {
|
||||
ACCEPT_LOCK();
|
||||
while (TAILQ_EMPTY(&head->so_comp) && head->so_error == 0 && ils->ils_disconnecting == false) {
|
||||
if (head->so_rcv.sb_state & SBS_CANTRCVMORE) {
|
||||
head->so_error = ECONNABORTED;
|
||||
break;
|
||||
}
|
||||
error = msleep(&head->so_timeo, &accept_mtx, PSOCK | PCATCH,
|
||||
"accept", 0);
|
||||
if (error) {
|
||||
ACCEPT_UNLOCK();
|
||||
ICL_WARN("msleep failed with error %d", error);
|
||||
continue;
|
||||
}
|
||||
if (ils->ils_disconnecting) {
|
||||
ACCEPT_UNLOCK();
|
||||
ICL_DEBUG("terminating");
|
||||
ils->ils_running = false;
|
||||
kthread_exit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (head->so_error) {
|
||||
error = head->so_error;
|
||||
head->so_error = 0;
|
||||
ACCEPT_UNLOCK();
|
||||
ICL_WARN("socket error %d", error);
|
||||
continue;
|
||||
}
|
||||
so = TAILQ_FIRST(&head->so_comp);
|
||||
KASSERT(so != NULL, ("NULL so"));
|
||||
KASSERT(!(so->so_qstate & SQ_INCOMP), ("accept1: so SQ_INCOMP"));
|
||||
KASSERT(so->so_qstate & SQ_COMP, ("accept1: so not SQ_COMP"));
|
||||
|
||||
/*
|
||||
* Before changing the flags on the socket, we have to bump the
|
||||
* reference count. Otherwise, if the protocol calls sofree(),
|
||||
* the socket will be released due to a zero refcount.
|
||||
*/
|
||||
SOCK_LOCK(so); /* soref() and so_state update */
|
||||
soref(so); /* file descriptor reference */
|
||||
|
||||
TAILQ_REMOVE(&head->so_comp, so, so_list);
|
||||
head->so_qlen--;
|
||||
so->so_state |= (head->so_state & SS_NBIO);
|
||||
so->so_qstate &= ~SQ_COMP;
|
||||
so->so_head = NULL;
|
||||
|
||||
SOCK_UNLOCK(so);
|
||||
ACCEPT_UNLOCK();
|
||||
|
||||
sa = NULL;
|
||||
error = soaccept(so, &sa);
|
||||
if (error != 0) {
|
||||
ICL_WARN("soaccept error %d", error);
|
||||
if (sa != NULL)
|
||||
free(sa, M_SONAME);
|
||||
soclose(so);
|
||||
}
|
||||
|
||||
(ils->ils_listen->il_accept)(so);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
icl_listen_add_tcp(struct icl_listen *il, int domain, int socktype, int protocol,
|
||||
struct sockaddr *sa)
|
||||
{
|
||||
struct icl_listen_sock *ils;
|
||||
struct socket *so;
|
||||
struct sockopt sopt;
|
||||
int error, one = 1;
|
||||
|
||||
error = socreate(domain, &so, socktype, protocol,
|
||||
curthread->td_ucred, curthread);
|
||||
if (error != 0) {
|
||||
ICL_WARN("socreate failed with error %d", error);
|
||||
return (error);
|
||||
}
|
||||
|
||||
sopt.sopt_dir = SOPT_SET;
|
||||
sopt.sopt_level = SOL_SOCKET;
|
||||
sopt.sopt_name = SO_REUSEADDR;
|
||||
sopt.sopt_val = &one;
|
||||
sopt.sopt_valsize = sizeof(one);
|
||||
sopt.sopt_td = NULL;
|
||||
error = sosetopt(so, &sopt);
|
||||
if (error != 0) {
|
||||
ICL_WARN("failed to set SO_REUSEADDR with error %d", error);
|
||||
soclose(so);
|
||||
return (error);
|
||||
}
|
||||
|
||||
error = sobind(so, sa, curthread);
|
||||
if (error != 0) {
|
||||
ICL_WARN("sobind failed with error %d", error);
|
||||
soclose(so);
|
||||
return (error);
|
||||
}
|
||||
|
||||
error = solisten(so, -1, curthread);
|
||||
if (error != 0) {
|
||||
ICL_WARN("solisten failed with error %d", error);
|
||||
soclose(so);
|
||||
return (error);
|
||||
}
|
||||
|
||||
ils = malloc(sizeof(*ils), M_ICL_PROXY, M_ZERO | M_WAITOK);
|
||||
ils->ils_listen = il;
|
||||
ils->ils_socket = so;
|
||||
|
||||
error = kthread_add(icl_accept_thread, ils, NULL, NULL, 0, 0, "iclacc");
|
||||
if (error != 0) {
|
||||
ICL_WARN("kthread_add failed with error %d", error);
|
||||
soclose(so);
|
||||
free(ils, M_ICL_PROXY);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
sx_xlock(&il->il_lock);
|
||||
TAILQ_INSERT_TAIL(&il->il_sockets, ils, ils_next);
|
||||
sx_xunlock(&il->il_lock);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
icl_listen_add(struct icl_listen *il, bool rdma, int domain, int socktype, int protocol,
|
||||
struct sockaddr *sa)
|
||||
{
|
||||
|
||||
if (rdma) {
|
||||
#ifndef ICL_RDMA
|
||||
ICL_DEBUG("RDMA not supported");
|
||||
return (EOPNOTSUPP);
|
||||
#else
|
||||
return (icl_listen_add_rdma(il, domain, socktype, protocol, sa));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
return (icl_listen_add_tcp(il, domain, socktype, protocol, sa));
|
||||
}
|
||||
|
||||
int
|
||||
icl_listen_remove(struct icl_listen *il, struct sockaddr *sa)
|
||||
{
|
||||
|
||||
/*
|
||||
* XXX
|
||||
*/
|
||||
|
||||
return (EOPNOTSUPP);
|
||||
}
|
||||
|
||||
#endif /* ICL_KERNEL_PROXY */
|
2109
sys/dev/iscsi/iscsi.c
Normal file
2109
sys/dev/iscsi/iscsi.c
Normal file
File diff suppressed because it is too large
Load Diff
135
sys/dev/iscsi/iscsi.h
Normal file
135
sys/dev/iscsi/iscsi.h
Normal file
@ -0,0 +1,135 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef ISCSI_H
|
||||
#define ISCSI_H
|
||||
|
||||
struct iscsi_softc;
|
||||
struct icl_conn;
|
||||
|
||||
#define ISCSI_NAME_LEN 224 /* 223 bytes, by RFC 3720, + '\0' */
|
||||
#define ISCSI_ADDR_LEN 47 /* INET6_ADDRSTRLEN + '\0' */
|
||||
#define ISCSI_SECRET_LEN 17 /* 16 + '\0' */
|
||||
|
||||
struct iscsi_outstanding {
|
||||
TAILQ_ENTRY(iscsi_outstanding) io_next;
|
||||
union ccb *io_ccb;
|
||||
size_t io_received;
|
||||
uint32_t io_initiator_task_tag;
|
||||
uint32_t io_datasn;
|
||||
};
|
||||
|
||||
struct iscsi_session {
|
||||
TAILQ_ENTRY(iscsi_session) is_next;
|
||||
|
||||
struct icl_conn *is_conn;
|
||||
struct mtx is_lock;
|
||||
|
||||
uint32_t is_statsn;
|
||||
uint32_t is_cmdsn;
|
||||
uint32_t is_expcmdsn;
|
||||
uint32_t is_maxcmdsn;
|
||||
uint32_t is_initiator_task_tag;
|
||||
int is_header_digest;
|
||||
int is_data_digest;
|
||||
int is_initial_r2t;
|
||||
size_t is_max_burst_length;
|
||||
size_t is_first_burst_length;
|
||||
uint8_t is_isid[6];
|
||||
bool is_immediate_data;
|
||||
size_t is_max_data_segment_length;
|
||||
char is_target_alias[ISCSI_ALIAS_LEN];
|
||||
|
||||
TAILQ_HEAD(, iscsi_outstanding) is_outstanding;
|
||||
TAILQ_HEAD(, icl_pdu) is_postponed;
|
||||
|
||||
struct callout is_callout;
|
||||
unsigned int is_timeout;
|
||||
|
||||
/*
|
||||
* XXX: This could be rewritten using a single variable,
|
||||
* but somehow it results in uglier code.
|
||||
*/
|
||||
/*
|
||||
* We're waiting for iscsid(8); after iscsid_timeout
|
||||
* expires, kernel will wake up an iscsid(8) to handle
|
||||
* the session.
|
||||
*/
|
||||
bool is_waiting_for_iscsid;
|
||||
|
||||
/*
|
||||
* Some iscsid(8) instance is handling the session;
|
||||
* after login_timeout expires, kernel will wake up
|
||||
* another iscsid(8) to handle the session.
|
||||
*/
|
||||
bool is_login_phase;
|
||||
|
||||
/*
|
||||
* We're in the process of removing the iSCSI session.
|
||||
*/
|
||||
bool is_terminating;
|
||||
|
||||
/*
|
||||
* We're waiting for the maintenance thread to do some
|
||||
* reconnection tasks.
|
||||
*/
|
||||
bool is_reconnecting;
|
||||
|
||||
bool is_connected;
|
||||
|
||||
struct cam_devq *is_devq;
|
||||
struct cam_sim *is_sim;
|
||||
struct cam_path *is_path;
|
||||
struct cv is_maintenance_cv;
|
||||
struct iscsi_softc *is_softc;
|
||||
unsigned int is_id;
|
||||
struct iscsi_session_conf is_conf;
|
||||
bool is_simq_frozen;
|
||||
|
||||
char is_reason[ISCSI_REASON_LEN];
|
||||
|
||||
#ifdef ICL_KERNEL_PROXY
|
||||
struct cv is_login_cv;;
|
||||
struct icl_pdu *is_login_pdu;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct iscsi_softc {
|
||||
device_t sc_dev;
|
||||
struct sx sc_lock;
|
||||
struct cdev *sc_cdev;
|
||||
TAILQ_HEAD(, iscsi_session) sc_sessions;
|
||||
struct cv sc_cv;
|
||||
unsigned int sc_last_session_id;
|
||||
eventhandler_tag sc_shutdown_eh;
|
||||
};
|
||||
|
||||
#endif /* !ISCSI_H */
|
201
sys/dev/iscsi/iscsi_ioctl.h
Normal file
201
sys/dev/iscsi/iscsi_ioctl.h
Normal file
@ -0,0 +1,201 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef ISCSI_IOCTL_H
|
||||
#define ISCSI_IOCTL_H
|
||||
|
||||
#ifdef ICL_KERNEL_PROXY
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#define ISCSI_PATH "/dev/iscsi"
|
||||
#define ISCSI_MAX_DATA_SEGMENT_LENGTH (128 * 1024)
|
||||
|
||||
#define ISCSI_NAME_LEN 224 /* 223 bytes, by RFC 3720, + '\0' */
|
||||
#define ISCSI_ADDR_LEN 47 /* INET6_ADDRSTRLEN + '\0' */
|
||||
#define ISCSI_ALIAS_LEN 256 /* XXX: Where did it come from? */
|
||||
#define ISCSI_SECRET_LEN 17 /* 16 + '\0' */
|
||||
#define ISCSI_REASON_LEN 32
|
||||
|
||||
#define ISCSI_DIGEST_NONE 0
|
||||
#define ISCSI_DIGEST_CRC32C 1
|
||||
|
||||
/*
|
||||
* Session configuration, set when adding the session.
|
||||
*/
|
||||
struct iscsi_session_conf {
|
||||
char isc_initiator[ISCSI_NAME_LEN];
|
||||
char isc_initiator_addr[ISCSI_ADDR_LEN];
|
||||
char isc_initiator_alias[ISCSI_ALIAS_LEN];
|
||||
char isc_target[ISCSI_NAME_LEN];
|
||||
char isc_target_addr[ISCSI_ADDR_LEN];
|
||||
char isc_user[ISCSI_NAME_LEN];
|
||||
char isc_secret[ISCSI_SECRET_LEN];
|
||||
char isc_mutual_user[ISCSI_NAME_LEN];
|
||||
char isc_mutual_secret[ISCSI_SECRET_LEN];
|
||||
int isc_discovery;
|
||||
int isc_header_digest;
|
||||
int isc_data_digest;
|
||||
int isc_iser;
|
||||
int isc_spare[4];
|
||||
};
|
||||
|
||||
/*
|
||||
* Session state, negotiated by iscsid(8) and queried by iscsictl(8).
|
||||
*/
|
||||
struct iscsi_session_state {
|
||||
struct iscsi_session_conf iss_conf;
|
||||
unsigned int iss_id;
|
||||
char iss_target_alias[ISCSI_ALIAS_LEN];
|
||||
int iss_header_digest;
|
||||
int iss_data_digest;
|
||||
int iss_max_data_segment_length;
|
||||
int iss_immediate_data;
|
||||
int iss_connected;
|
||||
char iss_reason[ISCSI_REASON_LEN];
|
||||
int iss_spare[4];
|
||||
};
|
||||
|
||||
/*
|
||||
* For use with iscsid(8).
|
||||
*/
|
||||
|
||||
struct iscsi_daemon_request {
|
||||
unsigned int idr_session_id;
|
||||
struct iscsi_session_conf idr_conf;
|
||||
int idr_spare[4];
|
||||
};
|
||||
|
||||
struct iscsi_daemon_handoff {
|
||||
unsigned int idh_session_id;
|
||||
int idh_socket;
|
||||
char idh_target_alias[ISCSI_ALIAS_LEN];
|
||||
uint8_t idh_isid[6];
|
||||
uint32_t idh_statsn;
|
||||
int idh_header_digest;
|
||||
int idh_data_digest;
|
||||
int idh_initial_r2t;
|
||||
int idh_immediate_data;
|
||||
size_t idh_max_data_segment_length;
|
||||
size_t idh_max_burst_length;
|
||||
size_t idh_first_burst_length;
|
||||
};
|
||||
|
||||
struct iscsi_daemon_fail {
|
||||
unsigned int idf_session_id;
|
||||
char idf_reason[ISCSI_REASON_LEN];
|
||||
};
|
||||
|
||||
#define ISCSIDWAIT _IOR('I', 0x01, struct iscsi_daemon_request)
|
||||
#define ISCSIDHANDOFF _IOW('I', 0x02, struct iscsi_daemon_handoff)
|
||||
#define ISCSIDFAIL _IOW('I', 0x03, struct iscsi_daemon_fail)
|
||||
|
||||
#ifdef ICL_KERNEL_PROXY
|
||||
|
||||
/*
|
||||
* When ICL_KERNEL_PROXY is not defined, the iscsid(8) is responsible
|
||||
* for creating the socket, connecting, performing Login Phase using
|
||||
* socked in the usual userspace way, and then passing the socket file
|
||||
* descriptor to the kernel part using ISCSIDHANDOFF.
|
||||
*
|
||||
* When ICL_KERNEL_PROXY is defined, the iscsid(8) creates the session
|
||||
* using ISCSICONNECT, performs Login Phase using ISCSISEND/ISCSIRECEIVE
|
||||
* instead of read(2)/write(2), and then calls ISCSIDHANDOFF with
|
||||
* idh_socket set to 0.
|
||||
*
|
||||
* The purpose of ICL_KERNEL_PROXY is to workaround the fact that,
|
||||
* at this time, it's not possible to do iWARP (RDMA) in userspace.
|
||||
*/
|
||||
|
||||
struct iscsi_daemon_connect {
|
||||
int idc_session_id;
|
||||
int idc_iser;
|
||||
int idc_domain;
|
||||
int idc_socktype;
|
||||
int idc_protocol;
|
||||
struct sockaddr *idc_from_addr;
|
||||
socklen_t idc_from_addrlen;
|
||||
struct sockaddr *idc_to_addr;
|
||||
socklen_t idc_to_addrlen;
|
||||
};
|
||||
|
||||
struct iscsi_daemon_send {
|
||||
int ids_session_id;
|
||||
void *ids_bhs;
|
||||
size_t ids_spare;
|
||||
void *ids_spare2;
|
||||
size_t ids_data_segment_len;
|
||||
void *ids_data_segment;
|
||||
};
|
||||
|
||||
struct iscsi_daemon_receive {
|
||||
int idr_session_id;
|
||||
void *idr_bhs;
|
||||
size_t idr_spare;
|
||||
void *idr_spare2;
|
||||
size_t idr_data_segment_len;
|
||||
void *idr_data_segment;
|
||||
};
|
||||
|
||||
struct iscsi_daemon_close {
|
||||
int idc_session_id;
|
||||
};
|
||||
|
||||
#define ISCSIDCONNECT _IOWR('I', 0x04, struct iscsi_daemon_connect)
|
||||
#define ISCSIDSEND _IOWR('I', 0x05, struct iscsi_daemon_send)
|
||||
#define ISCSIDRECEIVE _IOWR('I', 0x06, struct iscsi_daemon_receive)
|
||||
#define ISCSIDCLOSE _IOWR('I', 0x07, struct iscsi_daemon_close)
|
||||
|
||||
#endif /* ICL_KERNEL_PROXY */
|
||||
|
||||
/*
|
||||
* For use with iscsictl(8).
|
||||
*/
|
||||
|
||||
struct iscsi_session_add {
|
||||
struct iscsi_session_conf isa_conf;
|
||||
};
|
||||
|
||||
struct iscsi_session_remove {
|
||||
unsigned int isr_session_id;
|
||||
struct iscsi_session_conf isr_conf;
|
||||
};
|
||||
|
||||
struct iscsi_session_list {
|
||||
unsigned int isl_nentries;
|
||||
struct iscsi_session_state *isl_pstates;
|
||||
};
|
||||
|
||||
#define ISCSISADD _IOW('I', 0x11, struct iscsi_session_add)
|
||||
#define ISCSISREMOVE _IOW('I', 0x12, struct iscsi_session_remove)
|
||||
#define ISCSISLIST _IOWR('I', 0x13, struct iscsi_session_list)
|
||||
|
||||
#endif /* !ISCSI_IOCTL_H */
|
439
sys/dev/iscsi/iscsi_proto.h
Normal file
439
sys/dev/iscsi/iscsi_proto.h
Normal file
@ -0,0 +1,439 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef ISCSI_PROTO_H
|
||||
#define ISCSI_PROTO_H
|
||||
|
||||
#ifndef CTASSERT
|
||||
#define CTASSERT(x) _CTASSERT(x, __LINE__)
|
||||
#define _CTASSERT(x, y) __CTASSERT(x, y)
|
||||
#define __CTASSERT(x, y) typedef char __assert_ ## y [(x) ? 1 : -1]
|
||||
#endif
|
||||
|
||||
#define ISCSI_BHS_SIZE 48
|
||||
#define ISCSI_HEADER_DIGEST_SIZE 4
|
||||
#define ISCSI_DATA_DIGEST_SIZE 4
|
||||
|
||||
#define ISCSI_BHS_OPCODE_IMMEDIATE 0x40
|
||||
|
||||
#define ISCSI_BHS_OPCODE_NOP_OUT 0x00
|
||||
#define ISCSI_BHS_OPCODE_SCSI_COMMAND 0x01
|
||||
#define ISCSI_BHS_OPCODE_TASK_REQUEST 0x02
|
||||
#define ISCSI_BHS_OPCODE_LOGIN_REQUEST 0x03
|
||||
#define ISCSI_BHS_OPCODE_TEXT_REQUEST 0x04
|
||||
#define ISCSI_BHS_OPCODE_SCSI_DATA_OUT 0x05
|
||||
#define ISCSI_BHS_OPCODE_LOGOUT_REQUEST 0x06
|
||||
|
||||
#define ISCSI_BHS_OPCODE_NOP_IN 0x20
|
||||
#define ISCSI_BHS_OPCODE_SCSI_RESPONSE 0x21
|
||||
#define ISCSI_BHS_OPCODE_TASK_RESPONSE 0x22
|
||||
#define ISCSI_BHS_OPCODE_LOGIN_RESPONSE 0x23
|
||||
#define ISCSI_BHS_OPCODE_TEXT_RESPONSE 0x24
|
||||
#define ISCSI_BHS_OPCODE_SCSI_DATA_IN 0x25
|
||||
#define ISCSI_BHS_OPCODE_LOGOUT_RESPONSE 0x26
|
||||
#define ISCSI_BHS_OPCODE_R2T 0x31
|
||||
#define ISCSI_BHS_OPCODE_ASYNC_MESSAGE 0x32
|
||||
#define ISCSI_BHS_OPCODE_REJECT 0x3f
|
||||
|
||||
struct iscsi_bhs {
|
||||
uint8_t bhs_opcode;
|
||||
uint8_t bhs_opcode_specific1[3];
|
||||
uint8_t bhs_total_ahs_len;
|
||||
uint8_t bhs_data_segment_len[3];
|
||||
uint64_t bhs_lun;
|
||||
uint8_t bhs_inititator_task_tag[4];
|
||||
uint8_t bhs_opcode_specific4[28];
|
||||
};
|
||||
CTASSERT(sizeof(struct iscsi_bhs) == ISCSI_BHS_SIZE);
|
||||
|
||||
#define BHSSC_FLAGS_F 0x80
|
||||
#define BHSSC_FLAGS_R 0x40
|
||||
#define BHSSC_FLAGS_W 0x20
|
||||
#define BHSSC_FLAGS_ATTR 0x07
|
||||
|
||||
#define BHSSC_FLAGS_ATTR_UNTAGGED 0
|
||||
#define BHSSC_FLAGS_ATTR_SIMPLE 1
|
||||
#define BHSSC_FLAGS_ATTR_ORDERED 2
|
||||
#define BHSSC_FLAGS_ATTR_HOQ 3
|
||||
#define BHSSC_FLAGS_ATTR_ACA 4
|
||||
|
||||
struct iscsi_bhs_scsi_command {
|
||||
uint8_t bhssc_opcode;
|
||||
uint8_t bhssc_flags;
|
||||
uint8_t bhssc_reserved[2];
|
||||
uint8_t bhssc_total_ahs_len;
|
||||
uint8_t bhssc_data_segment_len[3];
|
||||
uint64_t bhssc_lun;
|
||||
uint32_t bhssc_initiator_task_tag;
|
||||
uint32_t bhssc_expected_data_transfer_length;
|
||||
uint32_t bhssc_cmdsn;
|
||||
uint32_t bhssc_expstatsn;
|
||||
uint8_t bhssc_cdb[16];
|
||||
};
|
||||
CTASSERT(sizeof(struct iscsi_bhs_scsi_command) == ISCSI_BHS_SIZE);
|
||||
|
||||
#define BHSSR_FLAGS_RESIDUAL_UNDERFLOW 0x02
|
||||
#define BHSSR_FLAGS_RESIDUAL_OVERFLOW 0x04
|
||||
|
||||
#define BHSSR_RESPONSE_COMMAND_COMPLETED 0x00
|
||||
|
||||
struct iscsi_bhs_scsi_response {
|
||||
uint8_t bhssr_opcode;
|
||||
uint8_t bhssr_flags;
|
||||
uint8_t bhssr_response;
|
||||
uint8_t bhssr_status;
|
||||
uint8_t bhssr_total_ahs_len;
|
||||
uint8_t bhssr_data_segment_len[3];
|
||||
uint64_t bhssr_reserved;
|
||||
uint32_t bhssr_initiator_task_tag;
|
||||
uint32_t bhssr_snack_tag;
|
||||
uint32_t bhssr_statsn;
|
||||
uint32_t bhssr_expcmdsn;
|
||||
uint32_t bhssr_maxcmdsn;
|
||||
uint32_t bhssr_expdatasn;
|
||||
uint32_t bhssr_bidirectional_read_residual_count;
|
||||
uint32_t bhssr_residual_count;
|
||||
};
|
||||
CTASSERT(sizeof(struct iscsi_bhs_scsi_response) == ISCSI_BHS_SIZE);
|
||||
|
||||
#define BHSTMR_FUNCTION_ABORT_TASK 1
|
||||
#define BHSTMR_FUNCTION_ABORT_TASK_SET 2
|
||||
#define BHSTMR_FUNCTION_CLEAR_ACA 3
|
||||
#define BHSTMR_FUNCTION_CLEAR_TASK_SET 4
|
||||
#define BHSTMR_FUNCTION_LOGICAL_UNIT_RESET 5
|
||||
#define BHSTMR_FUNCTION_TARGET_WARM_RESET 6
|
||||
#define BHSTMR_FUNCTION_TARGET_COLD_RESET 7
|
||||
#define BHSTMR_FUNCTION_TASK_REASSIGN 8
|
||||
|
||||
struct iscsi_bhs_task_management_request {
|
||||
uint8_t bhstmr_opcode;
|
||||
uint8_t bhstmr_function;
|
||||
uint8_t bhstmr_reserved[2];
|
||||
uint8_t bhstmr_total_ahs_len;
|
||||
uint8_t bhstmr_data_segment_len[3];
|
||||
uint64_t bhstmr_lun;
|
||||
uint32_t bhstmr_initiator_task_tag;
|
||||
uint32_t bhstmr_referenced_task_tag;
|
||||
uint32_t bhstmr_cmdsn;
|
||||
uint32_t bhstmr_expstatsn;
|
||||
uint32_t bhstmr_refcmdsn;
|
||||
uint32_t bhstmr_expdatasn;
|
||||
uint64_t bhstmr_reserved2;
|
||||
};
|
||||
CTASSERT(sizeof(struct iscsi_bhs_task_management_request) == ISCSI_BHS_SIZE);
|
||||
|
||||
#define BHSTMR_RESPONSE_FUNCTION_COMPLETE 0
|
||||
#define BHSTMR_RESPONSE_FUNCTION_NOT_SUPPORTED 5
|
||||
|
||||
struct iscsi_bhs_task_management_response {
|
||||
uint8_t bhstmr_opcode;
|
||||
uint8_t bhstmr_flags;
|
||||
uint8_t bhstmr_response;
|
||||
uint8_t bhstmr_reserved;
|
||||
uint8_t bhstmr_total_ahs_len;
|
||||
uint8_t bhstmr_data_segment_len[3];
|
||||
uint64_t bhstmr_reserved2;
|
||||
uint32_t bhstmr_initiator_task_tag;
|
||||
uint32_t bhstmr_reserved3;
|
||||
uint32_t bhstmr_statsn;
|
||||
uint32_t bhstmr_expcmdsn;
|
||||
uint32_t bhstmr_maxcmdsn;
|
||||
uint8_t bhstmr_reserved4[12];
|
||||
};
|
||||
CTASSERT(sizeof(struct iscsi_bhs_task_management_response) == ISCSI_BHS_SIZE);
|
||||
|
||||
#define BHSLR_FLAGS_TRANSIT 0x80
|
||||
#define BHSLR_FLAGS_CONTINUE 0x40
|
||||
|
||||
#define BHSLR_STAGE_SECURITY_NEGOTIATION 0
|
||||
#define BHSLR_STAGE_OPERATIONAL_NEGOTIATION 1
|
||||
#define BHSLR_STAGE_FULL_FEATURE_PHASE 3 /* Yes, 3. */
|
||||
|
||||
struct iscsi_bhs_login_request {
|
||||
uint8_t bhslr_opcode;
|
||||
uint8_t bhslr_flags;
|
||||
uint8_t bhslr_version_max;
|
||||
uint8_t bhslr_version_min;
|
||||
uint8_t bhslr_total_ahs_len;
|
||||
uint8_t bhslr_data_segment_len[3];
|
||||
uint8_t bhslr_isid[6];
|
||||
uint16_t bhslr_tsih;
|
||||
uint32_t bhslr_initiator_task_tag;
|
||||
uint16_t bhslr_cid;
|
||||
uint16_t bhslr_reserved;
|
||||
uint32_t bhslr_cmdsn;
|
||||
uint32_t bhslr_expstatsn;
|
||||
uint8_t bhslr_reserved2[16];
|
||||
};
|
||||
CTASSERT(sizeof(struct iscsi_bhs_login_request) == ISCSI_BHS_SIZE);
|
||||
|
||||
struct iscsi_bhs_login_response {
|
||||
uint8_t bhslr_opcode;
|
||||
uint8_t bhslr_flags;
|
||||
uint8_t bhslr_version_max;
|
||||
uint8_t bhslr_version_active;
|
||||
uint8_t bhslr_total_ahs_len;
|
||||
uint8_t bhslr_data_segment_len[3];
|
||||
uint8_t bhslr_isid[6];
|
||||
uint16_t bhslr_tsih;
|
||||
uint32_t bhslr_initiator_task_tag;
|
||||
uint32_t bhslr_reserved;
|
||||
uint32_t bhslr_statsn;
|
||||
uint32_t bhslr_expcmdsn;
|
||||
uint32_t bhslr_maxcmdsn;
|
||||
uint8_t bhslr_status_class;
|
||||
uint8_t bhslr_status_detail;
|
||||
uint16_t bhslr_reserved2;
|
||||
uint8_t bhslr_reserved3[8];
|
||||
};
|
||||
CTASSERT(sizeof(struct iscsi_bhs_login_response) == ISCSI_BHS_SIZE);
|
||||
|
||||
#define BHSTR_FLAGS_FINAL 0x80
|
||||
#define BHSTR_FLAGS_CONTINUE 0x40
|
||||
|
||||
struct iscsi_bhs_text_request {
|
||||
uint8_t bhstr_opcode;
|
||||
uint8_t bhstr_flags;
|
||||
uint16_t bhstr_reserved;
|
||||
uint8_t bhstr_total_ahs_len;
|
||||
uint8_t bhstr_data_segment_len[3];
|
||||
uint64_t bhstr_lun;
|
||||
uint32_t bhstr_initiator_task_tag;
|
||||
uint32_t bhstr_target_transfer_tag;
|
||||
uint32_t bhstr_cmdsn;
|
||||
uint32_t bhstr_expstatsn;
|
||||
uint8_t bhstr_reserved2[16];
|
||||
};
|
||||
CTASSERT(sizeof(struct iscsi_bhs_text_request) == ISCSI_BHS_SIZE);
|
||||
|
||||
struct iscsi_bhs_text_response {
|
||||
uint8_t bhstr_opcode;
|
||||
uint8_t bhstr_flags;
|
||||
uint16_t bhstr_reserved;
|
||||
uint8_t bhstr_total_ahs_len;
|
||||
uint8_t bhstr_data_segment_len[3];
|
||||
uint64_t bhstr_lun;
|
||||
uint32_t bhstr_initiator_task_tag;
|
||||
uint32_t bhstr_target_transfer_tag;
|
||||
uint32_t bhstr_statsn;
|
||||
uint32_t bhstr_expcmdsn;
|
||||
uint32_t bhstr_maxcmdsn;
|
||||
uint8_t bhstr_reserved2[12];
|
||||
};
|
||||
CTASSERT(sizeof(struct iscsi_bhs_text_response) == ISCSI_BHS_SIZE);
|
||||
|
||||
#define BHSDO_FLAGS_F 0x80
|
||||
|
||||
struct iscsi_bhs_data_out {
|
||||
uint8_t bhsdo_opcode;
|
||||
uint8_t bhsdo_flags;
|
||||
uint8_t bhsdo_reserved[2];
|
||||
uint8_t bhsdo_total_ahs_len;
|
||||
uint8_t bhsdo_data_segment_len[3];
|
||||
uint64_t bhsdo_lun;
|
||||
uint32_t bhsdo_initiator_task_tag;
|
||||
uint32_t bhsdo_target_transfer_tag;
|
||||
uint32_t bhsdo_reserved2;
|
||||
uint32_t bhsdo_expstatsn;
|
||||
uint32_t bhsdo_reserved3;
|
||||
uint32_t bhsdo_datasn;
|
||||
uint32_t bhsdo_buffer_offset;
|
||||
uint32_t bhsdo_reserved4;
|
||||
};
|
||||
CTASSERT(sizeof(struct iscsi_bhs_data_out) == ISCSI_BHS_SIZE);
|
||||
|
||||
#define BHSDI_FLAGS_F 0x80
|
||||
#define BHSDI_FLAGS_A 0x40
|
||||
#define BHSDI_FLAGS_O 0x04
|
||||
#define BHSDI_FLAGS_U 0x02
|
||||
#define BHSDI_FLAGS_S 0x01
|
||||
|
||||
struct iscsi_bhs_data_in {
|
||||
uint8_t bhsdi_opcode;
|
||||
uint8_t bhsdi_flags;
|
||||
uint8_t bhsdi_reserved;
|
||||
uint8_t bhsdi_status;
|
||||
uint8_t bhsdi_total_ahs_len;
|
||||
uint8_t bhsdi_data_segment_len[3];
|
||||
uint64_t bhsdi_lun;
|
||||
uint32_t bhsdi_initiator_task_tag;
|
||||
uint32_t bhsdi_target_transfer_tag;
|
||||
uint32_t bhsdi_statsn;
|
||||
uint32_t bhsdi_expcmdsn;
|
||||
uint32_t bhsdi_maxcmdsn;
|
||||
uint32_t bhsdi_datasn;
|
||||
uint32_t bhsdi_buffer_offset;
|
||||
uint32_t bhsdi_residual_count;
|
||||
};
|
||||
CTASSERT(sizeof(struct iscsi_bhs_data_in) == ISCSI_BHS_SIZE);
|
||||
|
||||
struct iscsi_bhs_r2t {
|
||||
uint8_t bhsr2t_opcode;
|
||||
uint8_t bhsr2t_flags;
|
||||
uint16_t bhsr2t_reserved;
|
||||
uint8_t bhsr2t_total_ahs_len;
|
||||
uint8_t bhsr2t_data_segment_len[3];
|
||||
uint64_t bhsr2t_lun;
|
||||
uint32_t bhsr2t_initiator_task_tag;
|
||||
uint32_t bhsr2t_target_transfer_tag;
|
||||
uint32_t bhsr2t_statsn;
|
||||
uint32_t bhsr2t_expcmdsn;
|
||||
uint32_t bhsr2t_maxcmdsn;
|
||||
uint32_t bhsr2t_r2tsn;
|
||||
uint32_t bhsr2t_buffer_offset;
|
||||
uint32_t bhsr2t_desired_data_transfer_length;
|
||||
};
|
||||
CTASSERT(sizeof(struct iscsi_bhs_r2t) == ISCSI_BHS_SIZE);
|
||||
|
||||
struct iscsi_bhs_nop_out {
|
||||
uint8_t bhsno_opcode;
|
||||
uint8_t bhsno_flags;
|
||||
uint16_t bhsno_reserved;
|
||||
uint8_t bhsno_total_ahs_len;
|
||||
uint8_t bhsno_data_segment_len[3];
|
||||
uint64_t bhsno_lun;
|
||||
uint32_t bhsno_initiator_task_tag;
|
||||
uint32_t bhsno_target_transfer_tag;
|
||||
uint32_t bhsno_cmdsn;
|
||||
uint32_t bhsno_expstatsn;
|
||||
uint8_t bhsno_reserved2[16];
|
||||
};
|
||||
CTASSERT(sizeof(struct iscsi_bhs_nop_out) == ISCSI_BHS_SIZE);
|
||||
|
||||
struct iscsi_bhs_nop_in {
|
||||
uint8_t bhsni_opcode;
|
||||
uint8_t bhsni_flags;
|
||||
uint16_t bhsni_reserved;
|
||||
uint8_t bhsni_total_ahs_len;
|
||||
uint8_t bhsni_data_segment_len[3];
|
||||
uint64_t bhsni_lun;
|
||||
uint32_t bhsni_initiator_task_tag;
|
||||
uint32_t bhsni_target_transfer_tag;
|
||||
uint32_t bhsni_statsn;
|
||||
uint32_t bhsni_expcmdsn;
|
||||
uint32_t bhsni_maxcmdsn;
|
||||
uint8_t bhsno_reserved2[12];
|
||||
};
|
||||
CTASSERT(sizeof(struct iscsi_bhs_nop_in) == ISCSI_BHS_SIZE);
|
||||
|
||||
#define BHSLR_REASON_CLOSE_SESSION 0
|
||||
#define BHSLR_REASON_CLOSE_CONNECTION 1
|
||||
#define BHSLR_REASON_REMOVE_FOR_RECOVERY 2
|
||||
|
||||
struct iscsi_bhs_logout_request {
|
||||
uint8_t bhslr_opcode;
|
||||
uint8_t bhslr_reason;
|
||||
uint16_t bhslr_reserved;
|
||||
uint8_t bhslr_total_ahs_len;
|
||||
uint8_t bhslr_data_segment_len[3];
|
||||
uint64_t bhslr_reserved2;
|
||||
uint32_t bhslr_initiator_task_tag;
|
||||
uint16_t bhslr_cid;
|
||||
uint16_t bhslr_reserved3;
|
||||
uint32_t bhslr_cmdsn;
|
||||
uint32_t bhslr_expstatsn;
|
||||
uint8_t bhslr_reserved4[16];
|
||||
};
|
||||
CTASSERT(sizeof(struct iscsi_bhs_logout_request) == ISCSI_BHS_SIZE);
|
||||
|
||||
#define BHSLR_RESPONSE_CLOSED_SUCCESSFULLY 0
|
||||
#define BHSLR_RESPONSE_RECOVERY_NOT_SUPPORTED 2
|
||||
|
||||
struct iscsi_bhs_logout_response {
|
||||
uint8_t bhslr_opcode;
|
||||
uint8_t bhslr_flags;
|
||||
uint8_t bhslr_response;
|
||||
uint8_t bhslr_reserved;
|
||||
uint8_t bhslr_total_ahs_len;
|
||||
uint8_t bhslr_data_segment_len[3];
|
||||
uint64_t bhslr_reserved2;
|
||||
uint32_t bhslr_initiator_task_tag;
|
||||
uint32_t bhslr_reserved3;
|
||||
uint32_t bhslr_statsn;
|
||||
uint32_t bhslr_expcmdsn;
|
||||
uint32_t bhslr_maxcmdsn;
|
||||
uint32_t bhslr_reserved4;
|
||||
uint16_t bhslr_time2wait;
|
||||
uint16_t bhslr_time2retain;
|
||||
uint32_t bhslr_reserved5;
|
||||
};
|
||||
CTASSERT(sizeof(struct iscsi_bhs_logout_response) == ISCSI_BHS_SIZE);
|
||||
|
||||
#define BHSAM_EVENT_TARGET_REQUESTS_LOGOUT 1
|
||||
#define BHSAM_EVENT_TARGET_TERMINATES_CONNECTION 2
|
||||
#define BHSAM_EVENT_TARGET_TERMINATES_SESSION 3
|
||||
|
||||
struct iscsi_bhs_asynchronous_message {
|
||||
uint8_t bhsam_opcode;
|
||||
uint8_t bhsam_flags;
|
||||
uint16_t bhsam_reserved;
|
||||
uint8_t bhsam_total_ahs_len;
|
||||
uint8_t bhsam_data_segment_len[3];
|
||||
uint64_t bhsam_lun;
|
||||
uint32_t bhsam_0xffffffff;
|
||||
uint32_t bhsam_reserved2;
|
||||
uint32_t bhsam_statsn;
|
||||
uint32_t bhsam_expcmdsn;
|
||||
uint32_t bhsam_maxcmdsn;
|
||||
uint8_t bhsam_async_event;
|
||||
uint8_t bhsam_async_vcode;
|
||||
uint16_t bhsam_parameter1;
|
||||
uint16_t bhsam_parameter2;
|
||||
uint16_t bhsam_parameter3;
|
||||
uint32_t bhsam_reserved3;
|
||||
};
|
||||
CTASSERT(sizeof(struct iscsi_bhs_asynchronous_message) == ISCSI_BHS_SIZE);
|
||||
|
||||
#define BHSSR_REASON_DATA_DIGEST_ERROR 0x02
|
||||
#define BHSSR_PROTOCOL_ERROR 0x04
|
||||
#define BHSSR_COMMAND_NOT_SUPPORTED 0x05
|
||||
#define BHSSR_INVALID_PDU_FIELD 0x09
|
||||
|
||||
struct iscsi_bhs_reject {
|
||||
uint8_t bhsr_opcode;
|
||||
uint8_t bhsr_flags;
|
||||
uint8_t bhsr_reason;
|
||||
uint8_t bhsr_reserved;
|
||||
uint8_t bhsr_total_ahs_len;
|
||||
uint8_t bhsr_data_segment_len[3];
|
||||
uint64_t bhsr_reserved2;
|
||||
uint32_t bhsr_0xffffffff;
|
||||
uint32_t bhsr_reserved3;
|
||||
uint32_t bhsr_statsn;
|
||||
uint32_t bhsr_expcmdsn;
|
||||
uint32_t bhsr_maxcmdsn;
|
||||
uint32_t bhsr_datasn_r2tsn;
|
||||
uint32_t bhsr_reserved4;
|
||||
uint32_t bhsr_reserved5;
|
||||
};
|
||||
CTASSERT(sizeof(struct iscsi_bhs_reject) == ISCSI_BHS_SIZE);
|
||||
|
||||
#endif /* !ISCSI_PROTO_H */
|
@ -858,7 +858,7 @@ iscsi_modevent(module_t mod, int what, void *arg)
|
||||
}
|
||||
|
||||
moduledata_t iscsi_mod = {
|
||||
"iscsi",
|
||||
"iscsi_initiator",
|
||||
(modeventhand_t) iscsi_modevent,
|
||||
0
|
||||
};
|
||||
@ -878,5 +878,5 @@ iscsi_rootconf(void)
|
||||
SYSINIT(cpu_rootconf1, SI_SUB_ROOT_CONF, SI_ORDER_FIRST, iscsi_rootconf, NULL)
|
||||
#endif
|
||||
|
||||
DECLARE_MODULE(iscsi, iscsi_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
|
||||
MODULE_DEPEND(iscsi, cam, 1, 1, 1);
|
||||
DECLARE_MODULE(iscsi_initiator, iscsi_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
|
||||
MODULE_DEPEND(iscsi_initiator, cam, 1, 1, 1);
|
||||
|
@ -160,6 +160,7 @@ SUBDIR= \
|
||||
${_ipw} \
|
||||
${_ipwfw} \
|
||||
${_isci} \
|
||||
iscsi \
|
||||
iscsi_initiator \
|
||||
isp \
|
||||
${_ispfw} \
|
||||
|
@ -12,6 +12,7 @@ SRCS+= ctl_cmd_table.c
|
||||
SRCS+= ctl_frontend.c
|
||||
SRCS+= ctl_frontend_cam_sim.c
|
||||
SRCS+= ctl_frontend_internal.c
|
||||
SRCS+= ctl_frontend_iscsi.c
|
||||
SRCS+= ctl_mem_pool.c
|
||||
SRCS+= ctl_scsi_all.c
|
||||
SRCS+= ctl_error.c
|
||||
@ -23,4 +24,6 @@ SRCS+= vnode_if.h
|
||||
SRCS+= opt_cam.h
|
||||
SRCS+= opt_kdtrace.h
|
||||
|
||||
#CFLAGS+=-DICL_KERNEL_PROXY
|
||||
|
||||
.include <bsd.kmod.mk>
|
||||
|
@ -1,5 +1,25 @@
|
||||
# $FreeBSD$
|
||||
|
||||
SUBDIR= initiator
|
||||
.PATH: ${.CURDIR}/../../dev/iscsi/
|
||||
KMOD= iscsi
|
||||
|
||||
.include <bsd.subdir.mk>
|
||||
SRCS= iscsi.c
|
||||
.if defined(ICL_RDMA)
|
||||
SRCS+= icl_rdma.c
|
||||
.else
|
||||
SRCS+= icl.c
|
||||
.endif
|
||||
SRCS+= icl_proxy.c
|
||||
SRCS+= opt_cam.h
|
||||
SRCS+= bus_if.h
|
||||
SRCS+= device_if.h
|
||||
|
||||
# Those below are required for RDMA.
|
||||
SRCS+= vnode_if.h
|
||||
SRCS+= opt_inet.h
|
||||
SRCS+= opt_inet6.h
|
||||
|
||||
CFLAGS+= -I${.CURDIR}/../../ofed/include
|
||||
#CFLAGS+=-DICL_KERNEL_PROXY
|
||||
|
||||
.include <bsd.kmod.mk>
|
||||
|
35
tools/regression/iscsi/ctl.conf
Normal file
35
tools/regression/iscsi/ctl.conf
Normal file
@ -0,0 +1,35 @@
|
||||
auth-group meh {
|
||||
chap user secretsecret
|
||||
}
|
||||
|
||||
portal-group meh {
|
||||
listen 0.0.0.0
|
||||
discovery-auth-group no-authentication
|
||||
}
|
||||
|
||||
target iqn.2012-06.com.example:1 {
|
||||
auth-group no-authentication
|
||||
portal-group meh
|
||||
lun 0 {
|
||||
path /var/tmp/example_t1l0
|
||||
size 4G
|
||||
}
|
||||
lun 1 {
|
||||
path /var/tmp/example_t1l1
|
||||
size 4G
|
||||
}
|
||||
}
|
||||
|
||||
target iqn.2012-06.com.example:2 {
|
||||
auth-group meh
|
||||
portal-group meh
|
||||
lun 0 {
|
||||
path /var/tmp/example_t2l0
|
||||
size 4G
|
||||
}
|
||||
lun 1 {
|
||||
path /var/tmp/example_t2l1
|
||||
size 4G
|
||||
}
|
||||
}
|
||||
|
59
tools/regression/iscsi/initiator-instructions.txt
Normal file
59
tools/regression/iscsi/initiator-instructions.txt
Normal file
@ -0,0 +1,59 @@
|
||||
How to prepare initiator virtual machines for iSCSI target testing
|
||||
------------------------------------------------------------------
|
||||
|
||||
1. Install operating systems.
|
||||
|
||||
- FreeBSD: Use default settings for everything. Don't install
|
||||
ports from the system installer, use "portsnap fetch extract"
|
||||
after installation instead.
|
||||
|
||||
- Fedora: Change the environment to "Minimal install".
|
||||
|
||||
- Solaris: Use defaults.
|
||||
|
||||
2. Install required software.
|
||||
|
||||
- FreeBSD: install from ports, with 'make install BATCH=1':
|
||||
benchmarks/bonnie++
|
||||
benchmarks/iozone
|
||||
benchmarks/postmark
|
||||
databases/postgresql92-server
|
||||
databases/postgresql92-contrib
|
||||
|
||||
- Fedora:
|
||||
yum install btrfs-progs bonnie++ postgresql-server postgresql-contrib iscsi-initiator-utils
|
||||
chkconfig iscsid on
|
||||
chkconfig iscsi on
|
||||
|
||||
After that, install iozone and postmark from source; they are not
|
||||
provided by Fedora; download sites:
|
||||
|
||||
http://www.iozone.org/src/current/iozone3_397.tar
|
||||
http://www.gtlib.gatech.edu/pub/debian/pool/main/p/postmark/postmark_1.53.orig.tar.gz
|
||||
|
||||
To build iozone, use "make linux". Copy the 'postmark' and 'iozone' binaries
|
||||
to /usr/local/bin/.
|
||||
|
||||
- Solaris:
|
||||
Install gcc:
|
||||
|
||||
pkg install gcc-45
|
||||
pkg install system/header
|
||||
|
||||
After that, install bonnie++, iozone, and postmark from source; download sites:
|
||||
|
||||
http://www.coker.com.au/bonnie++/experimental/bonnie++-1.97.tgz
|
||||
http://www.iozone.org/src/current/iozone3_397.tar
|
||||
http://www.gtlib.gatech.edu/pub/debian/pool/main/p/postmark/postmark_1.53.orig.tar.gz
|
||||
|
||||
To build iozone, use "make Solaris10gcc". Copy the 'bonnie++', 'postmark', and 'iozone'
|
||||
binaries to /usr/bin/.
|
||||
|
||||
Fetch the binary PostgreSQL distribution from the link below and untar to /usr/postgres/:
|
||||
|
||||
http://ftp.postgresql.org/pub/binary/v9.2.3/solaris/solaris11/i386/postgresql-9.2.3-S11.i386-32.tar.bz2
|
||||
|
||||
3. Run the test script.
|
||||
|
||||
./iscsi-test.sh
|
||||
|
794
tools/regression/iscsi/iscsi-test.sh
Normal file
794
tools/regression/iscsi/iscsi-test.sh
Normal file
@ -0,0 +1,794 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2012 The FreeBSD Foundation
|
||||
# All rights reserved.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# $FreeBSD$
|
||||
#
|
||||
|
||||
#
|
||||
# This expects that the iSCSI server being tested is at $TARGETIP and exports
|
||||
# two targets: $TARGET1 and $TARGET2; the former requiring no authentication,
|
||||
# and the latter using CHAP with user $USER and secret $SECRET. Discovery
|
||||
# must be permitted without authentication. Each target must contain exactly
|
||||
# two LUNs, 4GB each. For example, ctl.conf(5) should look like this:
|
||||
#
|
||||
# auth-group meh {
|
||||
# chap user secretsecret
|
||||
# }
|
||||
#
|
||||
# portal-group meh {
|
||||
# listen 0.0.0.0
|
||||
# discovery-auth-group no-authentication
|
||||
# }
|
||||
#
|
||||
# target iqn.2012-06.com.example:1 {
|
||||
# auth-group no-authentication
|
||||
# portal-group meh
|
||||
# lun 0 {
|
||||
# path /var/tmp/example_t1l0
|
||||
# size 4G
|
||||
# }
|
||||
# lun 1 {
|
||||
# path /var/tmp/example_t1l1
|
||||
# size 4G
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# target iqn.2012-06.com.example:2 {
|
||||
# auth-group meh
|
||||
# portal-group meh
|
||||
# lun 0 {
|
||||
# path /var/tmp/example_t2l0
|
||||
# size 4G
|
||||
# }
|
||||
# lun 1 {
|
||||
# path /var/tmp/example_t2l1
|
||||
# size 4G
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# Remember to create the backing files (/var/tmp/example_t1l0 etcc)
|
||||
#
|
||||
# On the initiator, $MNTDIR will be used for testing.
|
||||
#
|
||||
|
||||
TARGETIP=192.168.56.101
|
||||
TARGET1=iqn.2012-06.com.example:1
|
||||
TARGET2=iqn.2012-06.com.example:2
|
||||
USER=user
|
||||
SECRET=secretsecret
|
||||
MNTDIR=/mnt
|
||||
TMPDIR=/tmp
|
||||
|
||||
die() {
|
||||
echo "$*"
|
||||
exit 1
|
||||
}
|
||||
|
||||
case `uname` in
|
||||
FreeBSD)
|
||||
LUN0=/dev/da0
|
||||
LUN1=/dev/da1
|
||||
LUN2=/dev/da2
|
||||
LUN3=/dev/da3
|
||||
ZFSPOOL=iscsipool
|
||||
;;
|
||||
Linux)
|
||||
LUN0=/dev/sdb
|
||||
LUN1=/dev/sdc
|
||||
LUN2=/dev/sdd
|
||||
LUN3=/dev/sde
|
||||
;;
|
||||
SunOS)
|
||||
# LUN names are being set later, during attach.
|
||||
ZFSPOOL=iscsipool
|
||||
;;
|
||||
*)
|
||||
die "unsupported system"
|
||||
;;
|
||||
esac
|
||||
|
||||
check() {
|
||||
echo "# $@" > /dev/stderr
|
||||
$@ || die "$@ failed"
|
||||
}
|
||||
|
||||
banner() {
|
||||
echo "Will try to attach to $TARGET1 and $TARGET2 on $TARGETIP,"
|
||||
echo "user $USER, secret $SECRET. Will use mountpoint $MNTDIR, temporary dir $TMPDIR,"
|
||||
if [ -n "$LUN0" ]; then
|
||||
echo "scratch disks $LUN0, $LUN1, $LUN2, $LUN3."
|
||||
else
|
||||
echo "scratch disks unknown at this stage."
|
||||
fi
|
||||
echo
|
||||
echo "This script is NOT safe to run on multiuser system."
|
||||
echo
|
||||
echo "Press ^C to interrupt; will proceed in 5 seconds."
|
||||
sleep 5
|
||||
}
|
||||
|
||||
test_discovery_freebsd_9() {
|
||||
kldload iscsi_initiator
|
||||
check iscontrol -dt $TARGETIP > $TMPDIR/discovered
|
||||
cat $TMPDIR/discovered
|
||||
echo "TargetName=$TARGET1" > $TMPDIR/expected
|
||||
echo "TargetName=$TARGET2" >> $TMPDIR/expected
|
||||
check cmp $TMPDIR/expected $TMPDIR/discovered
|
||||
rm -f $TMPDIR/expected $TMPDIR/discovered
|
||||
}
|
||||
|
||||
test_discovery_freebsd() {
|
||||
/etc/rc.d/iscsid onestart
|
||||
check iscsictl -Ad $TARGETIP
|
||||
sleep 1
|
||||
iscsictl | awk '{ print $1 }' | sort > $TMPDIR/discovered
|
||||
printf "Target\n$TARGET1\n$TARGET2\n" | sort > $TMPDIR/expected
|
||||
check cmp $TMPDIR/expected $TMPDIR/discovered
|
||||
rm -f $TMPDIR/expected $TMPDIR/discovered
|
||||
check iscsictl -Ra
|
||||
sleep 1
|
||||
}
|
||||
|
||||
test_discovery_linux() {
|
||||
cat > /etc/iscsi/iscsid.conf << END
|
||||
|
||||
discovery.sendtargets.auth.authmethod = None
|
||||
node.startup = manual
|
||||
|
||||
END
|
||||
|
||||
check iscsiadm -m discovery -t sendtargets -p $TARGETIP > $TMPDIR/discovered
|
||||
cat $TMPDIR/discovered
|
||||
echo "$TARGETIP:3260,-1 $TARGET1" > $TMPDIR/expected
|
||||
echo "$TARGETIP:3260,-1 $TARGET2" >> $TMPDIR/expected
|
||||
check cmp $TMPDIR/expected $TMPDIR/discovered
|
||||
rm -f $TMPDIR/expected $TMPDIR/discovered
|
||||
|
||||
}
|
||||
|
||||
test_discovery_solaris() {
|
||||
check iscsiadm add discovery-address $TARGETIP
|
||||
check iscsiadm modify discovery --sendtargets enable
|
||||
check iscsiadm modify discovery --static enable
|
||||
check iscsiadm list target | awk '/^Target/ { print $2 }' | sort > $TMPDIR/discovered
|
||||
check iscsiadm remove discovery-address $TARGETIP
|
||||
cat $TMPDIR/discovered
|
||||
echo "$TARGET1" > $TMPDIR/expected
|
||||
echo "$TARGET2" >> $TMPDIR/expected
|
||||
check cmp $TMPDIR/expected $TMPDIR/discovered
|
||||
rm -f $TMPDIR/expected $TMPDIR/discovered
|
||||
}
|
||||
|
||||
test_discovery() {
|
||||
echo "*** discovery test ***"
|
||||
case `uname` in
|
||||
FreeBSD)
|
||||
case `uname -r` in
|
||||
9*)
|
||||
test_discovery_freebsd_9
|
||||
;;
|
||||
*)
|
||||
test_discovery_freebsd
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
Linux)
|
||||
test_discovery_linux
|
||||
;;
|
||||
SunOS)
|
||||
test_discovery_solaris
|
||||
;;
|
||||
*)
|
||||
die "unsupported system"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
test_attach_freebsd_9() {
|
||||
[ ! -e LUN0 ] || die "$LUN0 already exists"
|
||||
[ ! -e LUN1 ] || die "$LUN1 already exists"
|
||||
[ ! -e LUN2 ] || die "$LUN2 already exists"
|
||||
[ ! -e LUN3 ] || die "$LUN3 already exists"
|
||||
|
||||
cat > $TMPDIR/iscsi.conf << END
|
||||
|
||||
target1 {
|
||||
TargetName = $TARGET1
|
||||
TargetAddress = $TARGETIP
|
||||
}
|
||||
|
||||
target2 {
|
||||
TargetName = $TARGET2
|
||||
TargetAddress = $TARGETIP
|
||||
AuthMethod = CHAP
|
||||
chapIName = $USER
|
||||
chapSecret = $SECRET
|
||||
}
|
||||
|
||||
END
|
||||
check iscontrol -c $TMPDIR/iscsi.conf -n target1
|
||||
check iscontrol -c $TMPDIR/iscsi.conf -n target2
|
||||
|
||||
echo "Waiting 10 seconds for attach to complete."
|
||||
sleep 10
|
||||
|
||||
[ -e $LUN0 ] || die "$LUN0 doesn't exist"
|
||||
[ -e $LUN1 ] || die "$LUN1 doesn't exist"
|
||||
[ -e $LUN2 ] || die "$LUN2 doesn't exist"
|
||||
[ -e $LUN3 ] || die "$LUN3 doesn't exist"
|
||||
|
||||
rm $TMPDIR/iscsi.conf
|
||||
}
|
||||
|
||||
test_attach_freebsd() {
|
||||
[ ! -e LUN0 ] || die "$LUN0 already exists"
|
||||
[ ! -e LUN1 ] || die "$LUN1 already exists"
|
||||
[ ! -e LUN2 ] || die "$LUN2 already exists"
|
||||
[ ! -e LUN3 ] || die "$LUN3 already exists"
|
||||
|
||||
cat > $TMPDIR/iscsi.conf << END
|
||||
|
||||
target1 {
|
||||
TargetName = $TARGET1
|
||||
TargetAddress = $TARGETIP
|
||||
}
|
||||
|
||||
target2 {
|
||||
TargetName = $TARGET2
|
||||
TargetAddress = $TARGETIP
|
||||
AuthMethod = CHAP
|
||||
chapIName = $USER
|
||||
chapSecret = $SECRET
|
||||
}
|
||||
|
||||
END
|
||||
check iscsictl -Ac $TMPDIR/iscsi.conf -n target1
|
||||
check iscsictl -Ac $TMPDIR/iscsi.conf -n target2
|
||||
|
||||
echo "Waiting 10 seconds for attach to complete."
|
||||
sleep 10
|
||||
|
||||
[ -e $LUN0 ] || die "$LUN0 doesn't exist"
|
||||
[ -e $LUN1 ] || die "$LUN1 doesn't exist"
|
||||
[ -e $LUN2 ] || die "$LUN2 doesn't exist"
|
||||
[ -e $LUN3 ] || die "$LUN3 doesn't exist"
|
||||
|
||||
rm $TMPDIR/iscsi.conf
|
||||
}
|
||||
|
||||
test_attach_linux() {
|
||||
check iscsiadm --mode node --targetname "$TARGET1" -p "$TARGETIP:3260" --op=update --name node.session.auth.authmethod --value=None
|
||||
check iscsiadm --mode node --targetname "$TARGET1" -p "$TARGETIP:3260" --login
|
||||
check iscsiadm --mode node --targetname "$TARGET2" -p "$TARGETIP:3260" --op=update --name node.session.auth.authmethod --value=CHAP
|
||||
check iscsiadm --mode node --targetname "$TARGET2" -p "$TARGETIP:3260" --op=update --name node.session.auth.username --value="$USER"
|
||||
check iscsiadm --mode node --targetname "$TARGET2" -p "$TARGETIP:3260" --op=update --name node.session.auth.password --value="$SECRET"
|
||||
check iscsiadm --mode node --targetname "$TARGET2" -p "$TARGETIP:3260" --login
|
||||
}
|
||||
|
||||
test_attach_solaris() {
|
||||
# XXX: How to enter the CHAP secret from the script? For now,
|
||||
# just use the first target, and thus first two LUNs.
|
||||
#check iscsiadm modify initiator-node --authentication CHAP
|
||||
#check iscsiadm modify initiator-node --CHAP-name $USER
|
||||
#check iscsiadm modify initiator-node --CHAP-secret $SECRET
|
||||
iscsiadm add static-config $TARGET1,$TARGETIP
|
||||
LUN0=`iscsiadm list target -S | awk '/OS Device Name/ { print $4 }' | sed -n 1p`
|
||||
LUN1=`iscsiadm list target -S | awk '/OS Device Name/ { print $4 }' | sed -n 2p`
|
||||
LUN0=`echo ${LUN0}2 | sed 's/rdsk/dsk/'`
|
||||
LUN1=`echo ${LUN1}2 | sed 's/rdsk/dsk/'`
|
||||
[ -n "$LUN0" -a -n "LUN1" ] || die "attach failed"
|
||||
echo "Scratch disks: $LUN0, $LUN1"
|
||||
}
|
||||
|
||||
test_attach() {
|
||||
echo "*** attach test ***"
|
||||
case `uname` in
|
||||
FreeBSD)
|
||||
case `uname -r` in
|
||||
9*)
|
||||
test_attach_freebsd_9
|
||||
;;
|
||||
*)
|
||||
test_attach_freebsd
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
Linux)
|
||||
test_attach_linux
|
||||
;;
|
||||
SunOS)
|
||||
test_attach_solaris
|
||||
;;
|
||||
*)
|
||||
die "unsupported system"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
test_newfs_freebsd_ufs() {
|
||||
echo "*** UFS filesystem smoke test ***"
|
||||
check newfs $LUN0
|
||||
check newfs $LUN1
|
||||
check newfs $LUN2
|
||||
check newfs $LUN3
|
||||
check fsck -t ufs $LUN0
|
||||
check fsck -t ufs $LUN1
|
||||
check fsck -t ufs $LUN2
|
||||
check fsck -t ufs $LUN3
|
||||
}
|
||||
|
||||
test_newfs_freebsd_zfs() {
|
||||
echo "*** ZFS filesystem smoke test ***"
|
||||
check zpool create -f $ZFSPOOL $LUN0 $LUN1 $LUN2 $LUN3
|
||||
check zpool destroy -f $ZFSPOOL
|
||||
}
|
||||
|
||||
test_newfs_linux_ext4() {
|
||||
echo "*** ext4 filesystem smoke test ***"
|
||||
yes | check mkfs $LUN0
|
||||
yes | check mkfs $LUN1
|
||||
yes | check mkfs $LUN2
|
||||
yes | check mkfs $LUN3
|
||||
check fsck -f $LUN0
|
||||
check fsck -f $LUN1
|
||||
check fsck -f $LUN2
|
||||
check fsck -f $LUN3
|
||||
}
|
||||
|
||||
test_newfs_linux_btrfs() {
|
||||
echo "*** btrfs filesystem smoke test ***"
|
||||
check mkfs.btrfs $LUN0 $LUN1 $LUN2 $LUN3
|
||||
}
|
||||
|
||||
|
||||
test_newfs_solaris_ufs() {
|
||||
echo "*** UFS filesystem smoke test ***"
|
||||
yes | check newfs $LUN0
|
||||
yes | check newfs $LUN1
|
||||
check fsck -F ufs $LUN0
|
||||
check fsck -F ufs $LUN1
|
||||
}
|
||||
|
||||
test_newfs_solaris_zfs() {
|
||||
echo "*** ZFS filesystem smoke test ***"
|
||||
check zpool create -f $ZFSPOOL $LUN0 $LUN1
|
||||
check zpool destroy -f $ZFSPOOL
|
||||
}
|
||||
|
||||
test_newfs() {
|
||||
case `uname` in
|
||||
FreeBSD)
|
||||
test_newfs_freebsd_ufs
|
||||
test_newfs_freebsd_zfs
|
||||
;;
|
||||
Linux)
|
||||
test_newfs_linux_ext4
|
||||
test_newfs_linux_btrfs
|
||||
;;
|
||||
SunOS)
|
||||
test_newfs_solaris_ufs
|
||||
test_newfs_solaris_zfs
|
||||
;;
|
||||
*)
|
||||
die "unsupported system"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
test_cp() {
|
||||
echo "*** basic filesystem test ***"
|
||||
case `uname` in
|
||||
FreeBSD)
|
||||
check newfs $LUN0
|
||||
check mount -t ufs $LUN0 $MNTDIR
|
||||
check dd if=/dev/urandom of=$MNTDIR/1 bs=1m count=500
|
||||
check cp $MNTDIR/1 $MNTDIR/2
|
||||
check umount $MNTDIR
|
||||
check fsck -t ufs $LUN0
|
||||
check mount -t ufs $LUN0 $MNTDIR
|
||||
check cmp $MNTDIR/1 $MNTDIR/2
|
||||
check umount $MNTDIR
|
||||
|
||||
check zpool create -f $ZFSPOOL $LUN0 $LUN1 $LUN2 $LUN3
|
||||
check dd if=/dev/urandom of=/$ZFSPOOL/1 bs=1m count=500
|
||||
check zpool scrub $ZFSPOOL
|
||||
check cp /$ZFSPOOL/1 /$ZFSPOOL/2
|
||||
check cmp /$ZFSPOOL/1 /$ZFSPOOL/2
|
||||
check zpool destroy -f $ZFSPOOL
|
||||
;;
|
||||
Linux)
|
||||
yes | check mkfs $LUN0
|
||||
check mount $LUN0 $MNTDIR
|
||||
check dd if=/dev/urandom of=$MNTDIR/1 bs=1M count=500
|
||||
check cp $MNTDIR/1 $MNTDIR/2
|
||||
check umount $MNTDIR
|
||||
check fsck -f $LUN0
|
||||
check mount $LUN0 $MNTDIR
|
||||
check cmp $MNTDIR/1 $MNTDIR/2
|
||||
check umount $MNTDIR
|
||||
|
||||
check mkfs.btrfs $LUN0 $LUN1 $LUN2 $LUN3
|
||||
check mount $LUN0 $MNTDIR
|
||||
check dd if=/dev/urandom of=$MNTDIR/1 bs=1M count=500
|
||||
check cp $MNTDIR/1 $MNTDIR/2
|
||||
check umount $MNTDIR
|
||||
check mount $LUN0 $MNTDIR
|
||||
check cmp $MNTDIR/1 $MNTDIR/2
|
||||
check umount $MNTDIR
|
||||
;;
|
||||
SunOS)
|
||||
yes | check newfs $LUN0
|
||||
check mount -F ufs $LUN0 $MNTDIR
|
||||
check dd if=/dev/urandom of=$MNTDIR/1 bs=1024k count=500
|
||||
check cp $MNTDIR/1 $MNTDIR/2
|
||||
check umount $MNTDIR
|
||||
check fsck -yF ufs $LUN0
|
||||
check mount -F ufs $LUN0 $MNTDIR
|
||||
check cmp $MNTDIR/1 $MNTDIR/2
|
||||
check umount $MNTDIR
|
||||
|
||||
check zpool create -f $ZFSPOOL $LUN0 $LUN1
|
||||
check dd if=/dev/urandom of=/$ZFSPOOL/1 bs=1024k count=500
|
||||
check zpool scrub $ZFSPOOL
|
||||
check cp /$ZFSPOOL/1 /$ZFSPOOL/2
|
||||
check cmp /$ZFSPOOL/1 /$ZFSPOOL/2
|
||||
check zpool destroy -f $ZFSPOOL
|
||||
;;
|
||||
*)
|
||||
die "unsupported system"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
test_bonnie() {
|
||||
echo "*** bonnie++ ***"
|
||||
case `uname` in
|
||||
FreeBSD)
|
||||
check newfs $LUN0
|
||||
check mount -t ufs $LUN0 $MNTDIR
|
||||
check cd $MNTDIR
|
||||
check bonnie++ -u root -s 2g -r 1g -n0
|
||||
check bonnie++ -u root -s 0
|
||||
check cd -
|
||||
check umount $MNTDIR
|
||||
check fsck -t ufs $LUN0
|
||||
|
||||
check zpool create -f $ZFSPOOL $LUN0 $LUN1 $LUN2 $LUN3
|
||||
check cd /$ZFSPOOL
|
||||
check bonnie++ -u root -s 2g -r 1g -n0
|
||||
check bonnie++ -u root -s 0
|
||||
check cd -
|
||||
check zpool destroy -f $ZFSPOOL
|
||||
;;
|
||||
Linux)
|
||||
yes | check mkfs $LUN0
|
||||
check mount $LUN0 $MNTDIR
|
||||
check cd $MNTDIR
|
||||
check bonnie++ -u root -s 2g -r 1g -n0
|
||||
check bonnie++ -u root -s 0
|
||||
check cd -
|
||||
check umount $MNTDIR
|
||||
check fsck -f $LUN0
|
||||
|
||||
check mkfs.btrfs $LUN0 $LUN1 $LUN2 $LUN3
|
||||
check mount $LUN0 $MNTDIR
|
||||
check cd $MNTDIR
|
||||
check bonnie++ -u root -s 2g -r 1g -n0
|
||||
check bonnie++ -u root -s 0
|
||||
check cd -
|
||||
check umount $MNTDIR
|
||||
;;
|
||||
SunOS)
|
||||
yes | check newfs $LUN0
|
||||
check mount -F ufs $LUN0 $MNTDIR
|
||||
check cd $MNTDIR
|
||||
check bonnie++ -u root -s 2g -r 1g -n0
|
||||
check bonnie++ -u root -s 0
|
||||
check cd -
|
||||
check umount $MNTDIR
|
||||
check fsck -yF ufs $LUN0
|
||||
|
||||
check zpool create -f $ZFSPOOL $LUN0 $LUN1
|
||||
check cd /$ZFSPOOL
|
||||
check bonnie++ -u root -s 2g -r 1g -n0
|
||||
check bonnie++ -u root -s 0
|
||||
check cd -
|
||||
check zpool destroy -f $ZFSPOOL
|
||||
;;
|
||||
*)
|
||||
die "unsupported system"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
test_iozone() {
|
||||
echo "*** iozone ***"
|
||||
case `uname` in
|
||||
FreeBSD)
|
||||
check newfs $LUN0
|
||||
check mount -t ufs $LUN0 $MNTDIR
|
||||
check cd $MNTDIR
|
||||
check iozone -a
|
||||
check cd -
|
||||
check umount $MNTDIR
|
||||
check fsck -t ufs $LUN0
|
||||
|
||||
check zpool create -f $ZFSPOOL $LUN0 $LUN1 $LUN2 $LUN3
|
||||
check cd /$ZFSPOOL
|
||||
check iozone -a
|
||||
check cd -
|
||||
check zpool destroy -f $ZFSPOOL
|
||||
;;
|
||||
Linux)
|
||||
yes | check mkfs $LUN0
|
||||
check mount $LUN0 $MNTDIR
|
||||
check cd $MNTDIR
|
||||
check iozone -a
|
||||
check cd -
|
||||
check umount $MNTDIR
|
||||
check fsck -f $LUN0
|
||||
|
||||
check mkfs.btrfs $LUN0 $LUN1 $LUN2 $LUN3
|
||||
check mount $LUN0 $MNTDIR
|
||||
check cd $MNTDIR
|
||||
check iozone -a
|
||||
check cd -
|
||||
check umount $MNTDIR
|
||||
;;
|
||||
SunOS)
|
||||
yes | check newfs $LUN0
|
||||
check mount -F ufs $LUN0 $MNTDIR
|
||||
check cd $MNTDIR
|
||||
check iozone -a
|
||||
check cd -
|
||||
check umount $MNTDIR
|
||||
check fsck -yF ufs $LUN0
|
||||
|
||||
check zpool create -f $ZFSPOOL $LUN0 $LUN1
|
||||
check cd /$ZFSPOOL
|
||||
check iozone -a
|
||||
check cd -
|
||||
check zpool destroy -f $ZFSPOOL
|
||||
;;
|
||||
*)
|
||||
die "unsupported system"
|
||||
;;
|
||||
esac
|
||||
|
||||
}
|
||||
|
||||
test_postmark() {
|
||||
echo "*** postmark ***"
|
||||
case `uname` in
|
||||
FreeBSD)
|
||||
check newfs $LUN0
|
||||
check mount -t ufs $LUN0 $MNTDIR
|
||||
check cd $MNTDIR
|
||||
printf "set number 10000\nrun\n" | check postmark
|
||||
check cd -
|
||||
check umount $MNTDIR
|
||||
check fsck -t ufs $LUN0
|
||||
|
||||
check zpool create -f $ZFSPOOL $LUN0 $LUN1 $LUN2 $LUN3
|
||||
check cd /$ZFSPOOL
|
||||
printf "set number 10000\nrun\n" | check postmark
|
||||
check cd -
|
||||
check zpool destroy -f $ZFSPOOL
|
||||
;;
|
||||
Linux)
|
||||
yes | check mkfs $LUN0
|
||||
check mount $LUN0 $MNTDIR
|
||||
check cd $MNTDIR
|
||||
printf "set number 10000\nrun\n" | check postmark
|
||||
check cd -
|
||||
check umount $MNTDIR
|
||||
check fsck -f $LUN0
|
||||
|
||||
check mkfs.btrfs $LUN0 $LUN1 $LUN2 $LUN3
|
||||
check mount $LUN0 $MNTDIR
|
||||
check cd $MNTDIR
|
||||
printf "set number 10000\nrun\n" | check postmark
|
||||
check cd -
|
||||
check umount $MNTDIR
|
||||
;;
|
||||
SunOS)
|
||||
yes | check newfs $LUN0
|
||||
check mount -F ufs $LUN0 $MNTDIR
|
||||
check cd $MNTDIR
|
||||
printf "set number 10000\nrun\n" | check postmark
|
||||
check cd -
|
||||
check umount $MNTDIR
|
||||
check fsck -yF ufs $LUN0
|
||||
|
||||
check zpool create -f $ZFSPOOL $LUN0 $LUN1
|
||||
check cd /$ZFSPOOL
|
||||
printf "set number 10000\nrun\n" | check postmark
|
||||
check cd -
|
||||
check zpool destroy -f $ZFSPOOL
|
||||
;;
|
||||
*)
|
||||
die "unsupported system"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
test_postgresql_freebsd() {
|
||||
check newfs $LUN0
|
||||
check mount -t ufs $LUN0 $MNTDIR
|
||||
check chown pgsql $MNTDIR
|
||||
check chmod 755 $MNTDIR
|
||||
check cd /
|
||||
# XXX: How to make 'check' work here?
|
||||
su -m pgsql -c "initdb -D $MNTDIR/db"
|
||||
su -m pgsql -c "pg_ctl -D $MNTDIR/db -l /tmp/log start"
|
||||
check sleep 10
|
||||
su -m pgsql -c "pgbench -i postgres"
|
||||
su -m pgsql -c "pgbench -t 10000 postgres"
|
||||
su -m pgsql -c "pg_ctl -D $MNTDIR/db stop"
|
||||
check cd -
|
||||
check umount $MNTDIR
|
||||
check fsck -t ufs $LUN0
|
||||
|
||||
check zpool create -f $ZFSPOOL $LUN0 $LUN1 $LUN2 $LUN3
|
||||
check chown pgsql /$ZFSPOOL
|
||||
check chmod 755 /$ZFSPOOL
|
||||
check cd /
|
||||
# XXX: How to make 'check' work here?
|
||||
su -m pgsql -c "initdb -D /$ZFSPOOL/db"
|
||||
su -m pgsql -c "pg_ctl -D /$ZFSPOOL/db -l /tmp/log start"
|
||||
check sleep 10
|
||||
su -m pgsql -c "pgbench -i postgres"
|
||||
su -m pgsql -c "pgbench -t 10000 postgres"
|
||||
su -m pgsql -c "pg_ctl -D /$ZFSPOOL/db stop"
|
||||
check cd -
|
||||
check zpool destroy -f $ZFSPOOL
|
||||
}
|
||||
|
||||
test_postgresql_linux() {
|
||||
yes | check mkfs $LUN0
|
||||
check mount $LUN0 $MNTDIR
|
||||
check chown postgres $MNTDIR
|
||||
check chmod 755 $MNTDIR
|
||||
check cd /
|
||||
# XXX: How to make 'check' work here?
|
||||
su -m postgres -c "initdb -D $MNTDIR/db"
|
||||
su -m postgres -c "pg_ctl -D $MNTDIR/db -l /tmp/log start"
|
||||
check sleep 5
|
||||
su -m postgres -c "pgbench -i"
|
||||
su -m postgres -c "pgbench -t 10000"
|
||||
su -m postgres -c "pg_ctl -D $MNTDIR/db stop"
|
||||
check cd -
|
||||
check umount $MNTDIR
|
||||
check fsck -f $LUN0
|
||||
|
||||
check mkfs.btrfs $LUN0 $LUN1 $LUN2 $LUN3
|
||||
check mount $LUN0 $MNTDIR
|
||||
check chown postgres $MNTDIR
|
||||
check chmod 755 $MNTDIR
|
||||
check cd /
|
||||
su -m postgres -c "initdb -D $MNTDIR/db"
|
||||
su -m postgres -c "pg_ctl -D $MNTDIR/db -l /tmp/log start"
|
||||
check sleep 5
|
||||
su -m postgres -c "pgbench -i"
|
||||
su -m postgres -c "pgbench -t 10000"
|
||||
su -m postgres -c "pg_ctl -D $MNTDIR/db stop"
|
||||
check cd -
|
||||
check umount $MNTDIR
|
||||
}
|
||||
|
||||
test_postgresql_solaris() {
|
||||
PATH="$PATH:/usr/postgres/9.2-pgdg/bin" export PATH
|
||||
yes | check newfs $LUN0
|
||||
check mount -F ufs $LUN0 $MNTDIR
|
||||
check chown postgres $MNTDIR
|
||||
check chmod 755 $MNTDIR
|
||||
check cd /
|
||||
# XXX: How to make 'check' work here?
|
||||
su postgres -c "initdb -D $MNTDIR/db"
|
||||
su postgres -c "pg_ctl -D $MNTDIR/db -l /tmp/log start"
|
||||
check sleep 10
|
||||
su postgres -c "pgbench -i postgres"
|
||||
su postgres -c "pgbench -t 10000 postgres"
|
||||
su postgres -c "pg_ctl -D $MNTDIR/db stop"
|
||||
check cd -
|
||||
check umount $MNTDIR
|
||||
check fsck -yF ufs $LUN0
|
||||
|
||||
check zpool create -f $ZFSPOOL $LUN0 $LUN1 $LUN2 $LUN3
|
||||
check chown postgres /$ZFSPOOL
|
||||
check chmod 755 /$ZFSPOOL
|
||||
check cd /
|
||||
# XXX: How to make 'check' work here?
|
||||
su postgres -c "initdb -D /$ZFSPOOL/db"
|
||||
su postgres -c "pg_ctl -D /$ZFSPOOL/db -l /tmp/log start"
|
||||
check sleep 10
|
||||
su postgres -c "pgbench -i postgres"
|
||||
su postgres -c "pgbench -t 10000 postgres"
|
||||
su postgres -c "pg_ctl -D /$ZFSPOOL/db stop"
|
||||
check cd -
|
||||
check zpool destroy -f $ZFSPOOL
|
||||
}
|
||||
|
||||
test_postgresql() {
|
||||
echo "*** postgresql ***"
|
||||
case `uname` in
|
||||
FreeBSD)
|
||||
test_postgresql_freebsd
|
||||
;;
|
||||
Linux)
|
||||
test_postgresql_linux
|
||||
;;
|
||||
SunOS)
|
||||
test_postgresql_solaris
|
||||
;;
|
||||
*)
|
||||
die "unsupported system"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
test_detach() {
|
||||
echo "*** detach ***"
|
||||
case `uname` in
|
||||
FreeBSD)
|
||||
case `uname -r` in
|
||||
9*)
|
||||
echo "*** detaching not supported on FreeBSD 9 ***"
|
||||
echo "*** please reboot the initiator VM before trying to run this script again ***"
|
||||
;;
|
||||
*)
|
||||
check iscsictl -Ra
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
Linux)
|
||||
check iscsiadm -m node --logout
|
||||
;;
|
||||
SunOS)
|
||||
check iscsiadm remove static-config $TARGET1,$TARGETIP
|
||||
;;
|
||||
*)
|
||||
die "unsupported system"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
banner
|
||||
test_discovery
|
||||
test_attach
|
||||
test_newfs
|
||||
test_cp
|
||||
test_bonnie
|
||||
test_iozone
|
||||
test_postmark
|
||||
test_postgresql
|
||||
test_detach
|
||||
|
||||
echo "*** done ***"
|
||||
|
@ -68,6 +68,7 @@ SUBDIR= alias \
|
||||
id \
|
||||
ipcrm \
|
||||
ipcs \
|
||||
iscsictl \
|
||||
join \
|
||||
jot \
|
||||
${_kdump} \
|
||||
|
19
usr.bin/iscsictl/Makefile
Normal file
19
usr.bin/iscsictl/Makefile
Normal file
@ -0,0 +1,19 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= iscsictl
|
||||
SRCS= iscsictl.c periphs.c parse.y token.l y.tab.h
|
||||
CFLAGS+= -I${.CURDIR}
|
||||
CFLAGS+= -I${.CURDIR}/../../sys/dev/iscsi
|
||||
MAN= iscsictl.8
|
||||
|
||||
DPADD= ${LIBCAM} ${LIBUTIL}
|
||||
LDADD= -lcam -lfl -lutil
|
||||
|
||||
YFLAGS+= -v
|
||||
LFLAGS+= -i
|
||||
CLEANFILES= y.tab.c y.tab.h y.output
|
||||
|
||||
WARNS= 6
|
||||
NO_WMISSING_VARIABLE_DECLARATIONS=
|
||||
|
||||
.include <bsd.prog.mk>
|
153
usr.bin/iscsictl/iscsictl.8
Normal file
153
usr.bin/iscsictl/iscsictl.8
Normal file
@ -0,0 +1,153 @@
|
||||
.\" Copyright (c) 2012 The FreeBSD Foundation
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" 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 AUTHORS 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 AUTHORS 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.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd September 20, 2012
|
||||
.Dt ISCSICTL 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm iscsictl
|
||||
.Nd iSCSI initiator management utility
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Fl A
|
||||
.Fl h Ar host Fl t Ar target Op Fl u Ar user Fl s Ar secret
|
||||
.Nm
|
||||
.Fl A
|
||||
.Fl d Ar discovery-host Op Fl u Ar user Fl s Ar secret
|
||||
.Nm
|
||||
.Fl A
|
||||
.Fl a Op Fl c Ar path
|
||||
.Nm
|
||||
.Fl A
|
||||
.Fl n Ar nickname Op Fl c Ar path
|
||||
.Nm
|
||||
.Fl R
|
||||
.Op Fl h Ar host
|
||||
.Op Fl t Ar target
|
||||
.Nm
|
||||
.Fl R
|
||||
.Fl a
|
||||
.Nm
|
||||
.Fl R
|
||||
.Fl n Ar nickname Op Fl c Ar path
|
||||
.Nm
|
||||
.Fl L
|
||||
.Op Fl v
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
utility is used to configure the iSCSI initiator.
|
||||
.Pp
|
||||
The following options are available:
|
||||
.Bl -tag -width ".Fl A"
|
||||
.It Fl A
|
||||
Add session.
|
||||
.It Fl R
|
||||
Remove session.
|
||||
.It Fl L
|
||||
List sessions.
|
||||
.It Fl a
|
||||
When adding, add all sessions defined in the configuration file.
|
||||
When removing, remove all currently established sessions.
|
||||
.It Fl c
|
||||
Path to the configuration file.
|
||||
The default is
|
||||
.Pa /etc/iscsi.conf .
|
||||
.It Fl d
|
||||
Target host name or address used for SendTargets discovery.
|
||||
When used, it will add a temporary discovery session.
|
||||
After discovery is done, sessions will be added for each discovered target,
|
||||
and the temporary discovery sesion will be removed.
|
||||
.It Fl h
|
||||
Target host name or address for statically defined targets.
|
||||
.It Fl n
|
||||
The "nickname" of session defined in the configuration file.
|
||||
.It Fl t
|
||||
Target name.
|
||||
.It Fl v
|
||||
Verbose mode.
|
||||
.El
|
||||
.Pp
|
||||
Since connecting to the target is performed in background, non-zero
|
||||
exit status does not mean that the session was successfully established.
|
||||
Use
|
||||
.Nm Fl L
|
||||
to check the connection status.
|
||||
The initiator notifies
|
||||
.Xr devd 8
|
||||
when session gets connected or disconnected.
|
||||
.Pp
|
||||
Note that in order to the iSCSI initiator to be able to connect to a target,
|
||||
the
|
||||
.Xr iscsid 8
|
||||
daemon must be running.
|
||||
.Pp
|
||||
Also note that
|
||||
.Fx
|
||||
currently supports two different initiators: the old one,
|
||||
.Xr iscsi_initiator 4 ,
|
||||
with its control utility
|
||||
.Xr iscontrol 8 ,
|
||||
and the new one,
|
||||
.Xr iscsi 4 ,
|
||||
with
|
||||
.Xr iscsictl 8
|
||||
and
|
||||
.Xr iscsid 8 .
|
||||
The only thing the two have in common is the configuration file,
|
||||
.Xr iscsi.conf 5 .
|
||||
.Sh FILES
|
||||
.Bl -tag -width ".Pa /etc/iscsi.conf" -compact
|
||||
.It Pa /etc/iscsi.conf
|
||||
iSCSI initiator configuration file.
|
||||
.El
|
||||
.Sh EXIT STATUS
|
||||
The
|
||||
.Nm
|
||||
utility exits 0 on success, and >0 if an error occurs.
|
||||
.Sh EXAMPLES
|
||||
Attach to target qn.2012-06.com.example:target0, served by 192.168.1.1:
|
||||
.Dl Nm Fl A Fl t Ar qn.2012-06.com.example:target0 Fl h Ar 192.168.1.1
|
||||
.Pp
|
||||
Disconnect all iSCSI sessions:
|
||||
.Dl Nm Fl Ra
|
||||
.Sh SEE ALSO
|
||||
.Xr iscsid 8 ,
|
||||
.Xr iscsi.conf 5
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
command appeared in
|
||||
.Fx 10.0 .
|
||||
.Sh AUTHORS
|
||||
The
|
||||
.Nm
|
||||
was developed by
|
||||
.An Edward Tomasz Napierala Aq trasz@FreeBSD.org
|
||||
under sponsorship from the FreeBSD Foundation.
|
732
usr.bin/iscsictl/iscsictl.c
Normal file
732
usr.bin/iscsictl/iscsictl.c
Normal file
@ -0,0 +1,732 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <iscsi_ioctl.h>
|
||||
#include "iscsictl.h"
|
||||
|
||||
struct conf *
|
||||
conf_new(void)
|
||||
{
|
||||
struct conf *conf;
|
||||
|
||||
conf = calloc(1, sizeof(*conf));
|
||||
if (conf == NULL)
|
||||
err(1, "calloc");
|
||||
|
||||
TAILQ_INIT(&conf->conf_targets);
|
||||
|
||||
return (conf);
|
||||
}
|
||||
|
||||
struct target *
|
||||
target_find(struct conf *conf, const char *nickname)
|
||||
{
|
||||
struct target *targ;
|
||||
|
||||
TAILQ_FOREACH(targ, &conf->conf_targets, t_next) {
|
||||
if (targ->t_nickname != NULL &&
|
||||
strcasecmp(targ->t_nickname, nickname) == 0)
|
||||
return (targ);
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
struct target *
|
||||
target_new(struct conf *conf)
|
||||
{
|
||||
struct target *targ;
|
||||
|
||||
targ = calloc(1, sizeof(*targ));
|
||||
if (targ == NULL)
|
||||
err(1, "calloc");
|
||||
targ->t_conf = conf;
|
||||
TAILQ_INSERT_TAIL(&conf->conf_targets, targ, t_next);
|
||||
|
||||
return (targ);
|
||||
}
|
||||
|
||||
void
|
||||
target_delete(struct target *targ)
|
||||
{
|
||||
|
||||
TAILQ_REMOVE(&targ->t_conf->conf_targets, targ, t_next);
|
||||
free(targ);
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
default_initiator_name(void)
|
||||
{
|
||||
char *name;
|
||||
size_t namelen;
|
||||
int error;
|
||||
|
||||
namelen = _POSIX_HOST_NAME_MAX + strlen(DEFAULT_IQN);
|
||||
|
||||
name = calloc(1, namelen + 1);
|
||||
if (name == NULL)
|
||||
err(1, "calloc");
|
||||
strcpy(name, DEFAULT_IQN);
|
||||
error = gethostname(name + strlen(DEFAULT_IQN),
|
||||
namelen - strlen(DEFAULT_IQN));
|
||||
if (error != 0)
|
||||
err(1, "gethostname");
|
||||
|
||||
return (name);
|
||||
}
|
||||
|
||||
static bool
|
||||
valid_hex(const char ch)
|
||||
{
|
||||
switch (ch) {
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case 'a':
|
||||
case 'A':
|
||||
case 'b':
|
||||
case 'B':
|
||||
case 'c':
|
||||
case 'C':
|
||||
case 'd':
|
||||
case 'D':
|
||||
case 'e':
|
||||
case 'E':
|
||||
case 'f':
|
||||
case 'F':
|
||||
return (true);
|
||||
default:
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
valid_iscsi_name(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (strlen(name) >= MAX_NAME_LEN) {
|
||||
warnx("overlong name for \"%s\"; max length allowed "
|
||||
"by iSCSI specification is %d characters",
|
||||
name, MAX_NAME_LEN);
|
||||
return (false);
|
||||
}
|
||||
|
||||
/*
|
||||
* In the cases below, we don't return an error, just in case the admin
|
||||
* was right, and we're wrong.
|
||||
*/
|
||||
if (strncasecmp(name, "iqn.", strlen("iqn.")) == 0) {
|
||||
for (i = strlen("iqn."); name[i] != '\0'; i++) {
|
||||
/*
|
||||
* XXX: We should verify UTF-8 normalisation, as defined
|
||||
* by 3.2.6.2: iSCSI Name Encoding.
|
||||
*/
|
||||
if (isalnum(name[i]))
|
||||
continue;
|
||||
if (name[i] == '-' || name[i] == '.' || name[i] == ':')
|
||||
continue;
|
||||
warnx("invalid character \"%c\" in iSCSI name "
|
||||
"\"%s\"; allowed characters are letters, digits, "
|
||||
"'-', '.', and ':'", name[i], name);
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* XXX: Check more stuff: valid date and a valid reversed domain.
|
||||
*/
|
||||
} else if (strncasecmp(name, "eui.", strlen("eui.")) == 0) {
|
||||
if (strlen(name) != strlen("eui.") + 16)
|
||||
warnx("invalid iSCSI name \"%s\"; the \"eui.\" "
|
||||
"should be followed by exactly 16 hexadecimal "
|
||||
"digits", name);
|
||||
for (i = strlen("eui."); name[i] != '\0'; i++) {
|
||||
if (!valid_hex(name[i])) {
|
||||
warnx("invalid character \"%c\" in iSCSI "
|
||||
"name \"%s\"; allowed characters are 1-9 "
|
||||
"and A-F", name[i], name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (strncasecmp(name, "naa.", strlen("naa.")) == 0) {
|
||||
if (strlen(name) > strlen("naa.") + 32)
|
||||
warnx("invalid iSCSI name \"%s\"; the \"naa.\" "
|
||||
"should be followed by at most 32 hexadecimal "
|
||||
"digits", name);
|
||||
for (i = strlen("naa."); name[i] != '\0'; i++) {
|
||||
if (!valid_hex(name[i])) {
|
||||
warnx("invalid character \"%c\" in ISCSI "
|
||||
"name \"%s\"; allowed characters are 1-9 "
|
||||
"and A-F", name[i], name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
warnx("invalid iSCSI name \"%s\"; should start with "
|
||||
"either \".iqn\", \"eui.\", or \"naa.\"",
|
||||
name);
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
|
||||
void
|
||||
conf_verify(struct conf *conf)
|
||||
{
|
||||
struct target *targ;
|
||||
|
||||
TAILQ_FOREACH(targ, &conf->conf_targets, t_next) {
|
||||
assert(targ->t_nickname != NULL);
|
||||
if (targ->t_session_type == SESSION_TYPE_UNSPECIFIED)
|
||||
targ->t_session_type = SESSION_TYPE_NORMAL;
|
||||
if (targ->t_session_type == SESSION_TYPE_NORMAL &&
|
||||
targ->t_name == NULL)
|
||||
errx(1, "missing TargetName for target \"%s\"",
|
||||
targ->t_nickname);
|
||||
if (targ->t_session_type == SESSION_TYPE_DISCOVERY &&
|
||||
targ->t_name != NULL)
|
||||
errx(1, "cannot specify TargetName for discovery "
|
||||
"sessions for target \"%s\"", targ->t_nickname);
|
||||
if (targ->t_name != NULL) {
|
||||
if (valid_iscsi_name(targ->t_name) == false)
|
||||
errx(1, "invalid target name \"%s\"",
|
||||
targ->t_name);
|
||||
}
|
||||
if (targ->t_protocol == PROTOCOL_UNSPECIFIED)
|
||||
targ->t_protocol = PROTOCOL_ISCSI;
|
||||
#ifndef ICL_KERNEL_PROXY
|
||||
if (targ->t_protocol == PROTOCOL_ISER)
|
||||
errx(1, "iSER support requires ICL_KERNEL_PROXY; "
|
||||
"see iscsi(4) for details");
|
||||
#endif
|
||||
if (targ->t_address == NULL)
|
||||
errx(1, "missing TargetAddress for target \"%s\"",
|
||||
targ->t_nickname);
|
||||
if (targ->t_initiator_name == NULL)
|
||||
targ->t_initiator_name = default_initiator_name();
|
||||
if (valid_iscsi_name(targ->t_initiator_name) == false)
|
||||
errx(1, "invalid initiator name \"%s\"",
|
||||
targ->t_initiator_name);
|
||||
if (targ->t_header_digest == DIGEST_UNSPECIFIED)
|
||||
targ->t_header_digest = DIGEST_NONE;
|
||||
if (targ->t_data_digest == DIGEST_UNSPECIFIED)
|
||||
targ->t_data_digest = DIGEST_NONE;
|
||||
if (targ->t_auth_method == AUTH_METHOD_UNSPECIFIED) {
|
||||
if (targ->t_user != NULL || targ->t_secret != NULL ||
|
||||
targ->t_mutual_user != NULL ||
|
||||
targ->t_mutual_secret != NULL)
|
||||
targ->t_auth_method =
|
||||
AUTH_METHOD_CHAP;
|
||||
else
|
||||
targ->t_auth_method =
|
||||
AUTH_METHOD_NONE;
|
||||
}
|
||||
if (targ->t_auth_method == AUTH_METHOD_CHAP) {
|
||||
if (targ->t_user == NULL) {
|
||||
errx(1, "missing chapIName for target \"%s\"",
|
||||
targ->t_nickname);
|
||||
}
|
||||
if (targ->t_secret == NULL)
|
||||
errx(1, "missing chapSecret for target \"%s\"",
|
||||
targ->t_nickname);
|
||||
if (targ->t_mutual_user != NULL ||
|
||||
targ->t_mutual_secret != NULL) {
|
||||
if (targ->t_mutual_user == NULL)
|
||||
errx(1, "missing tgtChapName for "
|
||||
"target \"%s\"", targ->t_nickname);
|
||||
if (targ->t_mutual_secret == NULL)
|
||||
errx(1, "missing tgtChapSecret for "
|
||||
"target \"%s\"", targ->t_nickname);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
conf_from_target(struct iscsi_session_conf *conf,
|
||||
const struct target *targ)
|
||||
{
|
||||
memset(conf, 0, sizeof(*conf));
|
||||
|
||||
/*
|
||||
* XXX: Check bounds and return error instead of silently truncating.
|
||||
*/
|
||||
if (targ->t_initiator_name != NULL)
|
||||
strlcpy(conf->isc_initiator, targ->t_initiator_name,
|
||||
sizeof(conf->isc_initiator));
|
||||
if (targ->t_initiator_address != NULL)
|
||||
strlcpy(conf->isc_initiator_addr, targ->t_initiator_address,
|
||||
sizeof(conf->isc_initiator_addr));
|
||||
if (targ->t_initiator_alias != NULL)
|
||||
strlcpy(conf->isc_initiator_alias, targ->t_initiator_alias,
|
||||
sizeof(conf->isc_initiator_alias));
|
||||
if (targ->t_name != NULL)
|
||||
strlcpy(conf->isc_target, targ->t_name,
|
||||
sizeof(conf->isc_target));
|
||||
if (targ->t_address != NULL)
|
||||
strlcpy(conf->isc_target_addr, targ->t_address,
|
||||
sizeof(conf->isc_target_addr));
|
||||
if (targ->t_user != NULL)
|
||||
strlcpy(conf->isc_user, targ->t_user,
|
||||
sizeof(conf->isc_user));
|
||||
if (targ->t_secret != NULL)
|
||||
strlcpy(conf->isc_secret, targ->t_secret,
|
||||
sizeof(conf->isc_secret));
|
||||
if (targ->t_mutual_user != NULL)
|
||||
strlcpy(conf->isc_mutual_user, targ->t_mutual_user,
|
||||
sizeof(conf->isc_mutual_user));
|
||||
if (targ->t_mutual_secret != NULL)
|
||||
strlcpy(conf->isc_mutual_secret, targ->t_mutual_secret,
|
||||
sizeof(conf->isc_mutual_secret));
|
||||
if (targ->t_session_type == SESSION_TYPE_DISCOVERY)
|
||||
conf->isc_discovery = 1;
|
||||
if (targ->t_protocol == PROTOCOL_ISER)
|
||||
conf->isc_iser = 1;
|
||||
if (targ->t_header_digest == DIGEST_CRC32C)
|
||||
conf->isc_header_digest = ISCSI_DIGEST_CRC32C;
|
||||
else
|
||||
conf->isc_header_digest = ISCSI_DIGEST_NONE;
|
||||
if (targ->t_data_digest == DIGEST_CRC32C)
|
||||
conf->isc_data_digest = ISCSI_DIGEST_CRC32C;
|
||||
else
|
||||
conf->isc_data_digest = ISCSI_DIGEST_NONE;
|
||||
}
|
||||
|
||||
static int
|
||||
kernel_add(int iscsi_fd, const struct target *targ)
|
||||
{
|
||||
struct iscsi_session_add isa;
|
||||
int error;
|
||||
|
||||
memset(&isa, 0, sizeof(isa));
|
||||
conf_from_target(&isa.isa_conf, targ);
|
||||
error = ioctl(iscsi_fd, ISCSISADD, &isa);
|
||||
if (error != 0)
|
||||
warn("ISCSISADD");
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
kernel_remove(int iscsi_fd, const struct target *targ)
|
||||
{
|
||||
struct iscsi_session_remove isr;
|
||||
int error;
|
||||
|
||||
memset(&isr, 0, sizeof(isr));
|
||||
conf_from_target(&isr.isr_conf, targ);
|
||||
error = ioctl(iscsi_fd, ISCSISREMOVE, &isr);
|
||||
if (error != 0)
|
||||
warn("ISCSISREMOVE");
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: Add filtering.
|
||||
*/
|
||||
static int
|
||||
kernel_list(int iscsi_fd, const struct target *targ __unused,
|
||||
int verbose)
|
||||
{
|
||||
struct iscsi_session_state *states = NULL;
|
||||
const struct iscsi_session_state *state;
|
||||
const struct iscsi_session_conf *conf;
|
||||
struct iscsi_session_list isl;
|
||||
unsigned int i, nentries = 1;
|
||||
int error;
|
||||
bool show_periphs;
|
||||
|
||||
for (;;) {
|
||||
states = realloc(states,
|
||||
nentries * sizeof(struct iscsi_session_state));
|
||||
if (states == NULL)
|
||||
err(1, "realloc");
|
||||
|
||||
memset(&isl, 0, sizeof(isl));
|
||||
isl.isl_nentries = nentries;
|
||||
isl.isl_pstates = states;
|
||||
|
||||
error = ioctl(iscsi_fd, ISCSISLIST, &isl);
|
||||
if (error != 0 && errno == EMSGSIZE) {
|
||||
nentries *= 4;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (error != 0) {
|
||||
warn("ISCSISLIST");
|
||||
return (error);
|
||||
}
|
||||
|
||||
if (verbose != 0) {
|
||||
for (i = 0; i < isl.isl_nentries; i++) {
|
||||
state = &states[i];
|
||||
conf = &state->iss_conf;
|
||||
|
||||
printf("Session ID: %d\n", state->iss_id);
|
||||
printf("Initiator name: %s\n", conf->isc_initiator);
|
||||
printf("Initiator addr: %s\n",
|
||||
conf->isc_initiator_addr);
|
||||
printf("Initiator alias: %s\n",
|
||||
conf->isc_initiator_alias);
|
||||
printf("Target name: %s\n", conf->isc_target);
|
||||
printf("Target addr: %s\n",
|
||||
conf->isc_target_addr);
|
||||
printf("Target alias: %s\n",
|
||||
state->iss_target_alias);
|
||||
printf("User: %s\n", conf->isc_user);
|
||||
printf("Secret: %s\n", conf->isc_secret);
|
||||
printf("Mutual user: %s\n",
|
||||
conf->isc_mutual_user);
|
||||
printf("Mutual secret: %s\n",
|
||||
conf->isc_mutual_secret);
|
||||
printf("Session type: %s\n",
|
||||
conf->isc_discovery ? "Discovery" : "Normal");
|
||||
printf("Session state: %s\n",
|
||||
state->iss_connected ?
|
||||
"Connected" : "Disconnected");
|
||||
printf("Failure reason: %s\n", state->iss_reason);
|
||||
printf("Header digest: %s\n",
|
||||
state->iss_header_digest == ISCSI_DIGEST_CRC32C ?
|
||||
"CRC32C" : "None");
|
||||
printf("Data digest: %s\n",
|
||||
state->iss_data_digest == ISCSI_DIGEST_CRC32C ?
|
||||
"CRC32C" : "None");
|
||||
printf("DataSegmentLen: %d\n",
|
||||
state->iss_max_data_segment_length);
|
||||
printf("ImmediateData: %s\n",
|
||||
state->iss_immediate_data ? "Yes" : "No");
|
||||
printf("iSER (RDMA): %s\n",
|
||||
conf->isc_iser ? "Yes" : "No");
|
||||
printf("Device nodes: ");
|
||||
print_periphs(state->iss_id);
|
||||
printf("\n\n");
|
||||
}
|
||||
} else {
|
||||
printf("%-36s %-16s %s\n",
|
||||
"Target name", "Target addr", "State");
|
||||
for (i = 0; i < isl.isl_nentries; i++) {
|
||||
state = &states[i];
|
||||
conf = &state->iss_conf;
|
||||
show_periphs = false;
|
||||
|
||||
printf("%-36s %-16s ",
|
||||
conf->isc_target, conf->isc_target_addr);
|
||||
|
||||
if (state->iss_reason[0] != '\0') {
|
||||
printf("%s\n", state->iss_reason);
|
||||
} else {
|
||||
if (conf->isc_discovery) {
|
||||
printf("Discovery\n");
|
||||
} else if (state->iss_connected) {
|
||||
printf("Connected: ");
|
||||
print_periphs(state->iss_id);
|
||||
printf("\n");
|
||||
} else {
|
||||
printf("Disconnected\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
|
||||
fprintf(stderr, "usage: iscsictl -A -h host -t target "
|
||||
"[-u user -s secret]\n");
|
||||
fprintf(stderr, " iscsictl -A -d discovery-host "
|
||||
"[-u user -s secret]\n");
|
||||
fprintf(stderr, " iscsictl -A -a [-c path]\n");
|
||||
fprintf(stderr, " iscsictl -A -n nickname [-c path]\n");
|
||||
fprintf(stderr, " iscsictl -R [-h host] [-t target]\n");
|
||||
fprintf(stderr, " iscsictl -R -a\n");
|
||||
fprintf(stderr, " iscsictl -R -n nickname [-c path]\n");
|
||||
fprintf(stderr, " iscsictl -L [-v]\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char *
|
||||
checked_strdup(const char *s)
|
||||
{
|
||||
char *c;
|
||||
|
||||
c = strdup(s);
|
||||
if (c == NULL)
|
||||
err(1, "strdup");
|
||||
return (c);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int Aflag = 0, Rflag = 0, Lflag = 0, aflag = 0, vflag = 0;
|
||||
const char *conf_path = DEFAULT_CONFIG_PATH;
|
||||
char *nickname = NULL, *discovery_host = NULL, *host = NULL,
|
||||
*target = NULL, *user = NULL, *secret = NULL;
|
||||
int ch, error, iscsi_fd;
|
||||
int failed = 0;
|
||||
struct conf *conf;
|
||||
struct target *targ;
|
||||
|
||||
while ((ch = getopt(argc, argv, "ARLac:d:n:h:t:u:s:v")) != -1) {
|
||||
switch (ch) {
|
||||
case 'A':
|
||||
Aflag = 1;
|
||||
break;
|
||||
case 'R':
|
||||
Rflag = 1;
|
||||
break;
|
||||
case 'L':
|
||||
Lflag = 1;
|
||||
break;
|
||||
case 'a':
|
||||
aflag = 1;
|
||||
break;
|
||||
case 'c':
|
||||
conf_path = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
discovery_host = optarg;
|
||||
break;
|
||||
case 'n':
|
||||
nickname = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
host = optarg;
|
||||
break;
|
||||
case 't':
|
||||
target = optarg;
|
||||
break;
|
||||
case 'u':
|
||||
user = optarg;
|
||||
break;
|
||||
case 's':
|
||||
secret = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
vflag = 1;
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
if (argc != 0)
|
||||
usage();
|
||||
|
||||
if (Aflag + Rflag + Lflag == 0)
|
||||
Lflag = 1;
|
||||
if (Aflag + Rflag + Lflag > 1)
|
||||
errx(1, "at most one of -A, -R, or -L may be specified");
|
||||
|
||||
/*
|
||||
* Note that we ignore unneccessary/inapplicable "-c" flag; so that
|
||||
* people can do something like "alias ISCSICTL="iscsictl -c path"
|
||||
* in shell scripts.
|
||||
*/
|
||||
if (Aflag != 0) {
|
||||
if (aflag != 0) {
|
||||
if (host != NULL)
|
||||
errx(1, "-a and -h and mutually exclusive");
|
||||
if (target != NULL)
|
||||
errx(1, "-a and -t and mutually exclusive");
|
||||
if (user != NULL)
|
||||
errx(1, "-a and -u and mutually exclusive");
|
||||
if (secret != NULL)
|
||||
errx(1, "-a and -s and mutually exclusive");
|
||||
if (nickname != NULL)
|
||||
errx(1, "-a and -n and mutually exclusive");
|
||||
if (discovery_host != NULL)
|
||||
errx(1, "-a and -d and mutually exclusive");
|
||||
} else if (nickname != NULL) {
|
||||
if (host != NULL)
|
||||
errx(1, "-n and -h and mutually exclusive");
|
||||
if (target != NULL)
|
||||
errx(1, "-n and -t and mutually exclusive");
|
||||
if (user != NULL)
|
||||
errx(1, "-n and -u and mutually exclusive");
|
||||
if (secret != NULL)
|
||||
errx(1, "-n and -s and mutually exclusive");
|
||||
if (discovery_host != NULL)
|
||||
errx(1, "-n and -d and mutually exclusive");
|
||||
} else if (discovery_host != NULL) {
|
||||
if (host != NULL)
|
||||
errx(1, "-d and -h and mutually exclusive");
|
||||
if (target != NULL)
|
||||
errx(1, "-d and -t and mutually exclusive");
|
||||
} else {
|
||||
if (target == NULL && host == NULL)
|
||||
errx(1, "must specify -a, -n or -t/-h");
|
||||
|
||||
if (target != NULL && host == NULL)
|
||||
errx(1, "-t must always be used with -h");
|
||||
if (host != NULL && target == NULL)
|
||||
errx(1, "-h must always be used with -t");
|
||||
}
|
||||
|
||||
if (user != NULL && secret == NULL)
|
||||
errx(1, "-u must always be used with -s");
|
||||
if (secret != NULL && user == NULL)
|
||||
errx(1, "-s must always be used with -u");
|
||||
|
||||
if (vflag != 0)
|
||||
errx(1, "-v cannot be used with -A");
|
||||
|
||||
} else if (Rflag != 0) {
|
||||
if (user != NULL)
|
||||
errx(1, "-R and -u are mutually exclusive");
|
||||
if (secret != NULL)
|
||||
errx(1, "-R and -s are mutually exclusive");
|
||||
if (discovery_host != NULL)
|
||||
errx(1, "-R and -d are mutually exclusive");
|
||||
|
||||
if (aflag != 0) {
|
||||
if (host != NULL)
|
||||
errx(1, "-a and -h and mutually exclusive");
|
||||
if (target != NULL)
|
||||
errx(1, "-a and -t and mutually exclusive");
|
||||
if (nickname != NULL)
|
||||
errx(1, "-a and -n and mutually exclusive");
|
||||
} else if (nickname != NULL) {
|
||||
if (host != NULL)
|
||||
errx(1, "-n and -h and mutually exclusive");
|
||||
if (target != NULL)
|
||||
errx(1, "-n and -t and mutually exclusive");
|
||||
} else if (host != NULL) {
|
||||
if (target != NULL)
|
||||
errx(1, "-h and -t and mutually exclusive");
|
||||
} else if (target != NULL) {
|
||||
if (host != NULL)
|
||||
errx(1, "-t and -h and mutually exclusive");
|
||||
} else
|
||||
errx(1, "must specify either-a, -n, -t, or -h");
|
||||
|
||||
if (vflag != 0)
|
||||
errx(1, "-v cannot be used with -R");
|
||||
|
||||
} else {
|
||||
assert(Lflag != 0);
|
||||
|
||||
if (host != NULL)
|
||||
errx(1, "-L and -h and mutually exclusive");
|
||||
if (target != NULL)
|
||||
errx(1, "-L and -t and mutually exclusive");
|
||||
if (user != NULL)
|
||||
errx(1, "-L and -u and mutually exclusive");
|
||||
if (secret != NULL)
|
||||
errx(1, "-L and -s and mutually exclusive");
|
||||
if (nickname != NULL)
|
||||
errx(1, "-L and -n and mutually exclusive");
|
||||
if (discovery_host != NULL)
|
||||
errx(1, "-L and -d and mutually exclusive");
|
||||
}
|
||||
|
||||
iscsi_fd = open(ISCSI_PATH, O_RDWR);
|
||||
if (iscsi_fd < 0)
|
||||
err(1, "failed to open %s", ISCSI_PATH);
|
||||
|
||||
if (Aflag != 0 && aflag != 0) {
|
||||
conf = conf_new_from_file(conf_path);
|
||||
|
||||
TAILQ_FOREACH(targ, &conf->conf_targets, t_next)
|
||||
failed += kernel_add(iscsi_fd, targ);
|
||||
} else if (nickname != NULL) {
|
||||
conf = conf_new_from_file(conf_path);
|
||||
targ = target_find(conf, nickname);
|
||||
if (targ == NULL)
|
||||
errx(1, "target %s not found in the configuration file",
|
||||
nickname);
|
||||
|
||||
if (Aflag != 0)
|
||||
failed += kernel_add(iscsi_fd, targ);
|
||||
else if (Rflag != 0)
|
||||
failed += kernel_remove(iscsi_fd, targ);
|
||||
else
|
||||
failed += kernel_list(iscsi_fd, targ, vflag);
|
||||
} else {
|
||||
if (Aflag != 0 && target != NULL) {
|
||||
if (valid_iscsi_name(target) == false)
|
||||
errx(1, "invalid target name \"%s\"", target);
|
||||
}
|
||||
conf = conf_new();
|
||||
targ = target_new(conf);
|
||||
targ->t_initiator_name = default_initiator_name();
|
||||
targ->t_header_digest = DIGEST_NONE;
|
||||
targ->t_data_digest = DIGEST_NONE;
|
||||
targ->t_name = target;
|
||||
if (discovery_host != NULL) {
|
||||
targ->t_session_type = SESSION_TYPE_DISCOVERY;
|
||||
targ->t_address = discovery_host;
|
||||
} else {
|
||||
targ->t_session_type = SESSION_TYPE_NORMAL;
|
||||
targ->t_address = host;
|
||||
}
|
||||
targ->t_user = user;
|
||||
targ->t_secret = secret;
|
||||
|
||||
if (Aflag != 0)
|
||||
failed += kernel_add(iscsi_fd, targ);
|
||||
else if (Rflag != 0)
|
||||
failed += kernel_remove(iscsi_fd, targ);
|
||||
else
|
||||
failed += kernel_list(iscsi_fd, targ, vflag);
|
||||
}
|
||||
|
||||
error = close(iscsi_fd);
|
||||
if (error != 0)
|
||||
err(1, "close");
|
||||
|
||||
if (failed > 0)
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
116
usr.bin/iscsictl/iscsictl.h
Normal file
116
usr.bin/iscsictl/iscsictl.h
Normal file
@ -0,0 +1,116 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef ISCSICTL_H
|
||||
#define ISCSICTL_H
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <stdbool.h>
|
||||
#include <libutil.h>
|
||||
|
||||
#define DEFAULT_CONFIG_PATH "/etc/iscsi.conf"
|
||||
#define DEFAULT_IQN "iqn.1994-09.org.freebsd:"
|
||||
|
||||
#define MAX_NAME_LEN 223
|
||||
#define MAX_DATA_SEGMENT_LENGTH 65536
|
||||
|
||||
#define AUTH_METHOD_UNSPECIFIED 0
|
||||
#define AUTH_METHOD_NONE 1
|
||||
#define AUTH_METHOD_CHAP 2
|
||||
|
||||
#define DIGEST_UNSPECIFIED 0
|
||||
#define DIGEST_NONE 1
|
||||
#define DIGEST_CRC32C 2
|
||||
|
||||
#define SESSION_TYPE_UNSPECIFIED 0
|
||||
#define SESSION_TYPE_NORMAL 1
|
||||
#define SESSION_TYPE_DISCOVERY 2
|
||||
|
||||
#define PROTOCOL_UNSPECIFIED 0
|
||||
#define PROTOCOL_ISCSI 1
|
||||
#define PROTOCOL_ISER 2
|
||||
|
||||
struct target {
|
||||
TAILQ_ENTRY(target) t_next;
|
||||
struct conf *t_conf;
|
||||
char *t_nickname;
|
||||
char *t_name;
|
||||
char *t_address;
|
||||
char *t_initiator_name;
|
||||
char *t_initiator_address;
|
||||
char *t_initiator_alias;
|
||||
int t_header_digest;
|
||||
int t_data_digest;
|
||||
int t_auth_method;
|
||||
int t_session_type;
|
||||
int t_protocol;
|
||||
char *t_user;
|
||||
char *t_secret;
|
||||
char *t_mutual_user;
|
||||
char *t_mutual_secret;
|
||||
};
|
||||
|
||||
struct conf {
|
||||
TAILQ_HEAD(, target) conf_targets;
|
||||
};
|
||||
|
||||
#define CONN_SESSION_TYPE_NONE 0
|
||||
#define CONN_SESSION_TYPE_DISCOVERY 1
|
||||
#define CONN_SESSION_TYPE_NORMAL 2
|
||||
|
||||
struct connection {
|
||||
struct target *conn_target;
|
||||
int conn_socket;
|
||||
int conn_session_type;
|
||||
uint32_t conn_cmdsn;
|
||||
uint32_t conn_statsn;
|
||||
size_t conn_max_data_segment_length;
|
||||
size_t conn_max_burst_length;
|
||||
size_t conn_max_outstanding_r2t;
|
||||
int conn_header_digest;
|
||||
int conn_data_digest;
|
||||
};
|
||||
|
||||
struct conf *conf_new(void);
|
||||
struct conf *conf_new_from_file(const char *path);
|
||||
void conf_delete(struct conf *conf);
|
||||
void conf_verify(struct conf *conf);
|
||||
|
||||
struct target *target_new(struct conf *conf);
|
||||
struct target *target_find(struct conf *conf, const char *nickname);
|
||||
void target_delete(struct target *ic);
|
||||
|
||||
void print_periphs(int session_id);
|
||||
|
||||
char *checked_strdup(const char *);
|
||||
bool valid_iscsi_name(const char *name);
|
||||
|
||||
#endif /* !ISCSICTL_H */
|
333
usr.bin/iscsictl/parse.y
Normal file
333
usr.bin/iscsictl/parse.y
Normal file
@ -0,0 +1,333 @@
|
||||
%{
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <assert.h>
|
||||
#include <err.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "iscsictl.h"
|
||||
|
||||
extern FILE *yyin;
|
||||
extern char *yytext;
|
||||
extern int lineno;
|
||||
|
||||
static struct conf *conf;
|
||||
static struct target *target;
|
||||
|
||||
extern void yyerror(const char *);
|
||||
extern int yylex(void);
|
||||
extern void yyrestart(FILE *);
|
||||
|
||||
%}
|
||||
|
||||
%token AUTH_METHOD HEADER_DIGEST DATA_DIGEST TARGET_NAME TARGET_ADDRESS
|
||||
%token INITIATOR_NAME INITIATOR_ADDRESS INITIATOR_ALIAS USER SECRET
|
||||
%token MUTUAL_USER MUTUAL_SECRET SESSION_TYPE PROTOCOL IGNORED
|
||||
%token EQUALS OPENING_BRACKET CLOSING_BRACKET
|
||||
|
||||
%union
|
||||
{
|
||||
char *str;
|
||||
}
|
||||
|
||||
%token <str> STR
|
||||
|
||||
%%
|
||||
|
||||
statements:
|
||||
|
|
||||
statements target_statement
|
||||
;
|
||||
|
||||
target_statement: STR OPENING_BRACKET target_entries CLOSING_BRACKET
|
||||
{
|
||||
if (target_find(conf, $1) != NULL)
|
||||
errx(1, "duplicated target %s", $1);
|
||||
target->t_nickname = $1;
|
||||
target = target_new(conf);
|
||||
}
|
||||
;
|
||||
|
||||
target_entries:
|
||||
|
|
||||
target_entries target_entry
|
||||
;
|
||||
|
||||
target_entry:
|
||||
target_name_statement
|
||||
|
|
||||
target_address_statement
|
||||
|
|
||||
initiator_name_statement
|
||||
|
|
||||
initiator_address_statement
|
||||
|
|
||||
initiator_alias_statement
|
||||
|
|
||||
user_statement
|
||||
|
|
||||
secret_statement
|
||||
|
|
||||
mutual_user_statement
|
||||
|
|
||||
mutual_secret_statement
|
||||
|
|
||||
auth_method_statement
|
||||
|
|
||||
header_digest_statement
|
||||
|
|
||||
data_digest_statement
|
||||
|
|
||||
session_type_statement
|
||||
|
|
||||
protocol_statement
|
||||
|
|
||||
ignored_statement
|
||||
;
|
||||
|
||||
target_name_statement: TARGET_NAME EQUALS STR
|
||||
{
|
||||
if (target->t_name != NULL)
|
||||
errx(1, "duplicated TargetName at line %d", lineno + 1);
|
||||
target->t_name = $3;
|
||||
}
|
||||
;
|
||||
|
||||
target_address_statement: TARGET_ADDRESS EQUALS STR
|
||||
{
|
||||
if (target->t_address != NULL)
|
||||
errx(1, "duplicated TargetAddress at line %d", lineno + 1);
|
||||
target->t_address = $3;
|
||||
}
|
||||
;
|
||||
|
||||
initiator_name_statement: INITIATOR_NAME EQUALS STR
|
||||
{
|
||||
if (target->t_initiator_name != NULL)
|
||||
errx(1, "duplicated InitiatorName at line %d", lineno + 1);
|
||||
target->t_initiator_name = $3;
|
||||
}
|
||||
;
|
||||
|
||||
initiator_address_statement: INITIATOR_ADDRESS EQUALS STR
|
||||
{
|
||||
if (target->t_initiator_address != NULL)
|
||||
errx(1, "duplicated InitiatorAddress at line %d", lineno + 1);
|
||||
target->t_initiator_address = $3;
|
||||
}
|
||||
;
|
||||
|
||||
initiator_alias_statement: INITIATOR_ALIAS EQUALS STR
|
||||
{
|
||||
if (target->t_initiator_alias != NULL)
|
||||
errx(1, "duplicated InitiatorAlias at line %d", lineno + 1);
|
||||
target->t_initiator_alias = $3;
|
||||
}
|
||||
;
|
||||
|
||||
user_statement: USER EQUALS STR
|
||||
{
|
||||
if (target->t_user != NULL)
|
||||
errx(1, "duplicated chapIName at line %d", lineno + 1);
|
||||
target->t_user = $3;
|
||||
}
|
||||
;
|
||||
|
||||
secret_statement: SECRET EQUALS STR
|
||||
{
|
||||
if (target->t_secret != NULL)
|
||||
errx(1, "duplicated chapSecret at line %d", lineno + 1);
|
||||
target->t_secret = $3;
|
||||
}
|
||||
;
|
||||
|
||||
mutual_user_statement: MUTUAL_USER EQUALS STR
|
||||
{
|
||||
if (target->t_mutual_user != NULL)
|
||||
errx(1, "duplicated tgtChapName at line %d", lineno + 1);
|
||||
target->t_mutual_user = $3;
|
||||
}
|
||||
;
|
||||
|
||||
mutual_secret_statement:MUTUAL_SECRET EQUALS STR
|
||||
{
|
||||
if (target->t_mutual_secret != NULL)
|
||||
errx(1, "duplicated tgtChapSecret at line %d", lineno + 1);
|
||||
target->t_mutual_secret = $3;
|
||||
}
|
||||
;
|
||||
|
||||
auth_method_statement: AUTH_METHOD EQUALS STR
|
||||
{
|
||||
if (target->t_auth_method != AUTH_METHOD_UNSPECIFIED)
|
||||
errx(1, "duplicated AuthMethod at line %d", lineno + 1);
|
||||
if (strcasecmp($3, "none") == 0)
|
||||
target->t_auth_method = AUTH_METHOD_NONE;
|
||||
else if (strcasecmp($3, "chap") == 0)
|
||||
target->t_auth_method = AUTH_METHOD_CHAP;
|
||||
else
|
||||
errx(1, "invalid AuthMethod at line %d; "
|
||||
"must be either \"none\" or \"CHAP\"", lineno + 1);
|
||||
}
|
||||
;
|
||||
|
||||
header_digest_statement: HEADER_DIGEST EQUALS STR
|
||||
{
|
||||
if (target->t_header_digest != DIGEST_UNSPECIFIED)
|
||||
errx(1, "duplicated HeaderDigest at line %d", lineno + 1);
|
||||
if (strcasecmp($3, "none") == 0)
|
||||
target->t_header_digest = DIGEST_NONE;
|
||||
else if (strcasecmp($3, "CRC32C") == 0)
|
||||
target->t_header_digest = DIGEST_CRC32C;
|
||||
else
|
||||
errx(1, "invalid HeaderDigest at line %d; "
|
||||
"must be either \"none\" or \"CRC32C\"", lineno + 1);
|
||||
}
|
||||
;
|
||||
|
||||
data_digest_statement: DATA_DIGEST EQUALS STR
|
||||
{
|
||||
if (target->t_data_digest != DIGEST_UNSPECIFIED)
|
||||
errx(1, "duplicated DataDigest at line %d", lineno + 1);
|
||||
if (strcasecmp($3, "none") == 0)
|
||||
target->t_data_digest = DIGEST_NONE;
|
||||
else if (strcasecmp($3, "CRC32C") == 0)
|
||||
target->t_data_digest = DIGEST_CRC32C;
|
||||
else
|
||||
errx(1, "invalid DataDigest at line %d; "
|
||||
"must be either \"none\" or \"CRC32C\"", lineno + 1);
|
||||
}
|
||||
;
|
||||
|
||||
session_type_statement: SESSION_TYPE EQUALS STR
|
||||
{
|
||||
if (target->t_session_type != SESSION_TYPE_UNSPECIFIED)
|
||||
errx(1, "duplicated SessionType at line %d", lineno + 1);
|
||||
if (strcasecmp($3, "normal") == 0)
|
||||
target->t_session_type = SESSION_TYPE_NORMAL;
|
||||
else if (strcasecmp($3, "discovery") == 0)
|
||||
target->t_session_type = SESSION_TYPE_DISCOVERY;
|
||||
else
|
||||
errx(1, "invalid SessionType at line %d; "
|
||||
"must be either \"normal\" or \"discovery\"", lineno + 1);
|
||||
}
|
||||
;
|
||||
|
||||
protocol_statement: PROTOCOL EQUALS STR
|
||||
{
|
||||
if (target->t_protocol != PROTOCOL_UNSPECIFIED)
|
||||
errx(1, "duplicated protocol at line %d", lineno + 1);
|
||||
if (strcasecmp($3, "iscsi") == 0)
|
||||
target->t_protocol = PROTOCOL_ISCSI;
|
||||
else if (strcasecmp($3, "iser") == 0)
|
||||
target->t_protocol = PROTOCOL_ISER;
|
||||
else
|
||||
errx(1, "invalid protocol at line %d; "
|
||||
"must be either \"iscsi\" or \"iser\"", lineno + 1);
|
||||
}
|
||||
;
|
||||
|
||||
ignored_statement: IGNORED EQUALS STR
|
||||
{
|
||||
warnx("obsolete statement ignored at line %d", lineno + 1);
|
||||
}
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
void
|
||||
yyerror(const char *str)
|
||||
{
|
||||
|
||||
errx(1, "error in configuration file at line %d near '%s': %s",
|
||||
lineno + 1, yytext, str);
|
||||
}
|
||||
|
||||
static void
|
||||
check_perms(const char *path)
|
||||
{
|
||||
struct stat sb;
|
||||
int error;
|
||||
|
||||
error = stat(path, &sb);
|
||||
if (error != 0) {
|
||||
warn("stat");
|
||||
return;
|
||||
}
|
||||
if (sb.st_mode & S_IWOTH) {
|
||||
warnx("%s is world-writable", path);
|
||||
} else if (sb.st_mode & S_IROTH) {
|
||||
warnx("%s is world-readable", path);
|
||||
} else if (sb.st_mode & S_IXOTH) {
|
||||
/*
|
||||
* Ok, this one doesn't matter, but still do it,
|
||||
* just for consistency.
|
||||
*/
|
||||
warnx("%s is world-executable", path);
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: Should we also check for owner != 0?
|
||||
*/
|
||||
}
|
||||
|
||||
struct conf *
|
||||
conf_new_from_file(const char *path)
|
||||
{
|
||||
int error;
|
||||
|
||||
conf = conf_new();
|
||||
target = target_new(conf);
|
||||
|
||||
yyin = fopen(path, "r");
|
||||
if (yyin == NULL)
|
||||
err(1, "unable to open configuration file %s", path);
|
||||
check_perms(path);
|
||||
lineno = 0;
|
||||
yyrestart(yyin);
|
||||
error = yyparse();
|
||||
assert(error == 0);
|
||||
fclose(yyin);
|
||||
|
||||
assert(target->t_nickname == NULL);
|
||||
target_delete(target);
|
||||
|
||||
conf_verify(conf);
|
||||
|
||||
return (conf);
|
||||
}
|
186
usr.bin/iscsictl/periphs.c
Normal file
186
usr.bin/iscsictl/periphs.c
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (c) 1997-2007 Kenneth D. Merry
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions of this software were 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.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* 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/ioctl.h>
|
||||
#include <sys/stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/endian.h>
|
||||
#include <sys/sbuf.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <fcntl.h>
|
||||
#include <err.h>
|
||||
|
||||
#include <cam/cam.h>
|
||||
#include <cam/cam_debug.h>
|
||||
#include <cam/cam_ccb.h>
|
||||
#include <cam/scsi/scsi_all.h>
|
||||
#include <cam/scsi/scsi_da.h>
|
||||
#include <cam/scsi/scsi_pass.h>
|
||||
#include <cam/scsi/scsi_message.h>
|
||||
#include <cam/scsi/smp_all.h>
|
||||
#include <cam/ata/ata_all.h>
|
||||
#include <camlib.h>
|
||||
|
||||
#include "iscsictl.h"
|
||||
|
||||
void
|
||||
print_periphs(int session_id)
|
||||
{
|
||||
union ccb ccb;
|
||||
int bufsize, fd;
|
||||
unsigned int i;
|
||||
int skip_bus, skip_device;
|
||||
|
||||
if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) {
|
||||
warn("couldn't open %s", XPT_DEVICE);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* First, iterate over the whole list to find the bus.
|
||||
*/
|
||||
|
||||
bzero(&ccb, sizeof(union ccb));
|
||||
|
||||
ccb.ccb_h.path_id = CAM_XPT_PATH_ID;
|
||||
ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
|
||||
ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
|
||||
|
||||
ccb.ccb_h.func_code = XPT_DEV_MATCH;
|
||||
bufsize = sizeof(struct dev_match_result) * 100;
|
||||
ccb.cdm.match_buf_len = bufsize;
|
||||
ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize);
|
||||
if (ccb.cdm.matches == NULL) {
|
||||
warnx("can't malloc memory for matches");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
ccb.cdm.num_matches = 0;
|
||||
|
||||
/*
|
||||
* We fetch all nodes, since we display most of them in the default
|
||||
* case, and all in the verbose case.
|
||||
*/
|
||||
ccb.cdm.num_patterns = 0;
|
||||
ccb.cdm.pattern_buf_len = 0;
|
||||
|
||||
/*
|
||||
* We do the ioctl multiple times if necessary, in case there are
|
||||
* more than 100 nodes in the EDT.
|
||||
*/
|
||||
do {
|
||||
if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) {
|
||||
warn("error sending CAMIOCOMMAND ioctl");
|
||||
break;
|
||||
}
|
||||
|
||||
if ((ccb.ccb_h.status != CAM_REQ_CMP)
|
||||
|| ((ccb.cdm.status != CAM_DEV_MATCH_LAST)
|
||||
&& (ccb.cdm.status != CAM_DEV_MATCH_MORE))) {
|
||||
warnx("got CAM error %#x, CDM error %d\n",
|
||||
ccb.ccb_h.status, ccb.cdm.status);
|
||||
break;
|
||||
}
|
||||
|
||||
skip_bus = 1;
|
||||
skip_device = 1;
|
||||
|
||||
for (i = 0; i < ccb.cdm.num_matches; i++) {
|
||||
switch (ccb.cdm.matches[i].type) {
|
||||
case DEV_MATCH_BUS: {
|
||||
struct bus_match_result *bus_result;
|
||||
|
||||
bus_result = &ccb.cdm.matches[i].result.bus_result;
|
||||
|
||||
skip_bus = 1;
|
||||
|
||||
if (strcmp(bus_result->dev_name, "iscsi") != 0) {
|
||||
//printf("not iscsi\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((int)bus_result->unit_number != session_id) {
|
||||
//printf("wrong unit, %d != %d\n", bus_result->unit_number, session_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
skip_bus = 0;
|
||||
}
|
||||
case DEV_MATCH_DEVICE: {
|
||||
skip_device = 1;
|
||||
|
||||
if (skip_bus != 0)
|
||||
continue;
|
||||
|
||||
skip_device = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
case DEV_MATCH_PERIPH: {
|
||||
struct periph_match_result *periph_result;
|
||||
|
||||
periph_result =
|
||||
&ccb.cdm.matches[i].result.periph_result;
|
||||
|
||||
if (skip_device != 0)
|
||||
continue;
|
||||
|
||||
if (strcmp(periph_result->periph_name, "pass") == 0)
|
||||
continue;
|
||||
|
||||
fprintf(stdout, "%s%d ",
|
||||
periph_result->periph_name,
|
||||
periph_result->unit_number);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
fprintf(stdout, "unknown match type\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} while ((ccb.ccb_h.status == CAM_REQ_CMP)
|
||||
&& (ccb.cdm.status == CAM_DEV_MATCH_MORE));
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
93
usr.bin/iscsictl/token.l
Normal file
93
usr.bin/iscsictl/token.l
Normal file
@ -0,0 +1,93 @@
|
||||
%{
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "iscsictl.h"
|
||||
#include "y.tab.h"
|
||||
|
||||
int lineno;
|
||||
|
||||
#define YY_DECL int yylex(void)
|
||||
extern int yylex(void);
|
||||
|
||||
%}
|
||||
|
||||
%option noinput
|
||||
%option nounput
|
||||
|
||||
%%
|
||||
HeaderDigest { return HEADER_DIGEST; }
|
||||
DataDigest { return DATA_DIGEST; }
|
||||
TargetName { return TARGET_NAME; }
|
||||
TargetAddress { return TARGET_ADDRESS; }
|
||||
InitiatorName { return INITIATOR_NAME; }
|
||||
InitiatorAddress { return INITIATOR_ADDRESS; }
|
||||
InitiatorAlias { return INITIATOR_ALIAS; }
|
||||
chapIName { return USER; }
|
||||
chapSecret { return SECRET; }
|
||||
tgtChapName { return MUTUAL_USER; }
|
||||
tgtChapSecret { return MUTUAL_SECRET; }
|
||||
AuthMethod { return AUTH_METHOD; }
|
||||
SessionType { return SESSION_TYPE; }
|
||||
protocol { return PROTOCOL; }
|
||||
port { return IGNORED; }
|
||||
MaxConnections { return IGNORED; }
|
||||
TargetAlias { return IGNORED; }
|
||||
TargetPortalGroupTag { return IGNORED; }
|
||||
InitialR2T { return IGNORED; }
|
||||
ImmediateData { return IGNORED; }
|
||||
MaxRecvDataSegmentLength { return IGNORED; }
|
||||
MaxBurstLength { return IGNORED; }
|
||||
FirstBurstLength { return IGNORED; }
|
||||
DefaultTime2Wait { return IGNORED; }
|
||||
DefaultTime2Retain { return IGNORED; }
|
||||
MaxOutstandingR2T { return IGNORED; }
|
||||
DataPDUInOrder { return IGNORED; }
|
||||
DataSequenceInOrder { return IGNORED; }
|
||||
ErrorRecoveryLevel { return IGNORED; }
|
||||
tags { return IGNORED; }
|
||||
maxluns { return IGNORED; }
|
||||
sockbufsize { return IGNORED; }
|
||||
chapDigest { return IGNORED; }
|
||||
\"[^"]+\" { yylval.str = strndup(yytext + 1,
|
||||
strlen(yytext) - 2); return STR; }
|
||||
[a-zA-Z0-9\.\-_/\:\[\]]+ { yylval.str = strdup(yytext); return STR; }
|
||||
\{ { return OPENING_BRACKET; }
|
||||
\} { return CLOSING_BRACKET; }
|
||||
= { return EQUALS; }
|
||||
#.*$ /* ignore comments */;
|
||||
\n { lineno++; }
|
||||
[ \t]+ /* ignore whitespace */;
|
||||
%%
|
@ -17,6 +17,7 @@ SUBDIR= adduser \
|
||||
crashinfo \
|
||||
cron \
|
||||
ctladm \
|
||||
ctld \
|
||||
daemon \
|
||||
dconschat \
|
||||
devinfo \
|
||||
@ -35,6 +36,7 @@ SUBDIR= adduser \
|
||||
ifmcstat \
|
||||
inetd \
|
||||
iostat \
|
||||
iscsid \
|
||||
isfctl \
|
||||
kldxref \
|
||||
mailwrapper \
|
||||
|
@ -197,6 +197,16 @@
|
||||
.Nm
|
||||
.Ic dumpstructs
|
||||
.Nm
|
||||
.Ic islist
|
||||
.Op Fl v
|
||||
.Op Fl x
|
||||
.Nm
|
||||
.Ic islogout
|
||||
.Aq Fl a | Fl h Ar host | Fl c Ar connection-id | Fl i Ar name
|
||||
.Nm
|
||||
.Ic isterminate
|
||||
.Aq Fl a | Fl h Ar host | Fl c Ar connection-id | Fl i Ar name
|
||||
.Nm
|
||||
.Ic help
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
@ -883,6 +893,41 @@ If you specify
|
||||
.Fl x ,
|
||||
the entire LUN database is displayed in XML format.
|
||||
.El
|
||||
.It Ic islist
|
||||
Get a list of currently running iSCSI connections.
|
||||
This includes initiator and target names and the unique connection IDs.
|
||||
.Bl -tag -width 11n
|
||||
.It Fl v
|
||||
Verbose mode.
|
||||
.It Fl x
|
||||
Dump the raw XML.
|
||||
The connections list information from the kernel comes in XML format, and this
|
||||
option allows the display of the raw XML data.
|
||||
.El
|
||||
.It Ic islogout
|
||||
Ask the initiator to log out iSCSI connections matching criteria.
|
||||
.Bl -tag -width 11n
|
||||
.It Fl a
|
||||
Log out all connections.
|
||||
.It Fl h
|
||||
Specify initiator IP address.
|
||||
.It Fl c
|
||||
Specify connection ID.
|
||||
.It Fl i
|
||||
Specify initiator name.
|
||||
.El
|
||||
.It Ic isterminate
|
||||
Forcibly terminate iSCSI connections matching criteria.
|
||||
.Bl -tag -width 11n
|
||||
.It Fl a
|
||||
Terminate all connections.
|
||||
.It Fl h
|
||||
Specify initiator IP address.
|
||||
.It Fl c
|
||||
Specify connection ID.
|
||||
.It Fl i
|
||||
Specify initiator name.
|
||||
.El
|
||||
.It Ic help
|
||||
Display
|
||||
.Nm
|
||||
@ -977,7 +1022,8 @@ This will result in a sense key of NOT READY (0x02), and an ASC/ASCQ of
|
||||
.Xr cam 4 ,
|
||||
.Xr ctl 4 ,
|
||||
.Xr xpt 4 ,
|
||||
.Xr camcontrol 8
|
||||
.Xr camcontrol 8 ,
|
||||
.Xr ctld 8
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
|
@ -117,7 +117,10 @@ typedef enum {
|
||||
CTLADM_CMD_PRES_OUT,
|
||||
CTLADM_CMD_INQ_VPD_DEVID,
|
||||
CTLADM_CMD_RTPG,
|
||||
CTLADM_CMD_MODIFY
|
||||
CTLADM_CMD_MODIFY,
|
||||
CTLADM_CMD_ISLIST,
|
||||
CTLADM_CMD_ISLOGOUT,
|
||||
CTLADM_CMD_ISTERMINATE
|
||||
} ctladm_cmdfunction;
|
||||
|
||||
typedef enum {
|
||||
@ -180,6 +183,9 @@ static struct ctladm_opts option_table[] = {
|
||||
{"help", CTLADM_CMD_HELP, CTLADM_ARG_NONE, NULL},
|
||||
{"inject", CTLADM_CMD_ERR_INJECT, CTLADM_ARG_NEED_TL, "cd:i:p:r:s:"},
|
||||
{"inquiry", CTLADM_CMD_INQUIRY, CTLADM_ARG_NEED_TL, NULL},
|
||||
{"islist", CTLADM_CMD_ISLIST, CTLADM_ARG_NONE, "vx"},
|
||||
{"islogout", CTLADM_CMD_ISLOGOUT, CTLADM_ARG_NONE, "ah:c:i:"},
|
||||
{"isterminate", CTLADM_CMD_ISTERMINATE, CTLADM_ARG_NONE, "ah:c:i:"},
|
||||
{"lunlist", CTLADM_CMD_LUNLIST, CTLADM_ARG_NONE, NULL},
|
||||
{"modesense", CTLADM_CMD_MODESENSE, CTLADM_ARG_NEED_TL, "P:S:dlm:c:"},
|
||||
{"modify", CTLADM_CMD_MODIFY, CTLADM_ARG_NONE, "b:l:s:"},
|
||||
@ -489,6 +495,9 @@ cctl_port_dump(int fd, int quiet, int xml, int32_t targ_port,
|
||||
case CTL_PORT_ISC:
|
||||
type = "ISC";
|
||||
break;
|
||||
case CTL_PORT_ISCSI:
|
||||
type = "ISCSI";
|
||||
break;
|
||||
default:
|
||||
type = "UNKNOWN";
|
||||
break;
|
||||
@ -578,6 +587,7 @@ static struct ctladm_opts cctl_fe_table[] = {
|
||||
{"fc", CTL_PORT_FC, CTLADM_ARG_NONE, NULL},
|
||||
{"scsi", CTL_PORT_SCSI, CTLADM_ARG_NONE, NULL},
|
||||
{"internal", CTL_PORT_INTERNAL, CTLADM_ARG_NONE, NULL},
|
||||
{"iscsi", CTL_PORT_ISCSI, CTLADM_ARG_NONE, NULL},
|
||||
{"all", CTL_PORT_ALL, CTLADM_ARG_NONE, NULL},
|
||||
{NULL, 0, 0, NULL}
|
||||
};
|
||||
@ -3399,6 +3409,403 @@ cctl_modify_lun(int fd, int argc, char **argv, char *combinedopt)
|
||||
return (retval);
|
||||
}
|
||||
|
||||
struct cctl_islist_conn {
|
||||
int connection_id;
|
||||
char *initiator;
|
||||
char *initiator_addr;
|
||||
char *initiator_alias;
|
||||
char *target;
|
||||
char *target_alias;
|
||||
char *header_digest;
|
||||
char *data_digest;
|
||||
char *max_data_segment_length;;
|
||||
int immediate_data;
|
||||
int iser;
|
||||
STAILQ_ENTRY(cctl_islist_conn) links;
|
||||
};
|
||||
|
||||
struct cctl_islist_data {
|
||||
int num_conns;
|
||||
STAILQ_HEAD(,cctl_islist_conn) conn_list;
|
||||
struct cctl_islist_conn *cur_conn;
|
||||
int level;
|
||||
struct sbuf *cur_sb[32];
|
||||
};
|
||||
|
||||
static void
|
||||
cctl_islist_start_element(void *user_data, const char *name, const char **attr)
|
||||
{
|
||||
int i;
|
||||
struct cctl_islist_data *islist;
|
||||
struct cctl_islist_conn *cur_conn;
|
||||
|
||||
islist = (struct cctl_islist_data *)user_data;
|
||||
cur_conn = islist->cur_conn;
|
||||
islist->level++;
|
||||
if ((u_int)islist->level > (sizeof(islist->cur_sb) /
|
||||
sizeof(islist->cur_sb[0])))
|
||||
errx(1, "%s: too many nesting levels, %zd max", __func__,
|
||||
sizeof(islist->cur_sb) / sizeof(islist->cur_sb[0]));
|
||||
|
||||
islist->cur_sb[islist->level] = sbuf_new_auto();
|
||||
if (islist->cur_sb[islist->level] == NULL)
|
||||
err(1, "%s: Unable to allocate sbuf", __func__);
|
||||
|
||||
if (strcmp(name, "connection") == 0) {
|
||||
if (cur_conn != NULL)
|
||||
errx(1, "%s: improper connection element nesting",
|
||||
__func__);
|
||||
|
||||
cur_conn = calloc(1, sizeof(*cur_conn));
|
||||
if (cur_conn == NULL)
|
||||
err(1, "%s: cannot allocate %zd bytes", __func__,
|
||||
sizeof(*cur_conn));
|
||||
|
||||
islist->num_conns++;
|
||||
islist->cur_conn = cur_conn;
|
||||
|
||||
STAILQ_INSERT_TAIL(&islist->conn_list, cur_conn, links);
|
||||
|
||||
for (i = 0; attr[i] != NULL; i += 2) {
|
||||
if (strcmp(attr[i], "id") == 0) {
|
||||
cur_conn->connection_id =
|
||||
strtoull(attr[i+1], NULL, 0);
|
||||
} else {
|
||||
errx(1,
|
||||
"%s: invalid connection attribute %s = %s",
|
||||
__func__, attr[i], attr[i+1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cctl_islist_end_element(void *user_data, const char *name)
|
||||
{
|
||||
struct cctl_islist_data *islist;
|
||||
struct cctl_islist_conn *cur_conn;
|
||||
char *str;
|
||||
|
||||
islist = (struct cctl_islist_data *)user_data;
|
||||
cur_conn = islist->cur_conn;
|
||||
|
||||
if ((cur_conn == NULL)
|
||||
&& (strcmp(name, "ctlislist") != 0))
|
||||
errx(1, "%s: cur_conn == NULL! (name = %s)", __func__, name);
|
||||
|
||||
if (islist->cur_sb[islist->level] == NULL)
|
||||
errx(1, "%s: no valid sbuf at level %d (name %s)", __func__,
|
||||
islist->level, name);
|
||||
|
||||
sbuf_finish(islist->cur_sb[islist->level]);
|
||||
str = strdup(sbuf_data(islist->cur_sb[islist->level]));
|
||||
if (str == NULL)
|
||||
err(1, "%s can't allocate %zd bytes for string", __func__,
|
||||
sbuf_len(islist->cur_sb[islist->level]));
|
||||
|
||||
sbuf_delete(islist->cur_sb[islist->level]);
|
||||
islist->cur_sb[islist->level] = NULL;
|
||||
islist->level--;
|
||||
|
||||
if (strcmp(name, "initiator") == 0) {
|
||||
cur_conn->initiator = str;
|
||||
str = NULL;
|
||||
} else if (strcmp(name, "initiator_addr") == 0) {
|
||||
cur_conn->initiator_addr = str;
|
||||
str = NULL;
|
||||
} else if (strcmp(name, "initiator_alias") == 0) {
|
||||
cur_conn->initiator_alias = str;
|
||||
str = NULL;
|
||||
} else if (strcmp(name, "target") == 0) {
|
||||
cur_conn->target = str;
|
||||
str = NULL;
|
||||
} else if (strcmp(name, "target_alias") == 0) {
|
||||
cur_conn->target_alias = str;
|
||||
str = NULL;
|
||||
} else if (strcmp(name, "header_digest") == 0) {
|
||||
cur_conn->header_digest = str;
|
||||
str = NULL;
|
||||
} else if (strcmp(name, "data_digest") == 0) {
|
||||
cur_conn->data_digest = str;
|
||||
str = NULL;
|
||||
} else if (strcmp(name, "max_data_segment_length") == 0) {
|
||||
cur_conn->max_data_segment_length = str;
|
||||
str = NULL;
|
||||
} else if (strcmp(name, "immediate_data") == 0) {
|
||||
cur_conn->immediate_data = atoi(str);
|
||||
} else if (strcmp(name, "iser") == 0) {
|
||||
cur_conn->iser = atoi(str);
|
||||
} else if (strcmp(name, "connection") == 0) {
|
||||
islist->cur_conn = NULL;
|
||||
} else if (strcmp(name, "ctlislist") == 0) {
|
||||
} else
|
||||
errx(1, "unknown element %s", name);
|
||||
|
||||
free(str);
|
||||
}
|
||||
|
||||
static void
|
||||
cctl_islist_char_handler(void *user_data, const XML_Char *str, int len)
|
||||
{
|
||||
struct cctl_islist_data *islist;
|
||||
|
||||
islist = (struct cctl_islist_data *)user_data;
|
||||
|
||||
sbuf_bcat(islist->cur_sb[islist->level], str, len);
|
||||
}
|
||||
|
||||
static int
|
||||
cctl_islist(int fd, int argc, char **argv, char *combinedopt)
|
||||
{
|
||||
struct ctl_iscsi req;
|
||||
struct cctl_islist_data islist;
|
||||
struct cctl_islist_conn *conn;
|
||||
XML_Parser parser;
|
||||
char *conn_str;
|
||||
int conn_len;
|
||||
int dump_xml = 0;
|
||||
int c, retval, verbose = 0;
|
||||
|
||||
retval = 0;
|
||||
conn_len = 4096;
|
||||
|
||||
bzero(&islist, sizeof(islist));
|
||||
STAILQ_INIT(&islist.conn_list);
|
||||
|
||||
while ((c = getopt(argc, argv, combinedopt)) != -1) {
|
||||
switch (c) {
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
case 'x':
|
||||
dump_xml = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
retry:
|
||||
conn_str = malloc(conn_len);
|
||||
|
||||
bzero(&req, sizeof(req));
|
||||
req.type = CTL_ISCSI_LIST;
|
||||
req.data.list.alloc_len = conn_len;
|
||||
req.data.list.conn_xml = conn_str;
|
||||
|
||||
if (ioctl(fd, CTL_ISCSI, &req) == -1) {
|
||||
warn("%s: error issuing CTL_ISCSI ioctl", __func__);
|
||||
retval = 1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
if (req.status == CTL_ISCSI_ERROR) {
|
||||
warnx("%s: error returned from CTL_ISCSI ioctl:\n%s",
|
||||
__func__, req.error_str);
|
||||
} else if (req.status == CTL_ISCSI_LIST_NEED_MORE_SPACE) {
|
||||
conn_len = conn_len << 1;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (dump_xml != 0) {
|
||||
printf("%s", conn_str);
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
parser = XML_ParserCreate(NULL);
|
||||
if (parser == NULL) {
|
||||
warn("%s: Unable to create XML parser", __func__);
|
||||
retval = 1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
XML_SetUserData(parser, &islist);
|
||||
XML_SetElementHandler(parser, cctl_islist_start_element,
|
||||
cctl_islist_end_element);
|
||||
XML_SetCharacterDataHandler(parser, cctl_islist_char_handler);
|
||||
|
||||
retval = XML_Parse(parser, conn_str, strlen(conn_str), 1);
|
||||
XML_ParserFree(parser);
|
||||
if (retval != 1) {
|
||||
retval = 1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
if (verbose != 0) {
|
||||
STAILQ_FOREACH(conn, &islist.conn_list, links) {
|
||||
printf("Session ID: %d\n", conn->connection_id);
|
||||
printf("Initiator name: %s\n", conn->initiator);
|
||||
printf("Initiator addr: %s\n", conn->initiator_addr);
|
||||
printf("Initiator alias: %s\n", conn->initiator_alias);
|
||||
printf("Target name: %s\n", conn->target);
|
||||
printf("Target alias: %s\n", conn->target_alias);
|
||||
printf("Header digest: %s\n", conn->header_digest);
|
||||
printf("Data digest: %s\n", conn->data_digest);
|
||||
printf("DataSegmentLen: %s\n", conn->max_data_segment_length);
|
||||
printf("ImmediateData: %s\n", conn->immediate_data ? "Yes" : "No");
|
||||
printf("iSER (RDMA): %s\n", conn->iser ? "Yes" : "No");
|
||||
printf("\n");
|
||||
}
|
||||
} else {
|
||||
printf("%4s %-16s %-36s %-36s\n", "ID", "Address", "Initiator name",
|
||||
"Target name");
|
||||
STAILQ_FOREACH(conn, &islist.conn_list, links) {
|
||||
printf("%4u %-16s %-36s %-36s\n",
|
||||
conn->connection_id, conn->initiator_addr, conn->initiator,
|
||||
conn->target);
|
||||
}
|
||||
}
|
||||
bailout:
|
||||
free(conn_str);
|
||||
|
||||
return (retval);
|
||||
}
|
||||
|
||||
static int
|
||||
cctl_islogout(int fd, int argc, char **argv, char *combinedopt)
|
||||
{
|
||||
struct ctl_iscsi req;
|
||||
int retval = 0, c;
|
||||
int all = 0, connection_id = -1, nargs = 0;
|
||||
char *initiator_name = NULL, *initiator_addr = NULL;
|
||||
|
||||
while ((c = getopt(argc, argv, combinedopt)) != -1) {
|
||||
switch (c) {
|
||||
case 'a':
|
||||
all = 1;
|
||||
nargs++;
|
||||
break;
|
||||
case 'h':
|
||||
initiator_addr = strdup(optarg);
|
||||
if (initiator_addr == NULL)
|
||||
err(1, "%s: strdup", __func__);
|
||||
nargs++;
|
||||
break;
|
||||
case 'c':
|
||||
connection_id = strtoul(optarg, NULL, 0);
|
||||
nargs++;
|
||||
break;
|
||||
case 'i':
|
||||
initiator_name = strdup(optarg);
|
||||
if (initiator_name == NULL)
|
||||
err(1, "%s: strdup", __func__);
|
||||
nargs++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nargs == 0)
|
||||
errx(1, "%s: either -a, -h, -c, or -i must be specified",
|
||||
__func__);
|
||||
if (nargs > 1)
|
||||
errx(1, "%s: only one of -a, -h, -c, or -i may be specified",
|
||||
__func__);
|
||||
|
||||
bzero(&req, sizeof(req));
|
||||
req.type = CTL_ISCSI_LOGOUT;
|
||||
req.data.logout.connection_id = connection_id;
|
||||
if (initiator_addr != NULL)
|
||||
strlcpy(req.data.logout.initiator_addr,
|
||||
initiator_addr, sizeof(req.data.logout.initiator_addr));
|
||||
if (initiator_name != NULL)
|
||||
strlcpy(req.data.logout.initiator_name,
|
||||
initiator_name, sizeof(req.data.logout.initiator_name));
|
||||
if (all != 0)
|
||||
req.data.logout.all = 1;
|
||||
|
||||
if (ioctl(fd, CTL_ISCSI, &req) == -1) {
|
||||
warn("%s: error issuing CTL_ISCSI ioctl", __func__);
|
||||
retval = 1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
if (req.status != CTL_ISCSI_OK) {
|
||||
warnx("%s: error returned from CTL iSCSI logout request:\n%s",
|
||||
__func__, req.error_str);
|
||||
retval = 1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
printf("iSCSI logout requests submitted\n");
|
||||
|
||||
bailout:
|
||||
return (retval);
|
||||
}
|
||||
|
||||
static int
|
||||
cctl_isterminate(int fd, int argc, char **argv, char *combinedopt)
|
||||
{
|
||||
struct ctl_iscsi req;
|
||||
int retval = 0, c;
|
||||
int all = 0, connection_id = -1, nargs = 0;
|
||||
char *initiator_name = NULL, *initiator_addr = NULL;
|
||||
|
||||
while ((c = getopt(argc, argv, combinedopt)) != -1) {
|
||||
switch (c) {
|
||||
case 'a':
|
||||
all = 1;
|
||||
nargs++;
|
||||
break;
|
||||
case 'h':
|
||||
initiator_addr = strdup(optarg);
|
||||
if (initiator_addr == NULL)
|
||||
err(1, "%s: strdup", __func__);
|
||||
nargs++;
|
||||
break;
|
||||
case 'c':
|
||||
connection_id = strtoul(optarg, NULL, 0);
|
||||
nargs++;
|
||||
break;
|
||||
case 'i':
|
||||
initiator_name = strdup(optarg);
|
||||
if (initiator_name == NULL)
|
||||
err(1, "%s: strdup", __func__);
|
||||
nargs++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nargs == 0)
|
||||
errx(1, "%s: either -a, -h, -c, or -i must be specified",
|
||||
__func__);
|
||||
if (nargs > 1)
|
||||
errx(1, "%s: only one of -a, -h, -c, or -i may be specified",
|
||||
__func__);
|
||||
|
||||
bzero(&req, sizeof(req));
|
||||
req.type = CTL_ISCSI_TERMINATE;
|
||||
req.data.terminate.connection_id = connection_id;
|
||||
if (initiator_addr != NULL)
|
||||
strlcpy(req.data.terminate.initiator_addr,
|
||||
initiator_addr, sizeof(req.data.terminate.initiator_addr));
|
||||
if (initiator_name != NULL)
|
||||
strlcpy(req.data.terminate.initiator_name,
|
||||
initiator_name, sizeof(req.data.terminate.initiator_name));
|
||||
if (all != 0)
|
||||
req.data.terminate.all = 1;
|
||||
|
||||
if (ioctl(fd, CTL_ISCSI, &req) == -1) {
|
||||
warn("%s: error issuing CTL_ISCSI ioctl", __func__);
|
||||
retval = 1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
if (req.status != CTL_ISCSI_OK) {
|
||||
warnx("%s: error returned from CTL iSCSI connection "
|
||||
"termination request:\n%s", __func__, req.error_str);
|
||||
retval = 1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
printf("iSCSI connections terminated\n");
|
||||
|
||||
bailout:
|
||||
return (retval);
|
||||
}
|
||||
|
||||
/*
|
||||
* Name/value pair used for per-LUN attributes.
|
||||
@ -3713,6 +4120,9 @@ usage(int error)
|
||||
" [-s len fmt [args]] [-c] [-d delete_id]\n"
|
||||
" ctladm port <-l | -o <on|off> | [-w wwnn][-W wwpn]>\n"
|
||||
" [-p targ_port] [-t port_type] [-q] [-x]\n"
|
||||
" ctladm islist [-v | -x]\n"
|
||||
" ctladm islogout <-A | -a addr | -c connection-id | -n name>\n"
|
||||
" ctladm isterminate <-A | -a addr | -c connection-id | -n name>\n"
|
||||
" ctladm dumpooa\n"
|
||||
" ctladm dumpstructs\n"
|
||||
" ctladm help\n"
|
||||
@ -4093,6 +4503,15 @@ main(int argc, char **argv)
|
||||
case CTLADM_CMD_MODIFY:
|
||||
retval = cctl_modify_lun(fd, argc, argv, combinedopt);
|
||||
break;
|
||||
case CTLADM_CMD_ISLIST:
|
||||
retval = cctl_islist(fd, argc, argv, combinedopt);
|
||||
break;
|
||||
case CTLADM_CMD_ISLOGOUT:
|
||||
retval = cctl_islogout(fd, argc, argv, combinedopt);
|
||||
break;
|
||||
case CTLADM_CMD_ISTERMINATE:
|
||||
retval = cctl_isterminate(fd, argc, argv, combinedopt);
|
||||
break;
|
||||
case CTLADM_CMD_HELP:
|
||||
default:
|
||||
usage(retval);
|
||||
|
21
usr.sbin/ctld/Makefile
Normal file
21
usr.sbin/ctld/Makefile
Normal file
@ -0,0 +1,21 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= ctld
|
||||
SRCS= ctld.c discovery.c kernel.c keys.c log.c login.c parse.y pdu.c token.l y.tab.h
|
||||
CFLAGS+= -I${.CURDIR}
|
||||
CFLAGS+= -I${.CURDIR}/../../sys
|
||||
CFLAGS+= -I${.CURDIR}/../../sys/cam/ctl
|
||||
CFLAGS+= -I${.CURDIR}/../../sys/dev/iscsi
|
||||
#CFLAGS+= -DICL_KERNEL_PROXY
|
||||
MAN= ctld.8 ctl.conf.5
|
||||
|
||||
DPADD= ${LIBCAM} ${LIBSBUF} ${LIBBSDXML} ${LIBUTIL}
|
||||
LDADD= -lbsdxml -lcam -lcrypto -lfl -lsbuf -lssl -lutil
|
||||
|
||||
YFLAGS+= -v
|
||||
CLEANFILES= y.tab.c y.tab.h y.output
|
||||
|
||||
WARNS= 6
|
||||
NO_WMISSING_VARIABLE_DECLARATIONS=
|
||||
|
||||
.include <bsd.prog.mk>
|
244
usr.sbin/ctld/ctl.conf.5
Normal file
244
usr.sbin/ctld/ctl.conf.5
Normal file
@ -0,0 +1,244 @@
|
||||
.\" Copyright (c) 2012 The FreeBSD Foundation
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" 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 AUTHORS 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 AUTHORS 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.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd April 24, 2013
|
||||
.Dt CTL.CONF 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm ctl.conf
|
||||
.Nd CAM Target Layer / iSCSI target daemon configuration file
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
configuration file is used by the
|
||||
.Xr ctld 8
|
||||
daemon.
|
||||
Lines starting with
|
||||
.Ql #
|
||||
and empty lines are interpreted as comments.
|
||||
The general syntax of the
|
||||
.Nm
|
||||
file is:
|
||||
.Bd -literal -offset indent
|
||||
pidfile <path>
|
||||
|
||||
auth-group <name> {
|
||||
chap <user> <secret>
|
||||
...
|
||||
}
|
||||
|
||||
portal-group <name> {
|
||||
listen <address>
|
||||
listen-iser <address>
|
||||
discovery-auth-group <name>
|
||||
...
|
||||
}
|
||||
|
||||
target <name> {
|
||||
auth-group <name>
|
||||
portal-group <name>
|
||||
lun <number> {
|
||||
path <path>
|
||||
}
|
||||
...
|
||||
}
|
||||
.Ed
|
||||
.Ss global level
|
||||
The following statements are available at the global level:
|
||||
.Bl -tag -width indent
|
||||
.It Ic auth-group Aq Ar name
|
||||
Opens an auth-group section, defining an authentication group,
|
||||
which can then be assigned to any number of targets.
|
||||
.It Ic debug Aq Ar level
|
||||
Specifies debug level.
|
||||
The default is 0.
|
||||
.It Ic maxproc Aq Ar number
|
||||
Specifies limit for concurrently running child processes handling
|
||||
incoming connections.
|
||||
The default is 30.
|
||||
Setting it to 0 disables the limit.
|
||||
.It Ic pidfile Aq Ar path
|
||||
Specifies path to pidfile.
|
||||
The default is
|
||||
.Pa /var/run/ctld.pid .
|
||||
.It Ic portal-group Aq Ar name
|
||||
Opens a portal-group section, defining a portal group,
|
||||
which can then be assigned to any number of targets.
|
||||
.It Ic target Aq Ar name
|
||||
Opens a target configuration section.
|
||||
.It Ic timeout Aq Ar seconds
|
||||
Specifies timeout for login session, after which the connection
|
||||
will be forcibly terminated.
|
||||
The default is 60.
|
||||
Setting it to 0 disables the timeout.
|
||||
.El
|
||||
.Ss auth-grup level
|
||||
The following statements are available at the auth-group level:
|
||||
.Bl -tag -width indent
|
||||
.It Ic chap Ao Ar user Ac Aq Ar secret
|
||||
Specifies CHAP authentication credentials.
|
||||
.It Ic chap-mutual Ao Ar user Ac Ao Ar secret Ac Ao Ar mutualuser Ac Aq Ar mutualsecret
|
||||
Specifies mutual CHAP authentication credentials.
|
||||
Note that for any auth-group, configuration may contain either chap,
|
||||
or chap-mutual entries; it's an error to mix them.
|
||||
.El
|
||||
.Ss portal-group level
|
||||
The following statements are available at the portal-group level:
|
||||
.Bl -tag -width indent
|
||||
.It Ic discovery-auth-group Aq Ar name
|
||||
Assigns previously defined authentication group to that portal group,
|
||||
to be used for target discovery.
|
||||
By default, the discovery will be denied.
|
||||
A special auth-group, "no-authentication", may be used to allow for discovery
|
||||
without authentication.
|
||||
.It Ic listen Aq Ar address
|
||||
Specifies IPv4 or IPv6 address and port to listen on for incoming connections.
|
||||
.It Ic listen-iser Aq Ar address
|
||||
Specifies IPv4 or IPv6 address and port to listen on for incoming connections
|
||||
using iSER (iSCSI over RDMA) protocol.
|
||||
.El
|
||||
.Ss target level:
|
||||
The following statements are available at the target level:
|
||||
.Bl -tag -width indent
|
||||
.It Ic alias Aq Ar text
|
||||
Assigns human-readable description to that target.
|
||||
There is no default.
|
||||
.It Ic auth-group Aq Ar name
|
||||
Assigns previously defined authentication group to that target.
|
||||
There is no default; every target must use either auth-group,
|
||||
or chap, or chap-mutual statements.
|
||||
A special auth-group, "no-authentication", may be used to permit access
|
||||
without authentication.
|
||||
.It Ic chap Ao Ar user Ac Aq Ar secret
|
||||
Specifies CHAP authentication credentials.
|
||||
Note that targets must use either auth-group, or chap,
|
||||
or chap-mutual clauses; it's a configuration error to mix them in one target.
|
||||
.It Ic chap-mutual Ao Ar user Ac Ao Ar secret Ac Ao Ar mutualuser Ac Aq Ar mutualsecret
|
||||
Specifies mutual CHAP authentication credentials.
|
||||
Note that targets must use either auth-group, chap,
|
||||
chap-mutual clauses; it's a configuration error to mix them in one target.
|
||||
.It Ic portal-group Aq Ar name
|
||||
Assigns previously defined portal group to that target.
|
||||
Default portal group is "default", which makes the target available
|
||||
on TCP port 3260 on all configured IPv4 and IPv6 addresses.
|
||||
.It Ic lun Aq Ar number
|
||||
Opens a lun configuration section, defining LUN exported by a target.
|
||||
.El
|
||||
.Ss lun level
|
||||
The following statements are available at the lun level:
|
||||
.Bl -tag -width indent
|
||||
.It Ic backend Ao Ar block | Ar ramdisk Ac
|
||||
Specifies the CTL backend to use for a given LUN.
|
||||
Valid choices are
|
||||
.Dq block
|
||||
and
|
||||
.Dq ramdisk ;
|
||||
block is used for LUNs backed
|
||||
by files in the filesystem; ramdisk is a bitsink device, used mostly for
|
||||
testing.
|
||||
The default backend is block.
|
||||
.It Ic blocksize Aq Ar size
|
||||
Specifies blocksize visible to the initiator.
|
||||
The default blocksize is 512.
|
||||
.It Ic device-id Aq Ar string
|
||||
Specifies SCSI Device Identification string presented to the initiator.
|
||||
.It Ic option Ao Ar name Ac Aq Ar value
|
||||
Specifies CTL-specific options passed to the kernel.
|
||||
.It Ic path Aq Ar path
|
||||
Specifies path to file used to back the LUN.
|
||||
.It Ic serial Aq Ar string
|
||||
Specifies SCSI serial number presented to the initiator.
|
||||
.It Ic size Aq Ar size
|
||||
Specifies LUN size, in bytes.
|
||||
.El
|
||||
.Sh FILES
|
||||
.Bl -tag -width ".Pa /etc/ctl.conf" -compact
|
||||
.It Pa /etc/ctl.conf
|
||||
The default location of the
|
||||
.Xr ctld 8
|
||||
configuration file.
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
.Bd -literal
|
||||
pidfile /var/run/ctld.pid
|
||||
|
||||
auth-group example2 {
|
||||
chap-mutual "user" "secret" "mutualuser" "mutualsecret"
|
||||
chap-mutual "user2" "secret2" "mutualuser" "mutualsecret"
|
||||
}
|
||||
|
||||
portal-group example2 {
|
||||
discovery-auth-group no-authentication
|
||||
listen 127.0.0.1
|
||||
listen 0.0.0.0:3261
|
||||
listen [::]:3261
|
||||
listen [fe80::be:ef]
|
||||
}
|
||||
|
||||
target iqn.2012-06.com.example:target0 {
|
||||
alias "Testing target"
|
||||
auth-group no-authentication
|
||||
lun 0 {
|
||||
path /dev/zvol/example_0
|
||||
blocksize 4096
|
||||
size 4G
|
||||
}
|
||||
}
|
||||
|
||||
target iqn.2012-06.com.example:target3 {
|
||||
chap chapuser chapsecret
|
||||
lun 0 {
|
||||
path /dev/zvol/example_3
|
||||
}
|
||||
}
|
||||
|
||||
target iqn.2012-06.com.example:target2 {
|
||||
auth-group example2
|
||||
portal-group example2
|
||||
lun 0 {
|
||||
path /dev/zvol/example2_0
|
||||
}
|
||||
lun 1 {
|
||||
path /dev/zvol/example2_1
|
||||
option foo bar
|
||||
}
|
||||
}
|
||||
.Ed
|
||||
.Sh SEE ALSO
|
||||
.Xr ctl 4 ,
|
||||
.Xr ctladm 8 ,
|
||||
.Xr ctld 8
|
||||
.Sh AUTHORS
|
||||
The
|
||||
.Nm
|
||||
configuration file functionality for
|
||||
.Xr ctld 8
|
||||
was developed by
|
||||
.An Edward Tomasz Napierala Aq trasz@FreeBSD.org
|
||||
under sponsorship from the FreeBSD Foundation.
|
114
usr.sbin/ctld/ctld.8
Normal file
114
usr.sbin/ctld/ctld.8
Normal file
@ -0,0 +1,114 @@
|
||||
.\" Copyright (c) 2012 The FreeBSD Foundation
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" 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 AUTHORS 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 AUTHORS 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.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd September 20, 2012
|
||||
.Dt CTLD 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm ctld
|
||||
.Nd CAM Target Layer / iSCSI target daemon
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl d
|
||||
.Op Fl f Ar config-file
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
daemon is responsible for managing the CAM Target Layer configuration,
|
||||
accepting incoming iSCSI connections, performing authentication and
|
||||
passing connections to the kernel part of the native iSCSI target.
|
||||
.Pp
|
||||
.Pp
|
||||
Upon startup, the
|
||||
.Nm
|
||||
daemon parses the configuration file and exits, if it encounters any errors.
|
||||
Then it compares the configuration with the kernel list of LUNs managed
|
||||
by previously running
|
||||
.Nm
|
||||
instances, removes LUNs no longer existing in the configuration file,
|
||||
and creates new LUNs as neccessary.
|
||||
After that it listens for the incoming iSCSI connections, performs
|
||||
authentication, and, if successful, passes the connections to the kernel part
|
||||
of CTL iSCSI target, which handles it from that point.
|
||||
.Pp
|
||||
When it receives a SIGHUP signal, the
|
||||
.Nm
|
||||
reloads its configuration and applies the changes to the kernel.
|
||||
Changes are applied in a way that avoids unneccessary disruptions;
|
||||
for example removing one LUN does not affect other LUNs.
|
||||
.Pp
|
||||
When exiting gracefully, the
|
||||
.Nm
|
||||
daemon removes LUNs it managed and forcibly disconnects all the clients.
|
||||
Otherwise - e.g. when killed with SIGKILL - LUNs stay configured
|
||||
and clients remain connected.
|
||||
.Pp
|
||||
To perform administrative actions that apply to already connected
|
||||
sessions, such as forcing termination, use
|
||||
.Xr ctladm 8 .
|
||||
.Pp
|
||||
The following options are available:
|
||||
.Bl -tag -width ".Fl P Ar pidfile"
|
||||
.It Fl f Ar config-file
|
||||
Specifies the name of the configuration file.
|
||||
The default is
|
||||
.Pa /etc/ctl.conf .
|
||||
.It Fl d
|
||||
Debug mode.
|
||||
The server sends verbose debug output to standard error, and does not
|
||||
put itself in the background.
|
||||
The server will also not fork and will exit after processing one connection.
|
||||
This option is only intended for debugging the target.
|
||||
.El
|
||||
.Sh FILES
|
||||
.Bl -tag -width ".Pa /var/run/ctld.pid" -compact
|
||||
.It Pa /etc/ctl.conf
|
||||
The configuration file for
|
||||
.Nm .
|
||||
The file format and configuration options are described in
|
||||
.Xr ctl.conf 5 .
|
||||
.It Pa /var/run/ctld.pid
|
||||
The default location of the
|
||||
.Nm
|
||||
PID file.
|
||||
.El
|
||||
.Sh EXIT STATUS
|
||||
The
|
||||
.Nm
|
||||
utility exits 0 on success, and >0 if an error occurs.
|
||||
.Sh SEE ALSO
|
||||
.Xr ctl 4 ,
|
||||
.Xr ctl.conf 5 ,
|
||||
.Xr ctladm 8
|
||||
.Sh AUTHORS
|
||||
The
|
||||
.Nm
|
||||
was developed by
|
||||
.An Edward Tomasz Napierala Aq trasz@FreeBSD.org
|
||||
under sponsorship from the FreeBSD Foundation.
|
1715
usr.sbin/ctld/ctld.c
Normal file
1715
usr.sbin/ctld/ctld.c
Normal file
File diff suppressed because it is too large
Load Diff
277
usr.sbin/ctld/ctld.h
Normal file
277
usr.sbin/ctld/ctld.h
Normal file
@ -0,0 +1,277 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef CTLD_H
|
||||
#define CTLD_H
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <stdbool.h>
|
||||
#include <libutil.h>
|
||||
|
||||
#define DEFAULT_CONFIG_PATH "/etc/ctl.conf"
|
||||
#define DEFAULT_PIDFILE "/var/run/ctld.pid"
|
||||
#define DEFAULT_BLOCKSIZE 512
|
||||
|
||||
#define MAX_NAME_LEN 223
|
||||
#define MAX_DATA_SEGMENT_LENGTH (128 * 1024)
|
||||
#define MAX_BURST_LENGTH 16776192
|
||||
|
||||
struct auth {
|
||||
TAILQ_ENTRY(auth) a_next;
|
||||
struct auth_group *a_auth_group;
|
||||
char *a_user;
|
||||
char *a_secret;
|
||||
char *a_mutual_user;
|
||||
char *a_mutual_secret;
|
||||
};
|
||||
|
||||
#define AG_TYPE_UNKNOWN 0
|
||||
#define AG_TYPE_NO_AUTHENTICATION 1
|
||||
#define AG_TYPE_CHAP 2
|
||||
#define AG_TYPE_CHAP_MUTUAL 3
|
||||
|
||||
struct auth_group {
|
||||
TAILQ_ENTRY(auth_group) ag_next;
|
||||
struct conf *ag_conf;
|
||||
char *ag_name;
|
||||
struct target *ag_target;
|
||||
int ag_type;
|
||||
TAILQ_HEAD(, auth) ag_auths;
|
||||
};
|
||||
|
||||
struct portal {
|
||||
TAILQ_ENTRY(portal) p_next;
|
||||
struct portal_group *p_portal_group;
|
||||
bool p_iser;
|
||||
char *p_listen;
|
||||
struct addrinfo *p_ai;
|
||||
|
||||
TAILQ_HEAD(, target) p_targets;
|
||||
int p_socket;
|
||||
};
|
||||
|
||||
struct portal_group {
|
||||
TAILQ_ENTRY(portal_group) pg_next;
|
||||
struct conf *pg_conf;
|
||||
char *pg_name;
|
||||
struct auth_group *pg_discovery_auth_group;
|
||||
bool pg_unassigned;
|
||||
TAILQ_HEAD(, portal) pg_portals;
|
||||
|
||||
uint16_t pg_tag;
|
||||
};
|
||||
|
||||
struct lun_option {
|
||||
TAILQ_ENTRY(lun_option) lo_next;
|
||||
struct lun *lo_lun;
|
||||
char *lo_name;
|
||||
char *lo_value;
|
||||
};
|
||||
|
||||
struct lun {
|
||||
TAILQ_ENTRY(lun) l_next;
|
||||
TAILQ_HEAD(, lun_option) l_options;
|
||||
struct target *l_target;
|
||||
int l_lun;
|
||||
char *l_backend;
|
||||
int l_blocksize;
|
||||
char *l_device_id;
|
||||
char *l_path;
|
||||
char *l_serial;
|
||||
int64_t l_size;
|
||||
|
||||
int l_ctl_lun;
|
||||
};
|
||||
|
||||
struct target {
|
||||
TAILQ_ENTRY(target) t_next;
|
||||
TAILQ_HEAD(, lun) t_luns;
|
||||
struct conf *t_conf;
|
||||
struct auth_group *t_auth_group;
|
||||
struct portal_group *t_portal_group;
|
||||
char *t_iqn;
|
||||
char *t_alias;
|
||||
};
|
||||
|
||||
struct conf {
|
||||
char *conf_pidfile_path;
|
||||
TAILQ_HEAD(, target) conf_targets;
|
||||
TAILQ_HEAD(, auth_group) conf_auth_groups;
|
||||
TAILQ_HEAD(, portal_group) conf_portal_groups;
|
||||
int conf_debug;
|
||||
int conf_timeout;
|
||||
int conf_maxproc;
|
||||
|
||||
uint16_t conf_last_portal_group_tag;
|
||||
struct pidfh *conf_pidfh;
|
||||
};
|
||||
|
||||
#define CONN_SESSION_TYPE_NONE 0
|
||||
#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 portal *conn_portal;
|
||||
struct target *conn_target;
|
||||
int conn_socket;
|
||||
int conn_session_type;
|
||||
char *conn_initiator_name;
|
||||
char *conn_initiator_addr;
|
||||
char *conn_initiator_alias;
|
||||
uint32_t conn_cmdsn;
|
||||
uint32_t conn_statsn;
|
||||
size_t conn_max_data_segment_length;
|
||||
size_t conn_max_burst_length;
|
||||
int conn_immediate_data;
|
||||
int conn_header_digest;
|
||||
int conn_data_digest;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
struct conf *conf_new(void);
|
||||
struct conf *conf_new_from_file(const char *path);
|
||||
struct conf *conf_new_from_kernel(void);
|
||||
void conf_delete(struct conf *conf);
|
||||
int conf_verify(struct conf *conf);
|
||||
|
||||
struct auth_group *auth_group_new(struct conf *conf, const char *name);
|
||||
void auth_group_delete(struct auth_group *ag);
|
||||
struct auth_group *auth_group_find(struct conf *conf, const char *name);
|
||||
|
||||
const struct auth *auth_new_chap(struct auth_group *ag,
|
||||
const char *user, const char *secret);
|
||||
const struct auth *auth_new_chap_mutual(struct auth_group *ag,
|
||||
const char *user, const char *secret,
|
||||
const char *user2, const char *secret2);
|
||||
const struct auth *auth_find(struct auth_group *ag,
|
||||
const char *user);
|
||||
|
||||
struct portal_group *portal_group_new(struct conf *conf, const char *name);
|
||||
void portal_group_delete(struct portal_group *pg);
|
||||
struct portal_group *portal_group_find(struct conf *conf, const char *name);
|
||||
int portal_group_add_listen(struct portal_group *pg,
|
||||
const char *listen, bool iser);
|
||||
|
||||
struct target *target_new(struct conf *conf, const char *iqn);
|
||||
void target_delete(struct target *target);
|
||||
struct target *target_find(struct conf *conf,
|
||||
const char *iqn);
|
||||
|
||||
struct lun *lun_new(struct target *target, int lun_id);
|
||||
void lun_delete(struct lun *lun);
|
||||
struct lun *lun_find(struct target *target, int lun_id);
|
||||
void lun_set_backend(struct lun *lun, const char *value);
|
||||
void lun_set_blocksize(struct lun *lun, size_t value);
|
||||
void lun_set_device_id(struct lun *lun, const char *value);
|
||||
void lun_set_path(struct lun *lun, const char *value);
|
||||
void lun_set_serial(struct lun *lun, const char *value);
|
||||
void lun_set_size(struct lun *lun, size_t value);
|
||||
void lun_set_ctl_lun(struct lun *lun, uint32_t value);
|
||||
|
||||
struct lun_option *lun_option_new(struct lun *lun,
|
||||
const char *name, const char *value);
|
||||
void lun_option_delete(struct lun_option *clo);
|
||||
struct lun_option *lun_option_find(struct lun *lun, const char *name);
|
||||
void lun_option_set(struct lun_option *clo,
|
||||
const char *value);
|
||||
|
||||
void kernel_init(void);
|
||||
int kernel_lun_add(struct lun *lun);
|
||||
int kernel_lun_resize(struct lun *lun);
|
||||
int kernel_lun_remove(struct lun *lun);
|
||||
void kernel_handoff(struct connection *conn);
|
||||
int kernel_port_on(void);
|
||||
int kernel_port_off(void);
|
||||
void kernel_capsicate(void);
|
||||
|
||||
/*
|
||||
* ICL_KERNEL_PROXY
|
||||
*/
|
||||
void kernel_listen(struct addrinfo *ai, bool iser);
|
||||
int kernel_accept(void);
|
||||
void kernel_send(struct pdu *pdu);
|
||||
void kernel_receive(struct pdu *pdu);
|
||||
|
||||
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);
|
||||
int keys_find_int(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 *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 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 __printf0like(2, 3);
|
||||
void log_errx(int, const char *, ...)
|
||||
__dead2 __printf0like(2, 3);
|
||||
void log_warn(const char *, ...) __printf0like(1, 2);
|
||||
void log_warnx(const char *, ...) __printflike(1, 2);
|
||||
void log_debugx(const char *, ...) __printf0like(1, 2);
|
||||
|
||||
char *checked_strdup(const char *);
|
||||
bool valid_iscsi_name(const char *name);
|
||||
bool timed_out(void);
|
||||
|
||||
#endif /* !CTLD_H */
|
221
usr.sbin/ctld/discovery.c
Normal file
221
usr.sbin/ctld/discovery.c
Normal file
@ -0,0 +1,221 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "ctld.h"
|
||||
#include "iscsi_proto.h"
|
||||
|
||||
static struct pdu *
|
||||
text_receive(struct connection *conn)
|
||||
{
|
||||
struct pdu *request;
|
||||
struct iscsi_bhs_text_request *bhstr;
|
||||
|
||||
request = pdu_new(conn);
|
||||
pdu_receive(request);
|
||||
if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) !=
|
||||
ISCSI_BHS_OPCODE_TEXT_REQUEST)
|
||||
log_errx(1, "protocol error: received invalid opcode 0x%x",
|
||||
request->pdu_bhs->bhs_opcode);
|
||||
bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs;
|
||||
#if 0
|
||||
if ((bhstr->bhstr_flags & ISCSI_BHSTR_FLAGS_FINAL) == 0)
|
||||
log_errx(1, "received Text PDU without the \"F\" flag");
|
||||
#endif
|
||||
/*
|
||||
* XXX: Implement the C flag some day.
|
||||
*/
|
||||
if ((bhstr->bhstr_flags & BHSTR_FLAGS_CONTINUE) != 0)
|
||||
log_errx(1, "received Text PDU with unsupported \"C\" flag");
|
||||
if (request->pdu_data_len == 0)
|
||||
log_errx(1, "received Text PDU with empty data segment");
|
||||
|
||||
if (ntohl(bhstr->bhstr_cmdsn) < conn->conn_cmdsn) {
|
||||
log_errx(1, "received Text PDU with decreasing CmdSN: "
|
||||
"was %d, is %d", conn->conn_cmdsn, ntohl(bhstr->bhstr_cmdsn));
|
||||
}
|
||||
if (ntohl(bhstr->bhstr_expstatsn) != conn->conn_statsn) {
|
||||
log_errx(1, "received Text PDU with wrong StatSN: "
|
||||
"is %d, should be %d", ntohl(bhstr->bhstr_expstatsn),
|
||||
conn->conn_statsn);
|
||||
}
|
||||
conn->conn_cmdsn = ntohl(bhstr->bhstr_cmdsn);
|
||||
|
||||
return (request);
|
||||
}
|
||||
|
||||
static struct pdu *
|
||||
text_new_response(struct pdu *request)
|
||||
{
|
||||
struct pdu *response;
|
||||
struct connection *conn;
|
||||
struct iscsi_bhs_text_request *bhstr;
|
||||
struct iscsi_bhs_text_response *bhstr2;
|
||||
|
||||
bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs;
|
||||
conn = request->pdu_connection;
|
||||
|
||||
response = pdu_new_response(request);
|
||||
bhstr2 = (struct iscsi_bhs_text_response *)response->pdu_bhs;
|
||||
bhstr2->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_RESPONSE;
|
||||
bhstr2->bhstr_flags = BHSTR_FLAGS_FINAL;
|
||||
bhstr2->bhstr_lun = bhstr->bhstr_lun;
|
||||
bhstr2->bhstr_initiator_task_tag = bhstr->bhstr_initiator_task_tag;
|
||||
bhstr2->bhstr_target_transfer_tag = bhstr->bhstr_target_transfer_tag;
|
||||
bhstr2->bhstr_statsn = htonl(conn->conn_statsn++);
|
||||
bhstr2->bhstr_expcmdsn = htonl(conn->conn_cmdsn);
|
||||
bhstr2->bhstr_maxcmdsn = htonl(conn->conn_cmdsn);
|
||||
|
||||
return (response);
|
||||
}
|
||||
|
||||
static struct pdu *
|
||||
logout_receive(struct connection *conn)
|
||||
{
|
||||
struct pdu *request;
|
||||
struct iscsi_bhs_logout_request *bhslr;
|
||||
|
||||
request = pdu_new(conn);
|
||||
pdu_receive(request);
|
||||
if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) !=
|
||||
ISCSI_BHS_OPCODE_LOGOUT_REQUEST)
|
||||
log_errx(1, "protocol error: received invalid opcode 0x%x",
|
||||
request->pdu_bhs->bhs_opcode);
|
||||
bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs;
|
||||
if ((bhslr->bhslr_reason & 0x7f) != BHSLR_REASON_CLOSE_SESSION)
|
||||
log_debugx("received Logout PDU with invalid reason 0x%x; "
|
||||
"continuing anyway", bhslr->bhslr_reason & 0x7f);
|
||||
if (ntohl(bhslr->bhslr_cmdsn) < conn->conn_cmdsn) {
|
||||
log_errx(1, "received Logout PDU with decreasing CmdSN: "
|
||||
"was %d, is %d", conn->conn_cmdsn,
|
||||
ntohl(bhslr->bhslr_cmdsn));
|
||||
}
|
||||
if (ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) {
|
||||
log_errx(1, "received Logout PDU with wrong StatSN: "
|
||||
"is %d, should be %d", ntohl(bhslr->bhslr_expstatsn),
|
||||
conn->conn_statsn);
|
||||
}
|
||||
conn->conn_cmdsn = ntohl(bhslr->bhslr_cmdsn);
|
||||
|
||||
return (request);
|
||||
}
|
||||
|
||||
static struct pdu *
|
||||
logout_new_response(struct pdu *request)
|
||||
{
|
||||
struct pdu *response;
|
||||
struct connection *conn;
|
||||
struct iscsi_bhs_logout_request *bhslr;
|
||||
struct iscsi_bhs_logout_response *bhslr2;
|
||||
|
||||
bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs;
|
||||
conn = request->pdu_connection;
|
||||
|
||||
response = pdu_new_response(request);
|
||||
bhslr2 = (struct iscsi_bhs_logout_response *)response->pdu_bhs;
|
||||
bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_RESPONSE;
|
||||
bhslr2->bhslr_flags = 0x80;
|
||||
bhslr2->bhslr_response = BHSLR_RESPONSE_CLOSED_SUCCESSFULLY;
|
||||
bhslr2->bhslr_initiator_task_tag = bhslr->bhslr_initiator_task_tag;
|
||||
bhslr2->bhslr_statsn = htonl(conn->conn_statsn++);
|
||||
bhslr2->bhslr_expcmdsn = htonl(conn->conn_cmdsn);
|
||||
bhslr2->bhslr_maxcmdsn = htonl(conn->conn_cmdsn);
|
||||
|
||||
return (response);
|
||||
}
|
||||
|
||||
void
|
||||
discovery(struct connection *conn)
|
||||
{
|
||||
struct pdu *request, *response;
|
||||
struct keys *request_keys, *response_keys;
|
||||
struct target *targ;
|
||||
const char *send_targets;
|
||||
|
||||
log_debugx("beginning discovery session; waiting for Text PDU");
|
||||
request = text_receive(conn);
|
||||
request_keys = keys_new();
|
||||
keys_load(request_keys, request);
|
||||
|
||||
send_targets = keys_find(request_keys, "SendTargets");
|
||||
if (send_targets == NULL)
|
||||
log_errx(1, "received Text PDU without SendTargets");
|
||||
|
||||
response = text_new_response(request);
|
||||
response_keys = keys_new();
|
||||
|
||||
if (strcmp(send_targets, "All") == 0) {
|
||||
TAILQ_FOREACH(targ,
|
||||
&conn->conn_portal->p_portal_group->pg_conf->conf_targets,
|
||||
t_next) {
|
||||
if (targ->t_portal_group !=
|
||||
conn->conn_portal->p_portal_group) {
|
||||
log_debugx("not returning target \"%s\"; "
|
||||
"belongs to a different portal group",
|
||||
targ->t_iqn);
|
||||
continue;
|
||||
}
|
||||
keys_add(response_keys, "TargetName", targ->t_iqn);
|
||||
}
|
||||
} else {
|
||||
targ = target_find(conn->conn_portal->p_portal_group->pg_conf,
|
||||
send_targets);
|
||||
if (targ == NULL) {
|
||||
log_debugx("initiator requested information on unknown "
|
||||
"target \"%s\"; returning nothing", send_targets);
|
||||
} else {
|
||||
keys_add(response_keys, "TargetName", targ->t_iqn);
|
||||
}
|
||||
}
|
||||
keys_save(response_keys, response);
|
||||
|
||||
pdu_send(response);
|
||||
pdu_delete(response);
|
||||
keys_delete(response_keys);
|
||||
pdu_delete(request);
|
||||
keys_delete(request_keys);
|
||||
|
||||
log_debugx("done sending targets; waiting for Logout PDU");
|
||||
request = logout_receive(conn);
|
||||
response = logout_new_response(request);
|
||||
|
||||
pdu_send(response);
|
||||
pdu_delete(response);
|
||||
pdu_delete(request);
|
||||
|
||||
log_debugx("discovery session done");
|
||||
}
|
782
usr.sbin/ctld/kernel.c
Normal file
782
usr.sbin/ctld/kernel.c
Normal file
@ -0,0 +1,782 @@
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004 Silicon Graphics International Corp.
|
||||
* Copyright (c) 1997-2007 Kenneth D. Merry
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions of this software were 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,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/linker.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/callout.h>
|
||||
#include <sys/sbuf.h>
|
||||
#include <sys/capability.h>
|
||||
#include <assert.h>
|
||||
#include <bsdxml.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <cam/scsi/scsi_all.h>
|
||||
#include <cam/scsi/scsi_message.h>
|
||||
#include <cam/ctl/ctl.h>
|
||||
#include <cam/ctl/ctl_io.h>
|
||||
#include <cam/ctl/ctl_frontend_internal.h>
|
||||
#include <cam/ctl/ctl_backend.h>
|
||||
#include <cam/ctl/ctl_ioctl.h>
|
||||
#include <cam/ctl/ctl_backend_block.h>
|
||||
#include <cam/ctl/ctl_util.h>
|
||||
#include <cam/ctl/ctl_scsi_all.h>
|
||||
|
||||
#ifdef ICL_KERNEL_PROXY
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
#include "ctld.h"
|
||||
|
||||
static int ctl_fd = 0;
|
||||
|
||||
void
|
||||
kernel_init(void)
|
||||
{
|
||||
int retval, saved_errno;
|
||||
|
||||
ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR);
|
||||
if (ctl_fd < 0) {
|
||||
saved_errno = errno;
|
||||
retval = kldload("ctl");
|
||||
if (retval != -1)
|
||||
ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR);
|
||||
else
|
||||
errno = saved_errno;
|
||||
}
|
||||
if (ctl_fd < 0)
|
||||
log_err(1, "failed to open %s", CTL_DEFAULT_DEV);
|
||||
}
|
||||
|
||||
/*
|
||||
* Name/value pair used for per-LUN attributes.
|
||||
*/
|
||||
struct cctl_lun_nv {
|
||||
char *name;
|
||||
char *value;
|
||||
STAILQ_ENTRY(cctl_lun_nv) links;
|
||||
};
|
||||
|
||||
/*
|
||||
* Backend LUN information.
|
||||
*/
|
||||
struct cctl_lun {
|
||||
uint64_t lun_id;
|
||||
char *backend_type;
|
||||
uint64_t size_blocks;
|
||||
uint32_t blocksize;
|
||||
char *serial_number;
|
||||
char *device_id;
|
||||
char *cfiscsi_target;
|
||||
char *cfiscsi_target_alias;
|
||||
int cfiscsi_lun;
|
||||
STAILQ_HEAD(,cctl_lun_nv) attr_list;
|
||||
STAILQ_ENTRY(cctl_lun) links;
|
||||
};
|
||||
|
||||
struct cctl_devlist_data {
|
||||
int num_luns;
|
||||
STAILQ_HEAD(,cctl_lun) lun_list;
|
||||
struct cctl_lun *cur_lun;
|
||||
int level;
|
||||
struct sbuf *cur_sb[32];
|
||||
};
|
||||
|
||||
static void
|
||||
cctl_start_element(void *user_data, const char *name, const char **attr)
|
||||
{
|
||||
int i;
|
||||
struct cctl_devlist_data *devlist;
|
||||
struct cctl_lun *cur_lun;
|
||||
|
||||
devlist = (struct cctl_devlist_data *)user_data;
|
||||
cur_lun = devlist->cur_lun;
|
||||
devlist->level++;
|
||||
if ((u_int)devlist->level > (sizeof(devlist->cur_sb) /
|
||||
sizeof(devlist->cur_sb[0])))
|
||||
log_errx(1, "%s: too many nesting levels, %zd max", __func__,
|
||||
sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0]));
|
||||
|
||||
devlist->cur_sb[devlist->level] = sbuf_new_auto();
|
||||
if (devlist->cur_sb[devlist->level] == NULL)
|
||||
log_err(1, "%s: unable to allocate sbuf", __func__);
|
||||
|
||||
if (strcmp(name, "lun") == 0) {
|
||||
if (cur_lun != NULL)
|
||||
log_errx(1, "%s: improper lun element nesting",
|
||||
__func__);
|
||||
|
||||
cur_lun = calloc(1, sizeof(*cur_lun));
|
||||
if (cur_lun == NULL)
|
||||
log_err(1, "%s: cannot allocate %zd bytes", __func__,
|
||||
sizeof(*cur_lun));
|
||||
|
||||
devlist->num_luns++;
|
||||
devlist->cur_lun = cur_lun;
|
||||
|
||||
STAILQ_INIT(&cur_lun->attr_list);
|
||||
STAILQ_INSERT_TAIL(&devlist->lun_list, cur_lun, links);
|
||||
|
||||
for (i = 0; attr[i] != NULL; i += 2) {
|
||||
if (strcmp(attr[i], "id") == 0) {
|
||||
cur_lun->lun_id = strtoull(attr[i+1], NULL, 0);
|
||||
} else {
|
||||
log_errx(1, "%s: invalid LUN attribute %s = %s",
|
||||
__func__, attr[i], attr[i+1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cctl_end_element(void *user_data, const char *name)
|
||||
{
|
||||
struct cctl_devlist_data *devlist;
|
||||
struct cctl_lun *cur_lun;
|
||||
char *str;
|
||||
|
||||
devlist = (struct cctl_devlist_data *)user_data;
|
||||
cur_lun = devlist->cur_lun;
|
||||
|
||||
if ((cur_lun == NULL)
|
||||
&& (strcmp(name, "ctllunlist") != 0))
|
||||
log_errx(1, "%s: cur_lun == NULL! (name = %s)", __func__, name);
|
||||
|
||||
if (devlist->cur_sb[devlist->level] == NULL)
|
||||
log_errx(1, "%s: no valid sbuf at level %d (name %s)", __func__,
|
||||
devlist->level, name);
|
||||
|
||||
sbuf_finish(devlist->cur_sb[devlist->level]);
|
||||
str = checked_strdup(sbuf_data(devlist->cur_sb[devlist->level]));
|
||||
|
||||
if (strlen(str) == 0) {
|
||||
free(str);
|
||||
str = NULL;
|
||||
}
|
||||
|
||||
sbuf_delete(devlist->cur_sb[devlist->level]);
|
||||
devlist->cur_sb[devlist->level] = NULL;
|
||||
devlist->level--;
|
||||
|
||||
if (strcmp(name, "backend_type") == 0) {
|
||||
cur_lun->backend_type = str;
|
||||
str = NULL;
|
||||
} else if (strcmp(name, "size") == 0) {
|
||||
cur_lun->size_blocks = strtoull(str, NULL, 0);
|
||||
} else if (strcmp(name, "blocksize") == 0) {
|
||||
cur_lun->blocksize = strtoul(str, NULL, 0);
|
||||
} else if (strcmp(name, "serial_number") == 0) {
|
||||
cur_lun->serial_number = str;
|
||||
str = NULL;
|
||||
} else if (strcmp(name, "device_id") == 0) {
|
||||
cur_lun->device_id = str;
|
||||
str = NULL;
|
||||
} else if (strcmp(name, "cfiscsi_target") == 0) {
|
||||
cur_lun->cfiscsi_target = str;
|
||||
str = NULL;
|
||||
} else if (strcmp(name, "cfiscsi_target_alias") == 0) {
|
||||
cur_lun->cfiscsi_target_alias = str;
|
||||
str = NULL;
|
||||
} else if (strcmp(name, "cfiscsi_lun") == 0) {
|
||||
cur_lun->cfiscsi_lun = strtoul(str, NULL, 0);
|
||||
} else if (strcmp(name, "lun") == 0) {
|
||||
devlist->cur_lun = NULL;
|
||||
} else if (strcmp(name, "ctllunlist") == 0) {
|
||||
|
||||
} else {
|
||||
struct cctl_lun_nv *nv;
|
||||
|
||||
nv = calloc(1, sizeof(*nv));
|
||||
if (nv == NULL)
|
||||
log_err(1, "%s: can't allocate %zd bytes for nv pair",
|
||||
__func__, sizeof(*nv));
|
||||
|
||||
nv->name = checked_strdup(name);
|
||||
|
||||
nv->value = str;
|
||||
str = NULL;
|
||||
STAILQ_INSERT_TAIL(&cur_lun->attr_list, nv, links);
|
||||
}
|
||||
|
||||
free(str);
|
||||
}
|
||||
|
||||
static void
|
||||
cctl_char_handler(void *user_data, const XML_Char *str, int len)
|
||||
{
|
||||
struct cctl_devlist_data *devlist;
|
||||
|
||||
devlist = (struct cctl_devlist_data *)user_data;
|
||||
|
||||
sbuf_bcat(devlist->cur_sb[devlist->level], str, len);
|
||||
}
|
||||
|
||||
struct conf *
|
||||
conf_new_from_kernel(void)
|
||||
{
|
||||
struct conf *conf = NULL;
|
||||
struct target *targ;
|
||||
struct lun *cl;
|
||||
struct lun_option *lo;
|
||||
struct ctl_lun_list list;
|
||||
struct cctl_devlist_data devlist;
|
||||
struct cctl_lun *lun;
|
||||
XML_Parser parser;
|
||||
char *lun_str = NULL;
|
||||
int lun_len;
|
||||
int retval;
|
||||
|
||||
lun_len = 4096;
|
||||
|
||||
bzero(&devlist, sizeof(devlist));
|
||||
STAILQ_INIT(&devlist.lun_list);
|
||||
|
||||
log_debugx("obtaining previously configured CTL luns from the kernel");
|
||||
|
||||
retry:
|
||||
lun_str = realloc(lun_str, lun_len);
|
||||
if (lun_str == NULL)
|
||||
log_err(1, "realloc");
|
||||
|
||||
bzero(&list, sizeof(list));
|
||||
list.alloc_len = lun_len;
|
||||
list.status = CTL_LUN_LIST_NONE;
|
||||
list.lun_xml = lun_str;
|
||||
|
||||
if (ioctl(ctl_fd, CTL_LUN_LIST, &list) == -1) {
|
||||
log_warn("error issuing CTL_LUN_LIST ioctl");
|
||||
free(lun_str);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (list.status == CTL_LUN_LIST_ERROR) {
|
||||
log_warnx("error returned from CTL_LUN_LIST ioctl: %s",
|
||||
list.error_str);
|
||||
free(lun_str);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) {
|
||||
lun_len = lun_len << 1;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
parser = XML_ParserCreate(NULL);
|
||||
if (parser == NULL) {
|
||||
log_warnx("unable to create XML parser");
|
||||
free(lun_str);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
XML_SetUserData(parser, &devlist);
|
||||
XML_SetElementHandler(parser, cctl_start_element, cctl_end_element);
|
||||
XML_SetCharacterDataHandler(parser, cctl_char_handler);
|
||||
|
||||
retval = XML_Parse(parser, lun_str, strlen(lun_str), 1);
|
||||
XML_ParserFree(parser);
|
||||
free(lun_str);
|
||||
if (retval != 1) {
|
||||
log_warnx("XML_Parse failed");
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
conf = conf_new();
|
||||
|
||||
STAILQ_FOREACH(lun, &devlist.lun_list, links) {
|
||||
struct cctl_lun_nv *nv;
|
||||
|
||||
if (lun->cfiscsi_target == NULL) {
|
||||
log_debugx("CTL lun %ju wasn't managed by ctld; "
|
||||
"ignoring", (uintmax_t)lun->lun_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
targ = target_find(conf, lun->cfiscsi_target);
|
||||
if (targ == NULL) {
|
||||
#if 0
|
||||
log_debugx("found new kernel target %s for CTL lun %ld",
|
||||
lun->cfiscsi_target, lun->lun_id);
|
||||
#endif
|
||||
targ = target_new(conf, lun->cfiscsi_target);
|
||||
if (targ == NULL) {
|
||||
log_warnx("target_new failed");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
cl = lun_find(targ, lun->cfiscsi_lun);
|
||||
if (cl != NULL) {
|
||||
log_warnx("found CTL lun %ju, backing lun %d, target "
|
||||
"%s, also backed by CTL lun %d; ignoring",
|
||||
(uintmax_t) lun->lun_id, cl->l_lun,
|
||||
cl->l_target->t_iqn, cl->l_ctl_lun);
|
||||
continue;
|
||||
}
|
||||
|
||||
log_debugx("found CTL lun %ju, backing lun %d, target %s",
|
||||
(uintmax_t)lun->lun_id, lun->cfiscsi_lun, lun->cfiscsi_target);
|
||||
|
||||
cl = lun_new(targ, lun->cfiscsi_lun);
|
||||
if (cl == NULL) {
|
||||
log_warnx("lun_new failed");
|
||||
continue;
|
||||
}
|
||||
lun_set_backend(cl, lun->backend_type);
|
||||
lun_set_blocksize(cl, lun->blocksize);
|
||||
lun_set_device_id(cl, lun->device_id);
|
||||
lun_set_serial(cl, lun->serial_number);
|
||||
lun_set_size(cl, lun->size_blocks * cl->l_blocksize);
|
||||
lun_set_ctl_lun(cl, lun->lun_id);
|
||||
|
||||
STAILQ_FOREACH(nv, &lun->attr_list, links) {
|
||||
if (strcmp(nv->name, "file") == 0 ||
|
||||
strcmp(nv->name, "dev") == 0) {
|
||||
lun_set_path(cl, nv->value);
|
||||
continue;
|
||||
}
|
||||
lo = lun_option_new(cl, nv->name, nv->value);
|
||||
if (lo == NULL)
|
||||
log_warnx("unable to add CTL lun option %s "
|
||||
"for CTL lun %ju for lun %d, target %s",
|
||||
nv->name, (uintmax_t) lun->lun_id,
|
||||
cl->l_lun, cl->l_target->t_iqn);
|
||||
}
|
||||
}
|
||||
|
||||
return (conf);
|
||||
}
|
||||
|
||||
int
|
||||
kernel_lun_add(struct lun *lun)
|
||||
{
|
||||
struct lun_option *lo;
|
||||
struct ctl_lun_req req;
|
||||
char *tmp;
|
||||
int error, i, num_options;
|
||||
|
||||
bzero(&req, sizeof(req));
|
||||
|
||||
strlcpy(req.backend, lun->l_backend, sizeof(req.backend));
|
||||
req.reqtype = CTL_LUNREQ_CREATE;
|
||||
|
||||
req.reqdata.create.blocksize_bytes = lun->l_blocksize;
|
||||
|
||||
if (lun->l_size != 0)
|
||||
req.reqdata.create.lun_size_bytes = lun->l_size;
|
||||
|
||||
req.reqdata.create.flags |= CTL_LUN_FLAG_DEV_TYPE;
|
||||
req.reqdata.create.device_type = T_DIRECT;
|
||||
|
||||
if (lun->l_serial != NULL) {
|
||||
strlcpy(req.reqdata.create.serial_num, lun->l_serial,
|
||||
sizeof(req.reqdata.create.serial_num));
|
||||
req.reqdata.create.flags |= CTL_LUN_FLAG_SERIAL_NUM;
|
||||
}
|
||||
|
||||
if (lun->l_device_id != NULL) {
|
||||
strlcpy(req.reqdata.create.device_id, lun->l_device_id,
|
||||
sizeof(req.reqdata.create.device_id));
|
||||
req.reqdata.create.flags |= CTL_LUN_FLAG_DEVID;
|
||||
}
|
||||
|
||||
if (lun->l_path != NULL) {
|
||||
lo = lun_option_find(lun, "file");
|
||||
if (lo != NULL) {
|
||||
lun_option_set(lo, lun->l_path);
|
||||
} else {
|
||||
lo = lun_option_new(lun, "file", lun->l_path);
|
||||
assert(lo != NULL);
|
||||
}
|
||||
}
|
||||
|
||||
lo = lun_option_find(lun, "cfiscsi_target");
|
||||
if (lo != NULL) {
|
||||
lun_option_set(lo, lun->l_target->t_iqn);
|
||||
} else {
|
||||
lo = lun_option_new(lun, "cfiscsi_target",
|
||||
lun->l_target->t_iqn);
|
||||
assert(lo != NULL);
|
||||
}
|
||||
|
||||
if (lun->l_target->t_alias != NULL) {
|
||||
lo = lun_option_find(lun, "cfiscsi_target_alias");
|
||||
if (lo != NULL) {
|
||||
lun_option_set(lo, lun->l_target->t_alias);
|
||||
} else {
|
||||
lo = lun_option_new(lun, "cfiscsi_target_alias",
|
||||
lun->l_target->t_alias);
|
||||
assert(lo != NULL);
|
||||
}
|
||||
}
|
||||
|
||||
asprintf(&tmp, "%d", lun->l_lun);
|
||||
if (tmp == NULL)
|
||||
log_errx(1, "asprintf");
|
||||
lo = lun_option_find(lun, "cfiscsi_lun");
|
||||
if (lo != NULL) {
|
||||
lun_option_set(lo, tmp);
|
||||
free(tmp);
|
||||
} else {
|
||||
lo = lun_option_new(lun, "cfiscsi_lun", tmp);
|
||||
free(tmp);
|
||||
assert(lo != NULL);
|
||||
}
|
||||
|
||||
num_options = 0;
|
||||
TAILQ_FOREACH(lo, &lun->l_options, lo_next)
|
||||
num_options++;
|
||||
|
||||
req.num_be_args = num_options;
|
||||
if (num_options > 0) {
|
||||
req.be_args = malloc(num_options * sizeof(*req.be_args));
|
||||
if (req.be_args == NULL) {
|
||||
log_warn("error allocating %zd bytes",
|
||||
num_options * sizeof(*req.be_args));
|
||||
return (1);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
TAILQ_FOREACH(lo, &lun->l_options, lo_next) {
|
||||
/*
|
||||
* +1 for the terminating '\0'
|
||||
*/
|
||||
req.be_args[i].namelen = strlen(lo->lo_name) + 1;
|
||||
req.be_args[i].name = lo->lo_name;
|
||||
req.be_args[i].vallen = strlen(lo->lo_value) + 1;
|
||||
req.be_args[i].value = lo->lo_value;
|
||||
req.be_args[i].flags = CTL_BEARG_ASCII | CTL_BEARG_RD;
|
||||
i++;
|
||||
}
|
||||
assert(i == num_options);
|
||||
}
|
||||
|
||||
error = ioctl(ctl_fd, CTL_LUN_REQ, &req);
|
||||
free(req.be_args);
|
||||
if (error != 0) {
|
||||
log_warn("error issuing CTL_LUN_REQ ioctl");
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (req.status == CTL_LUN_ERROR) {
|
||||
log_warnx("error returned from LUN creation request: %s",
|
||||
req.error_str);
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (req.status != CTL_LUN_OK) {
|
||||
log_warnx("unknown LUN creation request status %d",
|
||||
req.status);
|
||||
return (1);
|
||||
}
|
||||
|
||||
lun_set_ctl_lun(lun, req.reqdata.create.req_lun_id);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
kernel_lun_resize(struct lun *lun)
|
||||
{
|
||||
struct ctl_lun_req req;
|
||||
|
||||
bzero(&req, sizeof(req));
|
||||
|
||||
strlcpy(req.backend, lun->l_backend, sizeof(req.backend));
|
||||
req.reqtype = CTL_LUNREQ_MODIFY;
|
||||
|
||||
req.reqdata.modify.lun_id = lun->l_ctl_lun;
|
||||
req.reqdata.modify.lun_size_bytes = lun->l_size;
|
||||
|
||||
if (ioctl(ctl_fd, CTL_LUN_REQ, &req) == -1) {
|
||||
log_warn("error issuing CTL_LUN_REQ ioctl");
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (req.status == CTL_LUN_ERROR) {
|
||||
log_warnx("error returned from LUN modification request: %s",
|
||||
req.error_str);
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (req.status != CTL_LUN_OK) {
|
||||
log_warnx("unknown LUN modification request status %d",
|
||||
req.status);
|
||||
return (1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
kernel_lun_remove(struct lun *lun)
|
||||
{
|
||||
struct ctl_lun_req req;
|
||||
|
||||
bzero(&req, sizeof(req));
|
||||
|
||||
strlcpy(req.backend, lun->l_backend, sizeof(req.backend));
|
||||
req.reqtype = CTL_LUNREQ_RM;
|
||||
|
||||
req.reqdata.rm.lun_id = lun->l_ctl_lun;
|
||||
|
||||
if (ioctl(ctl_fd, CTL_LUN_REQ, &req) == -1) {
|
||||
log_warn("error issuing CTL_LUN_REQ ioctl");
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (req.status == CTL_LUN_ERROR) {
|
||||
log_warnx("error returned from LUN removal request: %s",
|
||||
req.error_str);
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (req.status != CTL_LUN_OK) {
|
||||
log_warnx("unknown LUN removal request status %d", req.status);
|
||||
return (1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
kernel_handoff(struct connection *conn)
|
||||
{
|
||||
struct ctl_iscsi req;
|
||||
|
||||
bzero(&req, sizeof(req));
|
||||
|
||||
req.type = CTL_ISCSI_HANDOFF;
|
||||
strlcpy(req.data.handoff.initiator_name,
|
||||
conn->conn_initiator_name, sizeof(req.data.handoff.initiator_name));
|
||||
strlcpy(req.data.handoff.initiator_addr,
|
||||
conn->conn_initiator_addr, sizeof(req.data.handoff.initiator_addr));
|
||||
if (conn->conn_initiator_alias != NULL) {
|
||||
strlcpy(req.data.handoff.initiator_alias,
|
||||
conn->conn_initiator_alias, sizeof(req.data.handoff.initiator_alias));
|
||||
}
|
||||
strlcpy(req.data.handoff.target_name,
|
||||
conn->conn_target->t_iqn, sizeof(req.data.handoff.target_name));
|
||||
req.data.handoff.socket = conn->conn_socket;
|
||||
req.data.handoff.portal_group_tag =
|
||||
conn->conn_portal->p_portal_group->pg_tag;
|
||||
if (conn->conn_header_digest == CONN_DIGEST_CRC32C)
|
||||
req.data.handoff.header_digest = CTL_ISCSI_DIGEST_CRC32C;
|
||||
if (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.max_recv_data_segment_length =
|
||||
conn->conn_max_data_segment_length;
|
||||
req.data.handoff.max_burst_length = conn->conn_max_burst_length;
|
||||
req.data.handoff.immediate_data = conn->conn_immediate_data;
|
||||
|
||||
if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1)
|
||||
log_err(1, "error issuing CTL_ISCSI ioctl; "
|
||||
"dropping connection");
|
||||
|
||||
if (req.status != CTL_ISCSI_OK)
|
||||
log_errx(1, "error returned from CTL iSCSI handoff request: "
|
||||
"%s; dropping connection", req.error_str);
|
||||
}
|
||||
|
||||
int
|
||||
kernel_port_on(void)
|
||||
{
|
||||
struct ctl_port_entry entry;
|
||||
int error;
|
||||
|
||||
bzero(&entry, sizeof(&entry));
|
||||
|
||||
entry.port_type = CTL_PORT_ISCSI;
|
||||
entry.targ_port = -1;
|
||||
|
||||
error = ioctl(ctl_fd, CTL_ENABLE_PORT, &entry);
|
||||
if (error != 0) {
|
||||
log_warn("CTL_ENABLE_PORT ioctl failed");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
kernel_port_off(void)
|
||||
{
|
||||
struct ctl_port_entry entry;
|
||||
int error;
|
||||
|
||||
bzero(&entry, sizeof(&entry));
|
||||
|
||||
entry.port_type = CTL_PORT_ISCSI;
|
||||
entry.targ_port = -1;
|
||||
|
||||
error = ioctl(ctl_fd, CTL_DISABLE_PORT, &entry);
|
||||
if (error != 0) {
|
||||
log_warn("CTL_DISABLE_PORT ioctl failed");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
#ifdef ICL_KERNEL_PROXY
|
||||
void
|
||||
kernel_listen(struct addrinfo *ai, bool iser)
|
||||
{
|
||||
struct ctl_iscsi req;
|
||||
|
||||
bzero(&req, sizeof(req));
|
||||
|
||||
req.type = CTL_ISCSI_LISTEN;
|
||||
req.data.listen.iser = iser;
|
||||
req.data.listen.domain = ai->ai_family;
|
||||
req.data.listen.socktype = ai->ai_socktype;
|
||||
req.data.listen.protocol = ai->ai_protocol;
|
||||
req.data.listen.addr = ai->ai_addr;
|
||||
req.data.listen.addrlen = ai->ai_addrlen;
|
||||
|
||||
if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1)
|
||||
log_warn("error issuing CTL_ISCSI_LISTEN ioctl");
|
||||
}
|
||||
|
||||
int
|
||||
kernel_accept(void)
|
||||
{
|
||||
struct ctl_iscsi req;
|
||||
|
||||
bzero(&req, sizeof(req));
|
||||
|
||||
req.type = CTL_ISCSI_ACCEPT;
|
||||
|
||||
if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) {
|
||||
log_warn("error issuing CTL_ISCSI_LISTEN ioctl");
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (req.data.accept.connection_id);
|
||||
}
|
||||
|
||||
void
|
||||
kernel_send(struct pdu *pdu)
|
||||
{
|
||||
struct ctl_iscsi req;
|
||||
|
||||
bzero(&req, sizeof(req));
|
||||
|
||||
req.type = CTL_ISCSI_SEND;
|
||||
req.data.send.connection_id = pdu->pdu_connection->conn_socket;
|
||||
req.data.send.bhs = pdu->pdu_bhs;
|
||||
req.data.send.data_segment_len = pdu->pdu_data_len;
|
||||
req.data.send.data_segment = pdu->pdu_data;
|
||||
|
||||
if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1)
|
||||
log_err(1, "error issuing CTL_ISCSI ioctl; "
|
||||
"dropping connection");
|
||||
|
||||
if (req.status != CTL_ISCSI_OK)
|
||||
log_errx(1, "error returned from CTL iSCSI send: "
|
||||
"%s; dropping connection", req.error_str);
|
||||
}
|
||||
|
||||
void
|
||||
kernel_receive(struct pdu *pdu)
|
||||
{
|
||||
struct ctl_iscsi req;
|
||||
|
||||
pdu->pdu_data = malloc(MAX_DATA_SEGMENT_LENGTH);
|
||||
if (pdu->pdu_data == NULL)
|
||||
log_err(1, "malloc");
|
||||
|
||||
bzero(&req, sizeof(req));
|
||||
|
||||
req.type = CTL_ISCSI_RECEIVE;
|
||||
req.data.receive.connection_id = pdu->pdu_connection->conn_socket;
|
||||
req.data.receive.bhs = pdu->pdu_bhs;
|
||||
req.data.receive.data_segment_len = MAX_DATA_SEGMENT_LENGTH;
|
||||
req.data.receive.data_segment = pdu->pdu_data;
|
||||
|
||||
if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1)
|
||||
log_err(1, "error issuing CTL_ISCSI ioctl; "
|
||||
"dropping connection");
|
||||
|
||||
if (req.status != CTL_ISCSI_OK)
|
||||
log_errx(1, "error returned from CTL iSCSI receive: "
|
||||
"%s; dropping connection", req.error_str);
|
||||
|
||||
}
|
||||
|
||||
#endif /* ICL_KERNEL_PROXY */
|
||||
|
||||
/*
|
||||
* XXX: I CANT INTO LATIN
|
||||
*/
|
||||
void
|
||||
kernel_capsicate(void)
|
||||
{
|
||||
int error;
|
||||
cap_rights_t rights;
|
||||
const unsigned long cmds[] = { CTL_ISCSI };
|
||||
|
||||
cap_rights_init(&rights, CAP_IOCTL);
|
||||
error = cap_rights_limit(ctl_fd, &rights);
|
||||
if (error != 0 && errno != ENOSYS)
|
||||
log_err(1, "cap_rights_limit");
|
||||
|
||||
error = cap_ioctls_limit(ctl_fd, cmds,
|
||||
sizeof(cmds) / sizeof(cmds[0]));
|
||||
if (error != 0 && errno != ENOSYS)
|
||||
log_err(1, "cap_ioctls_limit");
|
||||
|
||||
error = cap_enter();
|
||||
if (error != 0 && errno != ENOSYS)
|
||||
log_err(1, "cap_enter");
|
||||
|
||||
if (cap_sandboxed())
|
||||
log_debugx("Capsicum capability mode enabled");
|
||||
else
|
||||
log_warnx("Capsicum capability mode not supported");
|
||||
}
|
||||
|
216
usr.sbin/ctld/keys.c
Normal file
216
usr.sbin/ctld/keys.c
Normal file
@ -0,0 +1,216 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "ctld.h"
|
||||
|
||||
struct keys *
|
||||
keys_new(void)
|
||||
{
|
||||
struct keys *keys;
|
||||
|
||||
keys = calloc(sizeof(*keys), 1);
|
||||
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)
|
||||
log_errx(1, "protocol error: empty data segment");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int
|
||||
keys_find_int(struct keys *keys, const char *name)
|
||||
{
|
||||
const char *str;
|
||||
char *endptr;
|
||||
int num;
|
||||
|
||||
str = keys_find(keys, name);
|
||||
if (str == NULL)
|
||||
return (-1);
|
||||
|
||||
num = strtoul(str, &endptr, 10);
|
||||
if (*endptr != '\0') {
|
||||
log_debugx("invalid numeric value \"%s\"", str);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (num);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
196
usr.sbin/ctld/log.c
Normal file
196
usr.sbin/ctld/log.c
Normal file
@ -0,0 +1,196 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <vis.h>
|
||||
|
||||
#include "ctld.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];
|
||||
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 {
|
||||
if (peer_name != NULL) {
|
||||
fprintf(stderr, "%s: %s (%s): %s: %s\n", getprogname(),
|
||||
peer_addr, peer_name, msgbuf_strvised, strerror(errno));
|
||||
syslog(priority, "%s (%s): %s: %s",
|
||||
peer_addr, peer_name, msgbuf_strvised, strerror(errno));
|
||||
} else if (peer_addr != NULL) {
|
||||
fprintf(stderr, "%s: %s: %s: %s\n", getprogname(),
|
||||
peer_addr, msgbuf_strvised, strerror(errno));
|
||||
syslog(priority, "%s: %s: %s",
|
||||
peer_addr, msgbuf_strvised, strerror(errno));
|
||||
} else {
|
||||
fprintf(stderr, "%s: %s: %s\n", getprogname(),
|
||||
msgbuf_strvised, strerror(errno));
|
||||
syslog(priority, "%s: %s",
|
||||
msgbuf_strvised, strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
1051
usr.sbin/ctld/login.c
Normal file
1051
usr.sbin/ctld/login.c
Normal file
File diff suppressed because it is too large
Load Diff
621
usr.sbin/ctld/parse.y
Normal file
621
usr.sbin/ctld/parse.y
Normal file
@ -0,0 +1,621 @@
|
||||
%{
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ctld.h"
|
||||
|
||||
extern FILE *yyin;
|
||||
extern char *yytext;
|
||||
extern int lineno;
|
||||
|
||||
static struct conf *conf = NULL;
|
||||
static struct auth_group *auth_group = NULL;
|
||||
static struct portal_group *portal_group = NULL;
|
||||
static struct target *target = NULL;
|
||||
static struct lun *lun = NULL;
|
||||
|
||||
extern void yyerror(const char *);
|
||||
extern int yylex(void);
|
||||
extern void yyrestart(FILE *);
|
||||
|
||||
%}
|
||||
|
||||
%token ALIAS AUTH_GROUP BACKEND BLOCKSIZE CHAP CHAP_MUTUAL CLOSING_BRACKET
|
||||
%token DEBUG DEVICE_ID DISCOVERY_AUTH_GROUP LISTEN LISTEN_ISER LUN MAXPROC NUM
|
||||
%token OPENING_BRACKET OPTION PATH PIDFILE PORTAL_GROUP SERIAL SIZE STR TARGET
|
||||
%token TIMEOUT
|
||||
|
||||
%union
|
||||
{
|
||||
uint64_t num;
|
||||
char *str;
|
||||
}
|
||||
|
||||
%token <num> NUM
|
||||
%token <str> STR
|
||||
|
||||
%%
|
||||
|
||||
statements:
|
||||
|
|
||||
statements statement
|
||||
;
|
||||
|
||||
statement:
|
||||
debug_statement
|
||||
|
|
||||
timeout_statement
|
||||
|
|
||||
maxproc_statement
|
||||
|
|
||||
pidfile_statement
|
||||
|
|
||||
auth_group_definition
|
||||
|
|
||||
portal_group_definition
|
||||
|
|
||||
target_statement
|
||||
;
|
||||
|
||||
debug_statement: DEBUG NUM
|
||||
{
|
||||
conf->conf_debug = $2;
|
||||
}
|
||||
;
|
||||
|
||||
timeout_statement: TIMEOUT NUM
|
||||
{
|
||||
conf->conf_timeout = $2;
|
||||
}
|
||||
;
|
||||
|
||||
maxproc_statement: MAXPROC NUM
|
||||
{
|
||||
conf->conf_maxproc = $2;
|
||||
}
|
||||
;
|
||||
|
||||
pidfile_statement: PIDFILE STR
|
||||
{
|
||||
if (conf->conf_pidfile_path != NULL) {
|
||||
log_warnx("pidfile specified more than once");
|
||||
free($2);
|
||||
return (1);
|
||||
}
|
||||
conf->conf_pidfile_path = $2;
|
||||
}
|
||||
;
|
||||
|
||||
auth_group_definition: AUTH_GROUP auth_group_name
|
||||
OPENING_BRACKET auth_group_entries CLOSING_BRACKET
|
||||
{
|
||||
auth_group = NULL;
|
||||
}
|
||||
;
|
||||
|
||||
auth_group_name: STR
|
||||
{
|
||||
auth_group = auth_group_new(conf, $1);
|
||||
free($1);
|
||||
if (auth_group == NULL)
|
||||
return (1);
|
||||
}
|
||||
;
|
||||
|
||||
auth_group_entries:
|
||||
|
|
||||
auth_group_entries auth_group_entry
|
||||
;
|
||||
|
||||
auth_group_entry:
|
||||
auth_group_chap
|
||||
|
|
||||
auth_group_chap_mutual
|
||||
;
|
||||
|
||||
auth_group_chap: CHAP STR STR
|
||||
{
|
||||
const struct auth *ca;
|
||||
|
||||
ca = auth_new_chap(auth_group, $2, $3);
|
||||
free($2);
|
||||
free($3);
|
||||
if (ca == NULL)
|
||||
return (1);
|
||||
}
|
||||
;
|
||||
|
||||
auth_group_chap_mutual: CHAP_MUTUAL STR STR STR STR
|
||||
{
|
||||
const struct auth *ca;
|
||||
|
||||
ca = auth_new_chap_mutual(auth_group, $2, $3, $4, $5);
|
||||
free($2);
|
||||
free($3);
|
||||
free($4);
|
||||
free($5);
|
||||
if (ca == NULL)
|
||||
return (1);
|
||||
}
|
||||
;
|
||||
|
||||
portal_group_definition: PORTAL_GROUP portal_group_name
|
||||
OPENING_BRACKET portal_group_entries CLOSING_BRACKET
|
||||
{
|
||||
portal_group = NULL;
|
||||
}
|
||||
;
|
||||
|
||||
portal_group_name: STR
|
||||
{
|
||||
portal_group = portal_group_new(conf, $1);
|
||||
free($1);
|
||||
if (portal_group == NULL)
|
||||
return (1);
|
||||
}
|
||||
;
|
||||
|
||||
portal_group_entries:
|
||||
|
|
||||
portal_group_entries portal_group_entry
|
||||
;
|
||||
|
||||
portal_group_entry:
|
||||
portal_group_discovery_auth_group
|
||||
|
|
||||
portal_group_listen
|
||||
|
|
||||
portal_group_listen_iser
|
||||
;
|
||||
|
||||
portal_group_discovery_auth_group: DISCOVERY_AUTH_GROUP STR
|
||||
{
|
||||
if (portal_group->pg_discovery_auth_group != NULL) {
|
||||
log_warnx("discovery-auth-group for portal-group "
|
||||
"\"%s\" specified more than once",
|
||||
portal_group->pg_name);
|
||||
return (1);
|
||||
}
|
||||
portal_group->pg_discovery_auth_group =
|
||||
auth_group_find(conf, $2);
|
||||
if (portal_group->pg_discovery_auth_group == NULL) {
|
||||
log_warnx("unknown discovery-auth-group \"%s\" "
|
||||
"for portal-group \"%s\"",
|
||||
$2, portal_group->pg_name);
|
||||
return (1);
|
||||
}
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
|
||||
portal_group_listen: LISTEN STR
|
||||
{
|
||||
int error;
|
||||
|
||||
error = portal_group_add_listen(portal_group, $2, false);
|
||||
free($2);
|
||||
if (error != 0)
|
||||
return (1);
|
||||
}
|
||||
;
|
||||
|
||||
portal_group_listen_iser: LISTEN_ISER STR
|
||||
{
|
||||
int error;
|
||||
|
||||
error = portal_group_add_listen(portal_group, $2, true);
|
||||
free($2);
|
||||
if (error != 0)
|
||||
return (1);
|
||||
}
|
||||
;
|
||||
|
||||
target_statement: TARGET target_iqn
|
||||
OPENING_BRACKET target_entries CLOSING_BRACKET
|
||||
{
|
||||
target = NULL;
|
||||
}
|
||||
;
|
||||
|
||||
target_iqn: STR
|
||||
{
|
||||
target = target_new(conf, $1);
|
||||
free($1);
|
||||
if (target == NULL)
|
||||
return (1);
|
||||
}
|
||||
;
|
||||
|
||||
target_entries:
|
||||
|
|
||||
target_entries target_entry
|
||||
;
|
||||
|
||||
target_entry:
|
||||
alias_statement
|
||||
|
|
||||
auth_group_statement
|
||||
|
|
||||
chap_statement
|
||||
|
|
||||
chap_mutual_statement
|
||||
|
|
||||
portal_group_statement
|
||||
|
|
||||
lun_statement
|
||||
;
|
||||
|
||||
alias_statement: ALIAS STR
|
||||
{
|
||||
if (target->t_alias != NULL) {
|
||||
log_warnx("alias for target \"%s\" "
|
||||
"specified more than once", target->t_iqn);
|
||||
return (1);
|
||||
}
|
||||
target->t_alias = $2;
|
||||
}
|
||||
;
|
||||
|
||||
auth_group_statement: AUTH_GROUP STR
|
||||
{
|
||||
if (target->t_auth_group != NULL) {
|
||||
if (target->t_auth_group->ag_name != NULL)
|
||||
log_warnx("auth-group for target \"%s\" "
|
||||
"specified more than once", target->t_iqn);
|
||||
else
|
||||
log_warnx("cannot mix auth-grup with explicit "
|
||||
"authorisations for target \"%s\"",
|
||||
target->t_iqn);
|
||||
return (1);
|
||||
}
|
||||
target->t_auth_group = auth_group_find(conf, $2);
|
||||
if (target->t_auth_group == NULL) {
|
||||
log_warnx("unknown auth-group \"%s\" for target "
|
||||
"\"%s\"", $2, target->t_iqn);
|
||||
return (1);
|
||||
}
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
|
||||
chap_statement: CHAP STR STR
|
||||
{
|
||||
const struct auth *ca;
|
||||
|
||||
if (target->t_auth_group != NULL) {
|
||||
if (target->t_auth_group->ag_name != NULL) {
|
||||
log_warnx("cannot mix auth-grup with explicit "
|
||||
"authorisations for target \"%s\"",
|
||||
target->t_iqn);
|
||||
free($2);
|
||||
free($3);
|
||||
return (1);
|
||||
}
|
||||
} else {
|
||||
target->t_auth_group = auth_group_new(conf, NULL);
|
||||
if (target->t_auth_group == NULL) {
|
||||
free($2);
|
||||
free($3);
|
||||
return (1);
|
||||
}
|
||||
target->t_auth_group->ag_target = target;
|
||||
}
|
||||
ca = auth_new_chap(target->t_auth_group, $2, $3);
|
||||
free($2);
|
||||
free($3);
|
||||
if (ca == NULL)
|
||||
return (1);
|
||||
}
|
||||
;
|
||||
|
||||
chap_mutual_statement: CHAP_MUTUAL STR STR STR STR
|
||||
{
|
||||
const struct auth *ca;
|
||||
|
||||
if (target->t_auth_group != NULL) {
|
||||
if (target->t_auth_group->ag_name != NULL) {
|
||||
log_warnx("cannot mix auth-grup with explicit "
|
||||
"authorisations for target \"%s\"",
|
||||
target->t_iqn);
|
||||
free($2);
|
||||
free($3);
|
||||
free($4);
|
||||
free($5);
|
||||
return (1);
|
||||
}
|
||||
} else {
|
||||
target->t_auth_group = auth_group_new(conf, NULL);
|
||||
if (target->t_auth_group == NULL) {
|
||||
free($2);
|
||||
free($3);
|
||||
free($4);
|
||||
free($5);
|
||||
return (1);
|
||||
}
|
||||
target->t_auth_group->ag_target = target;
|
||||
}
|
||||
ca = auth_new_chap_mutual(target->t_auth_group,
|
||||
$2, $3, $4, $5);
|
||||
free($2);
|
||||
free($3);
|
||||
free($4);
|
||||
free($5);
|
||||
if (ca == NULL)
|
||||
return (1);
|
||||
}
|
||||
;
|
||||
|
||||
portal_group_statement: PORTAL_GROUP STR
|
||||
{
|
||||
if (target->t_portal_group != NULL) {
|
||||
log_warnx("portal-group for target \"%s\" "
|
||||
"specified more than once", target->t_iqn);
|
||||
free($2);
|
||||
return (1);
|
||||
}
|
||||
target->t_portal_group = portal_group_find(conf, $2);
|
||||
if (target->t_portal_group == NULL) {
|
||||
log_warnx("unknown portal-group \"%s\" for target "
|
||||
"\"%s\"", $2, target->t_iqn);
|
||||
free($2);
|
||||
return (1);
|
||||
}
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
|
||||
lun_statement: LUN lun_number
|
||||
OPENING_BRACKET lun_statement_entries CLOSING_BRACKET
|
||||
{
|
||||
lun = NULL;
|
||||
}
|
||||
;
|
||||
|
||||
lun_number: NUM
|
||||
{
|
||||
lun = lun_new(target, $1);
|
||||
if (lun == NULL)
|
||||
return (1);
|
||||
}
|
||||
;
|
||||
|
||||
lun_statement_entries:
|
||||
|
|
||||
lun_statement_entries lun_statement_entry
|
||||
;
|
||||
|
||||
lun_statement_entry:
|
||||
backend_statement
|
||||
|
|
||||
blocksize_statement
|
||||
|
|
||||
device_id_statement
|
||||
|
|
||||
option_statement
|
||||
|
|
||||
path_statement
|
||||
|
|
||||
serial_statement
|
||||
|
|
||||
size_statement
|
||||
;
|
||||
|
||||
backend_statement: BACKEND STR
|
||||
{
|
||||
if (lun->l_backend != NULL) {
|
||||
log_warnx("backend for lun %d, target \"%s\" "
|
||||
"specified more than once",
|
||||
lun->l_lun, target->t_iqn);
|
||||
free($2);
|
||||
return (1);
|
||||
}
|
||||
lun_set_backend(lun, $2);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
|
||||
blocksize_statement: BLOCKSIZE NUM
|
||||
{
|
||||
if (lun->l_blocksize != 0) {
|
||||
log_warnx("blocksize for lun %d, target \"%s\" "
|
||||
"specified more than once",
|
||||
lun->l_lun, target->t_iqn);
|
||||
return (1);
|
||||
}
|
||||
lun_set_blocksize(lun, $2);
|
||||
}
|
||||
;
|
||||
|
||||
device_id_statement: DEVICE_ID STR
|
||||
{
|
||||
if (lun->l_device_id != NULL) {
|
||||
log_warnx("device_id for lun %d, target \"%s\" "
|
||||
"specified more than once",
|
||||
lun->l_lun, target->t_iqn);
|
||||
free($2);
|
||||
return (1);
|
||||
}
|
||||
lun_set_device_id(lun, $2);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
|
||||
option_statement: OPTION STR STR
|
||||
{
|
||||
struct lun_option *clo;
|
||||
|
||||
clo = lun_option_new(lun, $2, $3);
|
||||
free($2);
|
||||
free($3);
|
||||
if (clo == NULL)
|
||||
return (1);
|
||||
}
|
||||
;
|
||||
|
||||
path_statement: PATH STR
|
||||
{
|
||||
if (lun->l_path != NULL) {
|
||||
log_warnx("path for lun %d, target \"%s\" "
|
||||
"specified more than once",
|
||||
lun->l_lun, target->t_iqn);
|
||||
free($2);
|
||||
return (1);
|
||||
}
|
||||
lun_set_path(lun, $2);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
|
||||
serial_statement: SERIAL STR
|
||||
{
|
||||
if (lun->l_serial != NULL) {
|
||||
log_warnx("serial for lun %d, target \"%s\" "
|
||||
"specified more than once",
|
||||
lun->l_lun, target->t_iqn);
|
||||
free($2);
|
||||
return (1);
|
||||
}
|
||||
lun_set_serial(lun, $2);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
|
||||
size_statement: SIZE NUM
|
||||
{
|
||||
if (lun->l_size != 0) {
|
||||
log_warnx("size for lun %d, target \"%s\" "
|
||||
"specified more than once",
|
||||
lun->l_lun, target->t_iqn);
|
||||
return (1);
|
||||
}
|
||||
lun_set_size(lun, $2);
|
||||
}
|
||||
;
|
||||
%%
|
||||
|
||||
void
|
||||
yyerror(const char *str)
|
||||
{
|
||||
|
||||
log_warnx("error in configuration file at line %d near '%s': %s",
|
||||
lineno, yytext, str);
|
||||
}
|
||||
|
||||
static void
|
||||
check_perms(const char *path)
|
||||
{
|
||||
struct stat sb;
|
||||
int error;
|
||||
|
||||
error = stat(path, &sb);
|
||||
if (error != 0) {
|
||||
log_warn("stat");
|
||||
return;
|
||||
}
|
||||
if (sb.st_mode & S_IWOTH) {
|
||||
log_warnx("%s is world-writable", path);
|
||||
} else if (sb.st_mode & S_IROTH) {
|
||||
log_warnx("%s is world-readable", path);
|
||||
} else if (sb.st_mode & S_IXOTH) {
|
||||
/*
|
||||
* Ok, this one doesn't matter, but still do it,
|
||||
* just for consistency.
|
||||
*/
|
||||
log_warnx("%s is world-executable", path);
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: Should we also check for owner != 0?
|
||||
*/
|
||||
}
|
||||
|
||||
struct conf *
|
||||
conf_new_from_file(const char *path)
|
||||
{
|
||||
struct auth_group *ag;
|
||||
struct portal_group *pg;
|
||||
int error;
|
||||
|
||||
log_debugx("obtaining configuration from %s", path);
|
||||
|
||||
conf = conf_new();
|
||||
|
||||
ag = auth_group_new(conf, "no-authentication");
|
||||
ag->ag_type = AG_TYPE_NO_AUTHENTICATION;
|
||||
|
||||
/*
|
||||
* Here, the type doesn't really matter, as the group doesn't contain
|
||||
* any entries and thus will always deny access.
|
||||
*/
|
||||
ag = auth_group_new(conf, "no-access");
|
||||
ag->ag_type = AG_TYPE_CHAP;
|
||||
|
||||
pg = portal_group_new(conf, "default");
|
||||
portal_group_add_listen(pg, "0.0.0.0:3260", false);
|
||||
portal_group_add_listen(pg, "[::]:3260", false);
|
||||
|
||||
yyin = fopen(path, "r");
|
||||
if (yyin == NULL) {
|
||||
log_warn("unable to open configuration file %s", path);
|
||||
conf_delete(conf);
|
||||
return (NULL);
|
||||
}
|
||||
check_perms(path);
|
||||
lineno = 0;
|
||||
yyrestart(yyin);
|
||||
error = yyparse();
|
||||
auth_group = NULL;
|
||||
portal_group = NULL;
|
||||
target = NULL;
|
||||
lun = NULL;
|
||||
fclose(yyin);
|
||||
if (error != 0) {
|
||||
conf_delete(conf);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
error = conf_verify(conf);
|
||||
if (error != 0) {
|
||||
conf_delete(conf);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (conf);
|
||||
}
|
246
usr.sbin/ctld/pdu.c
Normal file
246
usr.sbin/ctld/pdu.c
Normal file
@ -0,0 +1,246 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ctld.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(sizeof(*pdu), 1);
|
||||
if (pdu == NULL)
|
||||
log_err(1, "calloc");
|
||||
|
||||
pdu->pdu_bhs = calloc(sizeof(*pdu->pdu_bhs), 1);
|
||||
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
|
||||
|
||||
void
|
||||
pdu_receive(struct pdu *pdu)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
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 <= MAX_DATA_SEGMENT_LENGTH);
|
||||
pdu->pdu_data_len = len;
|
||||
}
|
||||
|
||||
void
|
||||
pdu_send(struct pdu *pdu)
|
||||
{
|
||||
|
||||
pdu_set_data_segment_length(pdu, pdu->pdu_data_len);
|
||||
kernel_send(pdu);
|
||||
}
|
||||
|
||||
#else /* !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(int fd, char *data, size_t len)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
while (len > 0) {
|
||||
ret = read(fd, data, len);
|
||||
if (ret < 0) {
|
||||
if (timed_out())
|
||||
log_errx(1, "exiting due to timeout");
|
||||
log_err(1, "read");
|
||||
} else if (ret == 0)
|
||||
log_errx(1, "read: connection lost");
|
||||
len -= ret;
|
||||
data += ret;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pdu_receive(struct pdu *pdu)
|
||||
{
|
||||
size_t len, padding;
|
||||
char dummy[4];
|
||||
|
||||
pdu_read(pdu->pdu_connection->conn_socket,
|
||||
(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 > MAX_DATA_SEGMENT_LENGTH) {
|
||||
log_errx(1, "protocol error: received PDU "
|
||||
"with DataSegmentLength exceeding %d",
|
||||
MAX_DATA_SEGMENT_LENGTH);
|
||||
}
|
||||
|
||||
pdu->pdu_data_len = len;
|
||||
pdu->pdu_data = malloc(len);
|
||||
if (pdu->pdu_data == NULL)
|
||||
log_err(1, "malloc");
|
||||
|
||||
pdu_read(pdu->pdu_connection->conn_socket,
|
||||
(char *)pdu->pdu_data, pdu->pdu_data_len);
|
||||
|
||||
padding = pdu_padding(pdu);
|
||||
if (padding != 0) {
|
||||
assert(padding < sizeof(dummy));
|
||||
pdu_read(pdu->pdu_connection->conn_socket,
|
||||
(char *)dummy, padding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pdu_send(struct pdu *pdu)
|
||||
{
|
||||
ssize_t ret, total_len;
|
||||
size_t padding;
|
||||
uint32_t zero = 0;
|
||||
struct iovec iov[3];
|
||||
int iovcnt;
|
||||
|
||||
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(pdu->pdu_connection->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");
|
||||
}
|
||||
|
||||
#endif /* !ICL_KERNEL_PROXY */
|
||||
|
||||
void
|
||||
pdu_delete(struct pdu *pdu)
|
||||
{
|
||||
|
||||
free(pdu->pdu_data);
|
||||
free(pdu->pdu_bhs);
|
||||
free(pdu);
|
||||
}
|
85
usr.sbin/ctld/token.l
Normal file
85
usr.sbin/ctld/token.l
Normal file
@ -0,0 +1,85 @@
|
||||
%{
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ctld.h"
|
||||
#include "y.tab.h"
|
||||
|
||||
int lineno;
|
||||
|
||||
#define YY_DECL int yylex(void)
|
||||
extern int yylex(void);
|
||||
|
||||
%}
|
||||
|
||||
%option noinput
|
||||
%option nounput
|
||||
|
||||
%%
|
||||
alias { return ALIAS; }
|
||||
auth-group { return AUTH_GROUP; }
|
||||
backend { return BACKEND; }
|
||||
blocksize { return BLOCKSIZE; }
|
||||
chap { return CHAP; }
|
||||
chap-mutual { return CHAP_MUTUAL; }
|
||||
debug { return DEBUG; }
|
||||
device-id { return DEVICE_ID; }
|
||||
discovery-auth-group { return DISCOVERY_AUTH_GROUP; }
|
||||
listen { return LISTEN; }
|
||||
listen-iser { return LISTEN_ISER; }
|
||||
lun { return LUN; }
|
||||
maxproc { return MAXPROC; }
|
||||
option { return OPTION; }
|
||||
path { return PATH; }
|
||||
pidfile { return PIDFILE; }
|
||||
portal-group { return PORTAL_GROUP; }
|
||||
serial { return SERIAL; }
|
||||
size { return SIZE; }
|
||||
target { return TARGET; }
|
||||
timeout { return TIMEOUT; }
|
||||
[0-9]+[kKmMgGtTpPeE]? { if (expand_number(yytext, &yylval.num) == 0)
|
||||
return NUM;
|
||||
else
|
||||
return STR;
|
||||
}
|
||||
\"[^"]+\" { yylval.str = strndup(yytext + 1,
|
||||
strlen(yytext) - 2); return STR; }
|
||||
[a-zA-Z0-9\.\-_/\:\[\]]+ { yylval.str = strdup(yytext); return STR; }
|
||||
\{ { return OPENING_BRACKET; }
|
||||
\} { return CLOSING_BRACKET; }
|
||||
#.*$ /* ignore comments */;
|
||||
\n { lineno++; }
|
||||
[ \t]+ /* ignore whitespace */;
|
||||
%%
|
16
usr.sbin/iscsid/Makefile
Normal file
16
usr.sbin/iscsid/Makefile
Normal file
@ -0,0 +1,16 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= iscsid
|
||||
SRCS= discovery.c iscsid.c keys.c log.c login.c pdu.c
|
||||
CFLAGS+= -I${.CURDIR}
|
||||
CFLAGS+= -I${.CURDIR}/../../sys/cam
|
||||
CFLAGS+= -I${.CURDIR}/../../sys/dev/iscsi
|
||||
#CFLAGS+= -DICL_KERNEL_PROXY
|
||||
MAN= iscsid.8
|
||||
|
||||
DPADD= ${LIBUTIL}
|
||||
LDADD= -lcrypto -lssl -lutil
|
||||
|
||||
WARNS= 6
|
||||
|
||||
.include <bsd.prog.mk>
|
222
usr.sbin/iscsid/discovery.c
Normal file
222
usr.sbin/iscsid/discovery.c
Normal file
@ -0,0 +1,222 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "iscsid.h"
|
||||
#include "iscsi_proto.h"
|
||||
|
||||
static struct pdu *
|
||||
text_receive(struct connection *conn)
|
||||
{
|
||||
struct pdu *response;
|
||||
struct iscsi_bhs_text_response *bhstr;
|
||||
|
||||
response = pdu_new(conn);
|
||||
pdu_receive(response);
|
||||
if (response->pdu_bhs->bhs_opcode != ISCSI_BHS_OPCODE_TEXT_RESPONSE)
|
||||
log_errx(1, "protocol error: received invalid opcode 0x%x",
|
||||
response->pdu_bhs->bhs_opcode);
|
||||
bhstr = (struct iscsi_bhs_text_response *)response->pdu_bhs;
|
||||
#if 0
|
||||
if ((bhstr->bhstr_flags & BHSTR_FLAGS_FINAL) == 0)
|
||||
log_errx(1, "received Text PDU without the \"F\" flag");
|
||||
#endif
|
||||
/*
|
||||
* XXX: Implement the C flag some day.
|
||||
*/
|
||||
if ((bhstr->bhstr_flags & BHSTR_FLAGS_CONTINUE) != 0)
|
||||
log_errx(1, "received Text PDU with unsupported \"C\" flag");
|
||||
if (response->pdu_data_len == 0)
|
||||
log_errx(1, "received Text PDU with empty data segment");
|
||||
if (ntohl(bhstr->bhstr_statsn) != conn->conn_statsn + 1) {
|
||||
log_errx(1, "received Text PDU with wrong StatSN: "
|
||||
"is %d, should be %d", ntohl(bhstr->bhstr_statsn),
|
||||
conn->conn_statsn + 1);
|
||||
}
|
||||
conn->conn_statsn = ntohl(bhstr->bhstr_statsn);
|
||||
|
||||
return (response);
|
||||
}
|
||||
|
||||
static struct pdu *
|
||||
text_new_request(struct connection *conn)
|
||||
{
|
||||
struct pdu *request;
|
||||
struct iscsi_bhs_text_request *bhstr;
|
||||
|
||||
request = pdu_new(conn);
|
||||
bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs;
|
||||
bhstr->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_REQUEST |
|
||||
ISCSI_BHS_OPCODE_IMMEDIATE;
|
||||
bhstr->bhstr_flags = BHSTR_FLAGS_FINAL;
|
||||
bhstr->bhstr_initiator_task_tag = 0;
|
||||
bhstr->bhstr_target_transfer_tag = 0xffffffff;
|
||||
|
||||
bhstr->bhstr_initiator_task_tag = 0; /* XXX */
|
||||
bhstr->bhstr_cmdsn = 0; /* XXX */
|
||||
bhstr->bhstr_expstatsn = htonl(conn->conn_statsn + 1);
|
||||
|
||||
return (request);
|
||||
}
|
||||
|
||||
static struct pdu *
|
||||
logout_receive(struct connection *conn)
|
||||
{
|
||||
struct pdu *response;
|
||||
struct iscsi_bhs_logout_response *bhslr;
|
||||
|
||||
response = pdu_new(conn);
|
||||
pdu_receive(response);
|
||||
if (response->pdu_bhs->bhs_opcode != ISCSI_BHS_OPCODE_LOGOUT_RESPONSE)
|
||||
log_errx(1, "protocol error: received invalid opcode 0x%x",
|
||||
response->pdu_bhs->bhs_opcode);
|
||||
bhslr = (struct iscsi_bhs_logout_response *)response->pdu_bhs;
|
||||
if (ntohs(bhslr->bhslr_response) != BHSLR_RESPONSE_CLOSED_SUCCESSFULLY)
|
||||
log_warnx("received Logout Response with reason %d",
|
||||
ntohs(bhslr->bhslr_response));
|
||||
if (ntohl(bhslr->bhslr_statsn) != conn->conn_statsn + 1) {
|
||||
log_errx(1, "received Logout PDU with wrong StatSN: "
|
||||
"is %d, should be %d", ntohl(bhslr->bhslr_statsn),
|
||||
conn->conn_statsn + 1);
|
||||
}
|
||||
conn->conn_statsn = ntohl(bhslr->bhslr_statsn);
|
||||
|
||||
return (response);
|
||||
}
|
||||
|
||||
static struct pdu *
|
||||
logout_new_request(struct connection *conn)
|
||||
{
|
||||
struct pdu *request;
|
||||
struct iscsi_bhs_logout_request *bhslr;
|
||||
|
||||
request = pdu_new(conn);
|
||||
bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs;
|
||||
bhslr->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_REQUEST |
|
||||
ISCSI_BHS_OPCODE_IMMEDIATE;
|
||||
bhslr->bhslr_reason = BHSLR_REASON_CLOSE_SESSION;
|
||||
bhslr->bhslr_reason |= 0x80;
|
||||
bhslr->bhslr_initiator_task_tag = 0; /* XXX */
|
||||
bhslr->bhslr_cmdsn = 0; /* XXX */
|
||||
bhslr->bhslr_expstatsn = htonl(conn->conn_statsn + 1);
|
||||
|
||||
return (request);
|
||||
}
|
||||
|
||||
static void
|
||||
kernel_add(const struct connection *conn, const char *target)
|
||||
{
|
||||
struct iscsi_session_add isa;
|
||||
int error;
|
||||
|
||||
memset(&isa, 0, sizeof(isa));
|
||||
memcpy(&isa.isa_conf, &conn->conn_conf, sizeof(isa));
|
||||
strlcpy(isa.isa_conf.isc_target, target,
|
||||
sizeof(isa.isa_conf.isc_target));
|
||||
isa.isa_conf.isc_discovery = 0;
|
||||
error = ioctl(conn->conn_iscsi_fd, ISCSISADD, &isa);
|
||||
if (error != 0)
|
||||
log_warn("failed to add %s: ISCSISADD", target);
|
||||
}
|
||||
|
||||
static void
|
||||
kernel_remove(const struct connection *conn)
|
||||
{
|
||||
struct iscsi_session_remove isr;
|
||||
int error;
|
||||
|
||||
memset(&isr, 0, sizeof(isr));
|
||||
isr.isr_session_id = conn->conn_session_id;
|
||||
error = ioctl(conn->conn_iscsi_fd, ISCSISREMOVE, &isr);
|
||||
if (error != 0)
|
||||
log_warn("ISCSISREMOVE");
|
||||
}
|
||||
|
||||
void
|
||||
discovery(struct 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_keys = keys_new();
|
||||
keys_add(request_keys, "SendTargets", "All");
|
||||
keys_save(request_keys, request);
|
||||
keys_delete(request_keys);
|
||||
request_keys = NULL;
|
||||
pdu_send(request);
|
||||
pdu_delete(request);
|
||||
request = NULL;
|
||||
|
||||
log_debugx("waiting for Text Response");
|
||||
response = text_receive(conn);
|
||||
response_keys = keys_new();
|
||||
keys_load(response_keys, response);
|
||||
for (i = 0; i < KEYS_MAX; i++) {
|
||||
if (response_keys->keys_names[i] == NULL)
|
||||
break;
|
||||
|
||||
if (strcmp(response_keys->keys_names[i], "TargetName") != 0)
|
||||
continue;
|
||||
|
||||
log_debugx("adding target %s", response_keys->keys_values[i]);
|
||||
/*
|
||||
* XXX: Validate the target name?
|
||||
*/
|
||||
kernel_add(conn, response_keys->keys_values[i]);
|
||||
}
|
||||
keys_delete(response_keys);
|
||||
pdu_delete(response);
|
||||
|
||||
log_debugx("removing temporary discovery session");
|
||||
kernel_remove(conn);
|
||||
|
||||
log_debugx("discovery done; logging out");
|
||||
request = logout_new_request(conn);
|
||||
pdu_send(request);
|
||||
request = NULL;
|
||||
|
||||
log_debugx("waiting for Logout Response");
|
||||
response = logout_receive(conn);
|
||||
pdu_delete(response);
|
||||
|
||||
log_debugx("discovery session done");
|
||||
}
|
113
usr.sbin/iscsid/iscsid.8
Normal file
113
usr.sbin/iscsid/iscsid.8
Normal file
@ -0,0 +1,113 @@
|
||||
.\" Copyright (c) 2012 The FreeBSD Foundation
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" 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 AUTHORS 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 AUTHORS 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.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd September 20, 2012
|
||||
.Dt ISCSID 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm iscsid
|
||||
.Nd iSCSI initiator daemon
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl P Ar pidfile
|
||||
.Op Fl d
|
||||
.Op Fl l Ar loglevel
|
||||
.Op Fl m Ar maxproc
|
||||
.Op Fl t Ar seconds
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
daemon is responsible for performing the Login Phase of iSCSI connections,
|
||||
as well as performing SendTargets discovery.
|
||||
.Pp
|
||||
.Pp
|
||||
Upon startup, the
|
||||
.Nm
|
||||
daemon opens the iSCSI initiator device file and waits for kernel requests.
|
||||
It doesn't use any configuration file; all the information it needs it gets
|
||||
from the kernel.
|
||||
.Pp
|
||||
When the
|
||||
.Nm
|
||||
damon is not running, already established iSCSI connections continue
|
||||
to work.
|
||||
However, establishing new connections, or recovering existing ones in case
|
||||
of connection error, is not possible.
|
||||
.Pp
|
||||
The following options are available:
|
||||
.Bl -tag -width ".Fl P Ar pidfile"
|
||||
.It Fl P Ar pidfile
|
||||
Specify alternative location of a file where main process PID will be stored.
|
||||
The default location is /var/run/iscsid.pid.
|
||||
.It Fl d
|
||||
Debug mode.
|
||||
The server sends verbose debug output to standard error, and does not
|
||||
put itself in the background.
|
||||
The server will also not fork and will exit after processing one connection.
|
||||
This option is only intended for debugging the initiator.
|
||||
.It Fl l Ar loglevel
|
||||
Specifies debug level.
|
||||
The default is 0.
|
||||
.It Fl m Ar maxproc
|
||||
Specifies limit for concurrently running child processes handling
|
||||
connections.
|
||||
The default is 30.
|
||||
Setting it to 0 disables the limit.
|
||||
.It Fl t Ar seconds
|
||||
Specifies timeout for login session, after which the connection
|
||||
will be forcibly terminated.
|
||||
The default is 60.
|
||||
Setting it to 0 disables the timeout.
|
||||
.El
|
||||
.Sh FILES
|
||||
.Bl -tag -width ".Pa /var/run/iscsid.pid" -compact
|
||||
.It Pa /dev/iscsi
|
||||
The iSCSI initiator device file.
|
||||
.It Pa /var/run/iscsid.pid
|
||||
The default location of the
|
||||
.Nm
|
||||
PID file.
|
||||
.El
|
||||
.Sh EXIT STATUS
|
||||
The
|
||||
.Nm
|
||||
utility exits 0 on success, and >0 if an error occurs.
|
||||
.Sh SEE ALSO
|
||||
.Xr iscsictl 8
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
command appeared in
|
||||
.Fx 10.0 .
|
||||
.Sh AUTHORS
|
||||
The
|
||||
.Nm
|
||||
was developed by
|
||||
.An Edward Tomasz Napierala Aq trasz@FreeBSD.org
|
||||
under sponsorship from the FreeBSD Foundation.
|
576
usr.sbin/iscsid/iscsid.c
Normal file
576
usr.sbin/iscsid/iscsid.c
Normal file
@ -0,0 +1,576 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/linker.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/capability.h>
|
||||
#include <sys/wait.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libutil.h>
|
||||
|
||||
#include "iscsid.h"
|
||||
|
||||
static volatile bool sigalrm_received = false;
|
||||
|
||||
static int nchildren = 0;
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
|
||||
fprintf(stderr, "usage: iscsid [-P pidfile][-d][-m maxproc][-t timeout]\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char *
|
||||
checked_strdup(const char *s)
|
||||
{
|
||||
char *c;
|
||||
|
||||
c = strdup(s);
|
||||
if (c == NULL)
|
||||
log_err(1, "strdup");
|
||||
return (c);
|
||||
}
|
||||
|
||||
static int
|
||||
resolve_addr(const char *address, struct addrinfo **ai)
|
||||
{
|
||||
struct addrinfo hints;
|
||||
char *arg, *addr, *ch;
|
||||
const char *port;
|
||||
int error, colons = 0;
|
||||
|
||||
arg = checked_strdup(address);
|
||||
|
||||
if (arg[0] == '\0') {
|
||||
log_warnx("empty address");
|
||||
return (1);
|
||||
}
|
||||
if (arg[0] == '[') {
|
||||
/*
|
||||
* IPv6 address in square brackets, perhaps with port.
|
||||
*/
|
||||
arg++;
|
||||
addr = strsep(&arg, "]");
|
||||
if (arg == NULL) {
|
||||
log_warnx("invalid address %s", address);
|
||||
return (1);
|
||||
}
|
||||
if (arg[0] == '\0') {
|
||||
port = "3260";
|
||||
} else if (arg[0] == ':') {
|
||||
port = arg + 1;
|
||||
} else {
|
||||
log_warnx("invalid address %s", address);
|
||||
return (1);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Either IPv6 address without brackets - and without
|
||||
* a port - or IPv4 address. Just count the colons.
|
||||
*/
|
||||
for (ch = arg; *ch != '\0'; ch++) {
|
||||
if (*ch == ':')
|
||||
colons++;
|
||||
}
|
||||
if (colons > 1) {
|
||||
addr = arg;
|
||||
port = "3260";
|
||||
} else {
|
||||
addr = strsep(&arg, ":");
|
||||
if (arg == NULL)
|
||||
port = "3260";
|
||||
else
|
||||
port = arg;
|
||||
}
|
||||
}
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = PF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
|
||||
error = getaddrinfo(addr, port, &hints, ai);
|
||||
if (error != 0) {
|
||||
log_warnx("getaddrinfo for %s failed: %s",
|
||||
address, gai_strerror(error));
|
||||
return (1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static struct connection *
|
||||
connection_new(unsigned int session_id, const struct iscsi_session_conf *conf,
|
||||
int iscsi_fd)
|
||||
{
|
||||
struct connection *conn;
|
||||
struct addrinfo *from_ai, *to_ai;
|
||||
const char *from_addr, *to_addr;
|
||||
#ifdef ICL_KERNEL_PROXY
|
||||
struct iscsi_daemon_connect *idc;
|
||||
#endif
|
||||
int error;
|
||||
|
||||
conn = calloc(1, sizeof(*conn));
|
||||
if (conn == NULL)
|
||||
log_err(1, "calloc");
|
||||
|
||||
/*
|
||||
* Default values, from RFC 3720, section 12.
|
||||
*/
|
||||
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_data_segment_length = 8192;
|
||||
conn->conn_max_burst_length = 262144;
|
||||
conn->conn_first_burst_length = 65536;
|
||||
|
||||
conn->conn_session_id = session_id;
|
||||
/*
|
||||
* XXX: Should we sanitize this somehow?
|
||||
*/
|
||||
memcpy(&conn->conn_conf, conf, sizeof(conn->conn_conf));
|
||||
|
||||
from_addr = conn->conn_conf.isc_initiator_addr;
|
||||
to_addr = conn->conn_conf.isc_target_addr;
|
||||
|
||||
if (from_addr[0] != '\0') {
|
||||
error = resolve_addr(from_addr, &from_ai);
|
||||
if (error != 0)
|
||||
log_errx(1, "failed to resolve initiator address %s",
|
||||
from_addr);
|
||||
} else {
|
||||
from_ai = NULL;
|
||||
}
|
||||
|
||||
error = resolve_addr(to_addr, &to_ai);
|
||||
if (error != 0)
|
||||
log_errx(1, "failed to resolve target address %s", to_addr);
|
||||
|
||||
conn->conn_iscsi_fd = iscsi_fd;
|
||||
|
||||
#ifdef ICL_KERNEL_PROXY
|
||||
|
||||
idc = calloc(1, sizeof(*idc));
|
||||
if (idc == NULL)
|
||||
log_err(1, "calloc");
|
||||
|
||||
idc->idc_session_id = conn->conn_session_id;
|
||||
if (conn->conn_conf.isc_iser)
|
||||
idc->idc_iser = 1;
|
||||
idc->idc_domain = to_ai->ai_family;
|
||||
idc->idc_socktype = to_ai->ai_socktype;
|
||||
idc->idc_protocol = to_ai->ai_protocol;
|
||||
if (from_ai != NULL) {
|
||||
idc->idc_from_addr = from_ai->ai_addr;
|
||||
idc->idc_from_addrlen = from_ai->ai_addrlen;
|
||||
}
|
||||
idc->idc_to_addr = to_ai->ai_addr;
|
||||
idc->idc_to_addrlen = to_ai->ai_addrlen;
|
||||
|
||||
log_debugx("connecting to %s using ICL kernel proxy", to_addr);
|
||||
error = ioctl(iscsi_fd, ISCSIDCONNECT, idc);
|
||||
if (error != 0) {
|
||||
fail(conn, strerror(errno));
|
||||
log_err(1, "failed to connect to %s using ICL kernel proxy",
|
||||
to_addr);
|
||||
}
|
||||
|
||||
#else /* !ICL_KERNEL_PROXY */
|
||||
|
||||
if (conn->conn_conf.isc_iser)
|
||||
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,
|
||||
to_ai->ai_protocol);
|
||||
if (conn->conn_socket < 0)
|
||||
log_err(1, "failed to create socket for %s", from_addr);
|
||||
if (from_ai != NULL) {
|
||||
error = bind(conn->conn_socket, from_ai->ai_addr,
|
||||
from_ai->ai_addrlen);
|
||||
if (error != 0)
|
||||
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);
|
||||
if (error != 0) {
|
||||
fail(conn, strerror(errno));
|
||||
log_err(1, "failed to connect to %s", to_addr);
|
||||
}
|
||||
|
||||
#endif /* !ICL_KERNEL_PROXY */
|
||||
|
||||
return (conn);
|
||||
}
|
||||
|
||||
static void
|
||||
handoff(struct connection *conn)
|
||||
{
|
||||
struct iscsi_daemon_handoff *idh;
|
||||
int error;
|
||||
|
||||
log_debugx("handing off connection to the kernel");
|
||||
|
||||
idh = calloc(1, sizeof(*idh));
|
||||
if (idh == NULL)
|
||||
log_err(1, "calloc");
|
||||
idh->idh_session_id = conn->conn_session_id;
|
||||
#ifndef ICL_KERNEL_PROXY
|
||||
idh->idh_socket = conn->conn_socket;
|
||||
#endif
|
||||
strlcpy(idh->idh_target_alias, conn->conn_target_alias,
|
||||
sizeof(idh->idh_target_alias));
|
||||
memcpy(idh->idh_isid, conn->conn_isid, sizeof(idh->idh_isid));
|
||||
idh->idh_statsn = conn->conn_statsn;
|
||||
idh->idh_header_digest = conn->conn_header_digest;
|
||||
idh->idh_data_digest = conn->conn_data_digest;
|
||||
idh->idh_initial_r2t = conn->conn_initial_r2t;
|
||||
idh->idh_immediate_data = conn->conn_immediate_data;
|
||||
idh->idh_max_data_segment_length = conn->conn_max_data_segment_length;
|
||||
idh->idh_max_burst_length = conn->conn_max_burst_length;
|
||||
idh->idh_first_burst_length = conn->conn_first_burst_length;
|
||||
|
||||
error = ioctl(conn->conn_iscsi_fd, ISCSIDHANDOFF, idh);
|
||||
if (error != 0)
|
||||
log_err(1, "ISCSIDHANDOFF");
|
||||
}
|
||||
|
||||
void
|
||||
fail(const struct connection *conn, const char *reason)
|
||||
{
|
||||
struct iscsi_daemon_fail *idf;
|
||||
int error;
|
||||
|
||||
idf = calloc(1, sizeof(*idf));
|
||||
if (idf == NULL)
|
||||
log_err(1, "calloc");
|
||||
|
||||
idf->idf_session_id = conn->conn_session_id;
|
||||
strlcpy(idf->idf_reason, reason, sizeof(idf->idf_reason));
|
||||
|
||||
error = ioctl(conn->conn_iscsi_fd, ISCSIDFAIL, idf);
|
||||
if (error != 0)
|
||||
log_err(1, "ISCSIDFAIL");
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: I CANT INTO LATIN
|
||||
*/
|
||||
static void
|
||||
capsicate(struct connection *conn)
|
||||
{
|
||||
int error;
|
||||
cap_rights_t rights;
|
||||
#ifdef ICL_KERNEL_PROXY
|
||||
const unsigned long cmds[] = { ISCSIDCONNECT, ISCSIDSEND, ISCSIDRECEIVE,
|
||||
ISCSIDHANDOFF, ISCSIDFAIL, ISCSISADD, ISCSISREMOVE };
|
||||
#else
|
||||
const unsigned long cmds[] = { ISCSIDHANDOFF, ISCSIDFAIL, ISCSISADD,
|
||||
ISCSISREMOVE };
|
||||
#endif
|
||||
|
||||
cap_rights_init(&rights, CAP_IOCTL);
|
||||
error = cap_rights_limit(conn->conn_iscsi_fd, &rights);
|
||||
if (error != 0 && errno != ENOSYS)
|
||||
log_err(1, "cap_rights_limit");
|
||||
|
||||
error = cap_ioctls_limit(conn->conn_iscsi_fd, cmds,
|
||||
sizeof(cmds) / sizeof(cmds[0]));
|
||||
if (error != 0 && errno != ENOSYS)
|
||||
log_err(1, "cap_ioctls_limit");
|
||||
|
||||
error = cap_enter();
|
||||
if (error != 0 && errno != ENOSYS)
|
||||
log_err(1, "cap_enter");
|
||||
|
||||
if (cap_sandboxed())
|
||||
log_debugx("Capsicum capability mode enabled");
|
||||
else
|
||||
log_warnx("Capsicum capability mode not supported");
|
||||
}
|
||||
|
||||
bool
|
||||
timed_out(void)
|
||||
{
|
||||
|
||||
return (sigalrm_received);
|
||||
}
|
||||
|
||||
static void
|
||||
sigalrm_handler(int dummy __unused)
|
||||
{
|
||||
/*
|
||||
* It would be easiest to just log an error and exit. We can't
|
||||
* do this, though, because log_errx() is not signal safe, since
|
||||
* it calls syslog(3). Instead, set a flag checked by pdu_send()
|
||||
* and pdu_receive(), to call log_errx() there. Should they fail
|
||||
* to notice, we'll exit here one second later.
|
||||
*/
|
||||
if (sigalrm_received) {
|
||||
/*
|
||||
* Oh well. Just give up and quit.
|
||||
*/
|
||||
_exit(2);
|
||||
}
|
||||
|
||||
sigalrm_received = true;
|
||||
}
|
||||
|
||||
static void
|
||||
set_timeout(int timeout)
|
||||
{
|
||||
struct sigaction sa;
|
||||
struct itimerval itv;
|
||||
int error;
|
||||
|
||||
if (timeout <= 0) {
|
||||
log_debugx("session timeout disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
bzero(&sa, sizeof(sa));
|
||||
sa.sa_handler = sigalrm_handler;
|
||||
sigfillset(&sa.sa_mask);
|
||||
error = sigaction(SIGALRM, &sa, NULL);
|
||||
if (error != 0)
|
||||
log_err(1, "sigaction");
|
||||
|
||||
/*
|
||||
* First SIGALRM will arive after conf_timeout seconds.
|
||||
* If we do nothing, another one will arrive a second later.
|
||||
*/
|
||||
bzero(&itv, sizeof(itv));
|
||||
itv.it_interval.tv_sec = 1;
|
||||
itv.it_value.tv_sec = timeout;
|
||||
|
||||
log_debugx("setting session timeout to %d seconds",
|
||||
timeout);
|
||||
error = setitimer(ITIMER_REAL, &itv, NULL);
|
||||
if (error != 0)
|
||||
log_err(1, "setitimer");
|
||||
}
|
||||
|
||||
static void
|
||||
handle_request(int iscsi_fd, struct iscsi_daemon_request *request, int timeout)
|
||||
{
|
||||
struct connection *conn;
|
||||
|
||||
log_set_peer_addr(request->idr_conf.isc_target_addr);
|
||||
if (request->idr_conf.isc_target[0] != '\0') {
|
||||
log_set_peer_name(request->idr_conf.isc_target);
|
||||
setproctitle("%s (%s)", request->idr_conf.isc_target_addr, request->idr_conf.isc_target);
|
||||
} else {
|
||||
setproctitle("%s", request->idr_conf.isc_target_addr);
|
||||
}
|
||||
|
||||
conn = connection_new(request->idr_session_id, &request->idr_conf, iscsi_fd);
|
||||
set_timeout(timeout);
|
||||
capsicate(conn);
|
||||
login(conn);
|
||||
if (conn->conn_conf.isc_discovery != 0)
|
||||
discovery(conn);
|
||||
else
|
||||
handoff(conn);
|
||||
|
||||
log_debugx("nothing more to do; exiting");
|
||||
exit (0);
|
||||
}
|
||||
|
||||
static int
|
||||
wait_for_children(bool block)
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
int num = 0;
|
||||
|
||||
for (;;) {
|
||||
/*
|
||||
* If "block" is true, wait for at least one process.
|
||||
*/
|
||||
if (block && num == 0)
|
||||
pid = wait4(-1, &status, 0, NULL);
|
||||
else
|
||||
pid = wait4(-1, &status, WNOHANG, NULL);
|
||||
if (pid <= 0)
|
||||
break;
|
||||
if (WIFSIGNALED(status)) {
|
||||
log_warnx("child process %d terminated with signal %d",
|
||||
pid, WTERMSIG(status));
|
||||
} else if (WEXITSTATUS(status) != 0) {
|
||||
log_warnx("child process %d terminated with exit status %d",
|
||||
pid, WEXITSTATUS(status));
|
||||
} else {
|
||||
log_debugx("child process %d terminated gracefully", pid);
|
||||
}
|
||||
num++;
|
||||
}
|
||||
|
||||
return (num);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int ch, debug = 0, error, iscsi_fd, maxproc = 30, retval, saved_errno,
|
||||
timeout = 60;
|
||||
bool dont_daemonize = false;
|
||||
struct pidfh *pidfh;
|
||||
pid_t pid, otherpid;
|
||||
const char *pidfile_path = DEFAULT_PIDFILE;
|
||||
struct iscsi_daemon_request *request;
|
||||
|
||||
while ((ch = getopt(argc, argv, "P:dl:m:t:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'P':
|
||||
pidfile_path = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
dont_daemonize = true;
|
||||
debug++;
|
||||
break;
|
||||
case 'l':
|
||||
debug = atoi(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
maxproc = atoi(optarg);
|
||||
break;
|
||||
case 't':
|
||||
timeout = atoi(optarg);
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
if (argc != 0)
|
||||
usage();
|
||||
|
||||
log_init(debug);
|
||||
|
||||
pidfh = pidfile_open(pidfile_path, 0600, &otherpid);
|
||||
if (pidfh == NULL) {
|
||||
if (errno == EEXIST)
|
||||
log_errx(1, "daemon already running, pid: %jd.",
|
||||
(intmax_t)otherpid);
|
||||
log_err(1, "cannot open or create pidfile \"%s\"",
|
||||
pidfile_path);
|
||||
}
|
||||
|
||||
iscsi_fd = open(ISCSI_PATH, O_RDWR);
|
||||
if (iscsi_fd < 0) {
|
||||
saved_errno = errno;
|
||||
retval = kldload("iscsi");
|
||||
if (retval != -1)
|
||||
iscsi_fd = open(ISCSI_PATH, O_RDWR);
|
||||
else
|
||||
errno = saved_errno;
|
||||
}
|
||||
if (iscsi_fd < 0)
|
||||
log_err(1, "failed to open %s", ISCSI_PATH);
|
||||
|
||||
if (dont_daemonize == false) {
|
||||
if (daemon(0, 0) == -1) {
|
||||
log_warn("cannot daemonize");
|
||||
pidfile_remove(pidfh);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
pidfile_write(pidfh);
|
||||
|
||||
for (;;) {
|
||||
log_debugx("waiting for request from the kernel");
|
||||
|
||||
request = calloc(1, sizeof(*request));
|
||||
if (request == NULL)
|
||||
log_err(1, "calloc");
|
||||
|
||||
error = ioctl(iscsi_fd, ISCSIDWAIT, request);
|
||||
if (error != 0) {
|
||||
if (errno == EINTR) {
|
||||
nchildren -= wait_for_children(false);
|
||||
assert(nchildren >= 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
log_err(1, "ISCSIDWAIT");
|
||||
}
|
||||
|
||||
if (dont_daemonize) {
|
||||
log_debugx("not forking due to -d flag; "
|
||||
"will exit after servicing a single request");
|
||||
} else {
|
||||
nchildren -= wait_for_children(false);
|
||||
assert(nchildren >= 0);
|
||||
|
||||
while (maxproc > 0 && nchildren >= maxproc) {
|
||||
log_debugx("maxproc limit of %d child processes hit; "
|
||||
"waiting for child process to exit", maxproc);
|
||||
nchildren -= wait_for_children(true);
|
||||
assert(nchildren >= 0);
|
||||
}
|
||||
log_debugx("incoming connection; forking child process #%d",
|
||||
nchildren);
|
||||
nchildren++;
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0)
|
||||
log_err(1, "fork");
|
||||
if (pid > 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
pidfile_close(pidfh);
|
||||
handle_request(iscsi_fd, request, timeout);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
120
usr.sbin/iscsid/iscsid.h
Normal file
120
usr.sbin/iscsid/iscsid.h
Normal file
@ -0,0 +1,120 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef ISCSID_H
|
||||
#define ISCSID_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <iscsi_ioctl.h>
|
||||
|
||||
#define DEFAULT_PIDFILE "/var/run/iscsid.pid"
|
||||
|
||||
#define CONN_DIGEST_NONE 0
|
||||
#define CONN_DIGEST_CRC32C 1
|
||||
|
||||
#define CONN_MUTUAL_CHALLENGE_LEN 1024
|
||||
|
||||
struct connection {
|
||||
int conn_iscsi_fd;
|
||||
#ifndef ICL_KERNEL_PROXY
|
||||
int conn_socket;
|
||||
#endif
|
||||
unsigned int conn_session_id;
|
||||
struct iscsi_session_conf conn_conf;
|
||||
char conn_target_alias[ISCSI_ADDR_LEN];
|
||||
uint8_t conn_isid[6];
|
||||
uint32_t conn_statsn;
|
||||
int conn_header_digest;
|
||||
int conn_data_digest;
|
||||
bool conn_initial_r2t;
|
||||
bool conn_immediate_data;
|
||||
size_t conn_max_data_segment_length;
|
||||
size_t conn_max_burst_length;
|
||||
size_t conn_first_burst_length;
|
||||
char conn_mutual_challenge[CONN_MUTUAL_CHALLENGE_LEN];
|
||||
unsigned char conn_mutual_id;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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);
|
||||
int keys_find_int(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 __printf0like(2, 3);
|
||||
void log_errx(int, const char *, ...)
|
||||
__dead2 __printf0like(2, 3);
|
||||
void log_warn(const char *, ...) __printf0like(1, 2);
|
||||
void log_warnx(const char *, ...) __printflike(1, 2);
|
||||
void log_debugx(const char *, ...) __printf0like(1, 2);
|
||||
|
||||
char *checked_strdup(const char *);
|
||||
bool timed_out(void);
|
||||
void fail(const struct connection *, const char *);
|
||||
|
||||
#endif /* !ISCSID_H */
|
217
usr.sbin/iscsid/keys.c
Normal file
217
usr.sbin/iscsid/keys.c
Normal file
@ -0,0 +1,217 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "iscsid.h"
|
||||
|
||||
struct keys *
|
||||
keys_new(void)
|
||||
{
|
||||
struct keys *keys;
|
||||
|
||||
keys = calloc(sizeof(*keys), 1);
|
||||
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)
|
||||
log_errx(1, "protocol error: empty data segment");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int
|
||||
keys_find_int(struct keys *keys, const char *name)
|
||||
{
|
||||
const char *str;
|
||||
char *endptr;
|
||||
int num;
|
||||
|
||||
str = keys_find(keys, name);
|
||||
if (str == NULL)
|
||||
return (-1);
|
||||
|
||||
num = strtoul(str, &endptr, 10);
|
||||
if (*endptr != '\0') {
|
||||
log_debugx("invalid numeric value \"%s\"", str);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (num);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
196
usr.sbin/iscsid/log.c
Normal file
196
usr.sbin/iscsid/log.c
Normal file
@ -0,0 +1,196 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $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];
|
||||
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 {
|
||||
if (peer_name != NULL) {
|
||||
fprintf(stderr, "%s: %s (%s): %s: %s\n", getprogname(),
|
||||
peer_addr, peer_name, msgbuf_strvised, strerror(errno));
|
||||
syslog(priority, "%s (%s): %s: %s",
|
||||
peer_addr, peer_name, msgbuf_strvised, strerror(errno));
|
||||
} else if (peer_addr != NULL) {
|
||||
fprintf(stderr, "%s: %s: %s: %s\n", getprogname(),
|
||||
peer_addr, msgbuf_strvised, strerror(errno));
|
||||
syslog(priority, "%s: %s: %s",
|
||||
peer_addr, msgbuf_strvised, strerror(errno));
|
||||
} else {
|
||||
fprintf(stderr, "%s: %s: %s\n", getprogname(),
|
||||
msgbuf_strvised, strerror(errno));
|
||||
syslog(priority, "%s: %s",
|
||||
msgbuf_strvised, strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
868
usr.sbin/iscsid/login.c
Normal file
868
usr.sbin/iscsid/login.c
Normal file
@ -0,0 +1,868 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <netinet/in.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/md5.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include "iscsid.h"
|
||||
#include "iscsi_proto.h"
|
||||
|
||||
static int
|
||||
login_nsg(const struct pdu *response)
|
||||
{
|
||||
struct iscsi_bhs_login_response *bhslr;
|
||||
|
||||
bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs;
|
||||
|
||||
return (bhslr->bhslr_flags & 0x03);
|
||||
}
|
||||
|
||||
static void
|
||||
login_set_nsg(struct pdu *request, int nsg)
|
||||
{
|
||||
struct iscsi_bhs_login_request *bhslr;
|
||||
|
||||
assert(nsg == BHSLR_STAGE_SECURITY_NEGOTIATION ||
|
||||
nsg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION ||
|
||||
nsg == BHSLR_STAGE_FULL_FEATURE_PHASE);
|
||||
|
||||
bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs;
|
||||
|
||||
bhslr->bhslr_flags &= 0xFC;
|
||||
bhslr->bhslr_flags |= nsg;
|
||||
}
|
||||
|
||||
static void
|
||||
login_set_csg(struct pdu *request, int csg)
|
||||
{
|
||||
struct iscsi_bhs_login_request *bhslr;
|
||||
|
||||
assert(csg == BHSLR_STAGE_SECURITY_NEGOTIATION ||
|
||||
csg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION ||
|
||||
csg == BHSLR_STAGE_FULL_FEATURE_PHASE);
|
||||
|
||||
bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs;
|
||||
|
||||
bhslr->bhslr_flags &= 0xF3;
|
||||
bhslr->bhslr_flags |= csg << 2;
|
||||
}
|
||||
|
||||
static const char *
|
||||
login_target_error_str(int class, int detail)
|
||||
{
|
||||
static char msg[128];
|
||||
|
||||
/*
|
||||
* RFC 3270, 10.13.5. Status-Class and Status-Detail
|
||||
*/
|
||||
switch (class) {
|
||||
case 0x01:
|
||||
switch (detail) {
|
||||
case 0x01:
|
||||
return ("Target moved temporarily");
|
||||
case 0x02:
|
||||
return ("Target moved permanently");
|
||||
default:
|
||||
snprintf(msg, sizeof(msg), "unknown redirection; "
|
||||
"Status-Class 0x%x, Status-Detail 0x%x",
|
||||
class, detail);
|
||||
return (msg);
|
||||
}
|
||||
case 0x02:
|
||||
switch (detail) {
|
||||
case 0x00:
|
||||
return ("Initiator error");
|
||||
case 0x01:
|
||||
return ("Authentication failure");
|
||||
case 0x02:
|
||||
return ("Authorization failure");
|
||||
case 0x03:
|
||||
return ("Not found");
|
||||
case 0x04:
|
||||
return ("Target removed");
|
||||
case 0x05:
|
||||
return ("Unsupported version");
|
||||
case 0x06:
|
||||
return ("Too many connections");
|
||||
case 0x07:
|
||||
return ("Missing parameter");
|
||||
case 0x08:
|
||||
return ("Can't include in session");
|
||||
case 0x09:
|
||||
return ("Session type not supported");
|
||||
case 0x0a:
|
||||
return ("Session does not exist");
|
||||
case 0x0b:
|
||||
return ("Invalid during login");
|
||||
default:
|
||||
snprintf(msg, sizeof(msg), "unknown initiator error; "
|
||||
"Status-Class 0x%x, Status-Detail 0x%x",
|
||||
class, detail);
|
||||
return (msg);
|
||||
}
|
||||
case 0x03:
|
||||
switch (detail) {
|
||||
case 0x00:
|
||||
return ("Target error");
|
||||
case 0x01:
|
||||
return ("Service unavailable");
|
||||
case 0x02:
|
||||
return ("Out of resources");
|
||||
default:
|
||||
snprintf(msg, sizeof(msg), "unknown target error; "
|
||||
"Status-Class 0x%x, Status-Detail 0x%x",
|
||||
class, detail);
|
||||
return (msg);
|
||||
}
|
||||
default:
|
||||
snprintf(msg, sizeof(msg), "unknown error; "
|
||||
"Status-Class 0x%x, Status-Detail 0x%x",
|
||||
class, detail);
|
||||
return (msg);
|
||||
}
|
||||
}
|
||||
|
||||
static struct pdu *
|
||||
login_receive(struct connection *conn, bool initial)
|
||||
{
|
||||
struct pdu *response;
|
||||
struct iscsi_bhs_login_response *bhslr;
|
||||
const char *errorstr;
|
||||
|
||||
response = pdu_new(conn);
|
||||
pdu_receive(response);
|
||||
if (response->pdu_bhs->bhs_opcode != ISCSI_BHS_OPCODE_LOGIN_RESPONSE) {
|
||||
log_errx(1, "protocol error: received invalid opcode 0x%x",
|
||||
response->pdu_bhs->bhs_opcode);
|
||||
}
|
||||
bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs;
|
||||
/*
|
||||
* XXX: Implement the C flag some day.
|
||||
*/
|
||||
if ((bhslr->bhslr_flags & BHSLR_FLAGS_CONTINUE) != 0)
|
||||
log_errx(1, "received Login PDU with unsupported \"C\" flag");
|
||||
if (bhslr->bhslr_version_max != 0x00)
|
||||
log_errx(1, "received Login PDU with unsupported "
|
||||
"Version-max 0x%x", bhslr->bhslr_version_max);
|
||||
if (bhslr->bhslr_version_active != 0x00)
|
||||
log_errx(1, "received Login PDU with unsupported "
|
||||
"Version-active 0x%x", bhslr->bhslr_version_active);
|
||||
if (bhslr->bhslr_status_class != 0) {
|
||||
errorstr = login_target_error_str(bhslr->bhslr_status_class,
|
||||
bhslr->bhslr_status_detail);
|
||||
fail(conn, errorstr);
|
||||
log_errx(1, "target returned error: %s", errorstr);
|
||||
}
|
||||
#if 0
|
||||
if (response->pdu_data_len == 0)
|
||||
log_errx(1, "received Login PDU with empty data segment");
|
||||
#endif
|
||||
if (initial == false &&
|
||||
ntohl(bhslr->bhslr_statsn) != conn->conn_statsn + 1) {
|
||||
/*
|
||||
* It's a warning, not an error, to work around what seems
|
||||
* to be bug in NetBSD iSCSI target.
|
||||
*/
|
||||
log_warnx("received Login PDU with wrong StatSN: "
|
||||
"is %d, should be %d", ntohl(bhslr->bhslr_statsn),
|
||||
conn->conn_statsn + 1);
|
||||
}
|
||||
conn->conn_statsn = ntohl(bhslr->bhslr_statsn);
|
||||
|
||||
return (response);
|
||||
}
|
||||
|
||||
static struct pdu *
|
||||
login_new_request(struct connection *conn)
|
||||
{
|
||||
struct pdu *request;
|
||||
struct iscsi_bhs_login_request *bhslr;
|
||||
|
||||
request = pdu_new(conn);
|
||||
bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs;
|
||||
bhslr->bhslr_opcode = ISCSI_BHS_OPCODE_LOGIN_REQUEST |
|
||||
ISCSI_BHS_OPCODE_IMMEDIATE;
|
||||
bhslr->bhslr_flags = BHSLR_FLAGS_TRANSIT;
|
||||
login_set_csg(request, BHSLR_STAGE_SECURITY_NEGOTIATION);
|
||||
login_set_nsg(request, BHSLR_STAGE_OPERATIONAL_NEGOTIATION);
|
||||
memcpy(bhslr->bhslr_isid, &conn->conn_isid, sizeof(bhslr->bhslr_isid));
|
||||
bhslr->bhslr_initiator_task_tag = 0;
|
||||
bhslr->bhslr_cmdsn = 0;
|
||||
bhslr->bhslr_expstatsn = htonl(conn->conn_statsn + 1);
|
||||
|
||||
return (request);
|
||||
}
|
||||
|
||||
static int
|
||||
login_list_prefers(const char *list,
|
||||
const char *choice1, const char *choice2)
|
||||
{
|
||||
char *tofree, *str, *token;
|
||||
|
||||
tofree = str = checked_strdup(list);
|
||||
|
||||
while ((token = strsep(&str, ",")) != NULL) {
|
||||
if (strcmp(token, choice1) == 0) {
|
||||
free(tofree);
|
||||
return (1);
|
||||
}
|
||||
if (strcmp(token, choice2) == 0) {
|
||||
free(tofree);
|
||||
return (2);
|
||||
}
|
||||
}
|
||||
free(tofree);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static int
|
||||
login_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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: Review this _carefully_.
|
||||
*/
|
||||
static int
|
||||
login_hex2bin(const char *hex, char **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, "0x", strlen("0x")) != 0) {
|
||||
log_warnx("malformed variable, should start with \"0x\"");
|
||||
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 = login_hex2int(hex[i]);
|
||||
if (nibble < 0) {
|
||||
log_warnx("malformed variable, invalid char \"%c\"",
|
||||
hex[i]);
|
||||
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);
|
||||
}
|
||||
|
||||
static char *
|
||||
login_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);
|
||||
}
|
||||
|
||||
static void
|
||||
login_compute_md5(const char id, const char *secret,
|
||||
const void *challenge, size_t challenge_len, void *response,
|
||||
size_t response_len)
|
||||
{
|
||||
MD5_CTX ctx;
|
||||
int rv;
|
||||
|
||||
assert(response_len == MD5_DIGEST_LENGTH);
|
||||
|
||||
MD5_Init(&ctx);
|
||||
MD5_Update(&ctx, &id, sizeof(id));
|
||||
MD5_Update(&ctx, secret, strlen(secret));
|
||||
MD5_Update(&ctx, challenge, challenge_len);
|
||||
rv = MD5_Final(response, &ctx);
|
||||
if (rv != 1)
|
||||
log_errx(1, "MD5_Final");
|
||||
}
|
||||
|
||||
static void
|
||||
login_negotiate_key(struct connection *conn, const char *name,
|
||||
const char *value)
|
||||
{
|
||||
int which, tmp;
|
||||
|
||||
if (strcmp(name, "TargetAlias") == 0) {
|
||||
strlcpy(conn->conn_target_alias, value,
|
||||
sizeof(conn->conn_target_alias));
|
||||
} else if (strcmp(value, "Irrelevant") == 0) {
|
||||
/* Ignore. */
|
||||
} else if (strcmp(name, "HeaderDigest") == 0) {
|
||||
which = login_list_prefers(value, "CRC32C", "None");
|
||||
switch (which) {
|
||||
case 1:
|
||||
log_debugx("target prefers CRC32C "
|
||||
"for header digest; we'll use it");
|
||||
conn->conn_header_digest = CONN_DIGEST_CRC32C;
|
||||
break;
|
||||
case 2:
|
||||
log_debugx("target prefers not to do "
|
||||
"header digest; we'll comply");
|
||||
break;
|
||||
default:
|
||||
log_warnx("target sent unrecognized "
|
||||
"HeaderDigest value \"%s\"; will use None", value);
|
||||
break;
|
||||
}
|
||||
} else if (strcmp(name, "DataDigest") == 0) {
|
||||
which = login_list_prefers(value, "CRC32C", "None");
|
||||
switch (which) {
|
||||
case 1:
|
||||
log_debugx("target prefers CRC32C "
|
||||
"for data digest; we'll use it");
|
||||
conn->conn_data_digest = CONN_DIGEST_CRC32C;
|
||||
break;
|
||||
case 2:
|
||||
log_debugx("target prefers not to do "
|
||||
"data digest; we'll comply");
|
||||
break;
|
||||
default:
|
||||
log_warnx("target sent unrecognized "
|
||||
"DataDigest value \"%s\"; will use None", value);
|
||||
break;
|
||||
}
|
||||
} else if (strcmp(name, "MaxConnections") == 0) {
|
||||
/* Ignore. */
|
||||
} else if (strcmp(name, "InitialR2T") == 0) {
|
||||
if (strcmp(value, "Yes") == 0)
|
||||
conn->conn_initial_r2t = true;
|
||||
else
|
||||
conn->conn_initial_r2t = false;
|
||||
} else if (strcmp(name, "ImmediateData") == 0) {
|
||||
if (strcmp(value, "Yes") == 0)
|
||||
conn->conn_immediate_data = true;
|
||||
else
|
||||
conn->conn_immediate_data = false;
|
||||
} else if (strcmp(name, "MaxRecvDataSegmentLength") == 0) {
|
||||
tmp = strtoul(value, NULL, 10);
|
||||
if (tmp <= 0)
|
||||
log_errx(1, "received invalid "
|
||||
"MaxRecvDataSegmentLength");
|
||||
conn->conn_max_data_segment_length = tmp;
|
||||
} else if (strcmp(name, "MaxBurstLength") == 0) {
|
||||
if (conn->conn_immediate_data) {
|
||||
tmp = strtoul(value, NULL, 10);
|
||||
if (tmp <= 0)
|
||||
log_errx(1, "received invalid MaxBurstLength");
|
||||
conn->conn_max_burst_length = tmp;
|
||||
}
|
||||
} else if (strcmp(name, "FirstBurstLength") == 0) {
|
||||
tmp = strtoul(value, NULL, 10);
|
||||
if (tmp <= 0)
|
||||
log_errx(1, "received invalid FirstBurstLength");
|
||||
conn->conn_first_burst_length = tmp;
|
||||
} else if (strcmp(name, "DefaultTime2Wait") == 0) {
|
||||
/* Ignore */
|
||||
} else if (strcmp(name, "DefaultTime2Retain") == 0) {
|
||||
/* Ignore */
|
||||
} else if (strcmp(name, "MaxOutstandingR2T") == 0) {
|
||||
/* Ignore */
|
||||
} else if (strcmp(name, "DataPDUInOrder") == 0) {
|
||||
/* Ignore */
|
||||
} else if (strcmp(name, "DataSequenceInOrder") == 0) {
|
||||
/* Ignore */
|
||||
} else if (strcmp(name, "ErrorRecoveryLevel") == 0) {
|
||||
/* Ignore */
|
||||
} else if (strcmp(name, "OFMarker") == 0) {
|
||||
/* Ignore */
|
||||
} else if (strcmp(name, "IFMarker") == 0) {
|
||||
/* Ignore */
|
||||
} else if (strcmp(name, "TargetPortalGroupTag") == 0) {
|
||||
/* Ignore */
|
||||
} else {
|
||||
log_debugx("unknown key \"%s\"; ignoring", name);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
login_negotiate(struct connection *conn)
|
||||
{
|
||||
struct pdu *request, *response;
|
||||
struct keys *request_keys, *response_keys;
|
||||
struct iscsi_bhs_login_response *bhslr;
|
||||
int i;
|
||||
|
||||
log_debugx("beginning parameter negotiation");
|
||||
request = login_new_request(conn);
|
||||
login_set_csg(request, BHSLR_STAGE_OPERATIONAL_NEGOTIATION);
|
||||
login_set_nsg(request, BHSLR_STAGE_FULL_FEATURE_PHASE);
|
||||
request_keys = keys_new();
|
||||
if (conn->conn_conf.isc_discovery == 0) {
|
||||
if (conn->conn_conf.isc_header_digest != 0)
|
||||
keys_add(request_keys, "HeaderDigest", "CRC32C");
|
||||
if (conn->conn_conf.isc_data_digest != 0)
|
||||
keys_add(request_keys, "DataDigest", "CRC32C");
|
||||
|
||||
keys_add(request_keys, "ImmediateData", "Yes");
|
||||
keys_add_int(request_keys, "MaxBurstLength",
|
||||
ISCSI_MAX_DATA_SEGMENT_LENGTH);
|
||||
keys_add_int(request_keys, "FirstBurstLength",
|
||||
ISCSI_MAX_DATA_SEGMENT_LENGTH);
|
||||
}
|
||||
keys_add(request_keys, "InitialR2T", "Yes");
|
||||
keys_add_int(request_keys, "MaxRecvDataSegmentLength",
|
||||
ISCSI_MAX_DATA_SEGMENT_LENGTH);
|
||||
keys_add(request_keys, "DefaultTime2Wait", "0");
|
||||
keys_add(request_keys, "DefaultTime2Retain", "0");
|
||||
keys_save(request_keys, request);
|
||||
keys_delete(request_keys);
|
||||
request_keys = NULL;
|
||||
pdu_send(request);
|
||||
pdu_delete(request);
|
||||
request = NULL;
|
||||
|
||||
response = login_receive(conn, false);
|
||||
response_keys = keys_new();
|
||||
keys_load(response_keys, response);
|
||||
for (i = 0; i < KEYS_MAX; i++) {
|
||||
if (response_keys->keys_names[i] == NULL)
|
||||
break;
|
||||
|
||||
login_negotiate_key(conn,
|
||||
response_keys->keys_names[i], response_keys->keys_values[i]);
|
||||
}
|
||||
|
||||
bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs;
|
||||
if ((bhslr->bhslr_flags & BHSLR_FLAGS_TRANSIT) == 0)
|
||||
log_warnx("received final login response "
|
||||
"without the \"T\" flag");
|
||||
else if (login_nsg(response) != BHSLR_STAGE_FULL_FEATURE_PHASE)
|
||||
log_warnx("received final login response with wrong NSG 0x%x",
|
||||
login_nsg(response));
|
||||
|
||||
log_debugx("parameter negotiation done; "
|
||||
"transitioning to Full Feature phase");
|
||||
|
||||
keys_delete(response_keys);
|
||||
pdu_delete(response);
|
||||
}
|
||||
|
||||
static void
|
||||
login_send_chap_a(struct connection *conn)
|
||||
{
|
||||
struct pdu *request;
|
||||
struct keys *request_keys;
|
||||
|
||||
request = login_new_request(conn);
|
||||
request_keys = keys_new();
|
||||
keys_add(request_keys, "CHAP_A", "5");
|
||||
keys_save(request_keys, request);
|
||||
keys_delete(request_keys);
|
||||
pdu_send(request);
|
||||
pdu_delete(request);
|
||||
}
|
||||
|
||||
static void
|
||||
login_send_chap_r(struct pdu *response)
|
||||
{
|
||||
struct connection *conn;
|
||||
struct pdu *request;
|
||||
struct keys *request_keys, *response_keys;
|
||||
const char *chap_a, *chap_c, *chap_i;
|
||||
char *chap_r, *challenge, response_bin[MD5_DIGEST_LENGTH];
|
||||
size_t challenge_len;
|
||||
int error, rv;
|
||||
unsigned char id;
|
||||
char *mutual_chap_c, mutual_chap_i[4];
|
||||
|
||||
/*
|
||||
* As in the rest of the initiator, 'request' means
|
||||
* 'initiator -> target', and 'response' means 'target -> initiator',
|
||||
*
|
||||
* So, here the 'response' from the target is the packet that contains
|
||||
* CHAP challenge; our CHAP response goes into 'request'.
|
||||
*/
|
||||
|
||||
conn = response->pdu_connection;
|
||||
|
||||
response_keys = keys_new();
|
||||
keys_load(response_keys, response);
|
||||
|
||||
/*
|
||||
* First, compute the response.
|
||||
*/
|
||||
chap_a = keys_find(response_keys, "CHAP_A");
|
||||
if (chap_a == NULL)
|
||||
log_errx(1, "received CHAP packet without CHAP_A");
|
||||
chap_c = keys_find(response_keys, "CHAP_C");
|
||||
if (chap_c == NULL)
|
||||
log_errx(1, "received CHAP packet without CHAP_C");
|
||||
chap_i = keys_find(response_keys, "CHAP_I");
|
||||
if (chap_i == NULL)
|
||||
log_errx(1, "received CHAP packet without CHAP_I");
|
||||
|
||||
if (strcmp(chap_a, "5") != 0)
|
||||
log_errx(1, "received CHAP packet "
|
||||
"with unsupported CHAP_A \"%s\"", chap_a);
|
||||
id = strtoul(chap_i, NULL, 10);
|
||||
error = login_hex2bin(chap_c, &challenge, &challenge_len);
|
||||
if (error != 0)
|
||||
log_errx(1, "received CHAP packet with malformed CHAP_C");
|
||||
login_compute_md5(id, conn->conn_conf.isc_secret,
|
||||
challenge, challenge_len, response_bin, sizeof(response_bin));
|
||||
free(challenge);
|
||||
chap_r = login_bin2hex(response_bin, sizeof(response_bin));
|
||||
|
||||
keys_delete(response_keys);
|
||||
|
||||
request = login_new_request(conn);
|
||||
request_keys = keys_new();
|
||||
keys_add(request_keys, "CHAP_N", conn->conn_conf.isc_user);
|
||||
keys_add(request_keys, "CHAP_R", chap_r);
|
||||
free(chap_r);
|
||||
|
||||
/*
|
||||
* If we want mutual authentication, we're expected to send
|
||||
* our CHAP_I/CHAP_C now.
|
||||
*/
|
||||
if (conn->conn_conf.isc_mutual_user[0] != '\0') {
|
||||
log_debugx("requesting mutual authentication; "
|
||||
"binary challenge size is %zd bytes",
|
||||
sizeof(conn->conn_mutual_challenge));
|
||||
|
||||
rv = RAND_bytes(conn->conn_mutual_challenge,
|
||||
sizeof(conn->conn_mutual_challenge));
|
||||
if (rv != 1) {
|
||||
log_errx(1, "RAND_bytes failed: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
rv = RAND_bytes(&conn->conn_mutual_id,
|
||||
sizeof(conn->conn_mutual_id));
|
||||
if (rv != 1) {
|
||||
log_errx(1, "RAND_bytes failed: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
mutual_chap_c = login_bin2hex(conn->conn_mutual_challenge,
|
||||
sizeof(conn->conn_mutual_challenge));
|
||||
snprintf(mutual_chap_i, sizeof(mutual_chap_i),
|
||||
"%d", conn->conn_mutual_id);
|
||||
keys_add(request_keys, "CHAP_I", mutual_chap_i);
|
||||
keys_add(request_keys, "CHAP_C", mutual_chap_c);
|
||||
free(mutual_chap_c);
|
||||
}
|
||||
|
||||
keys_save(request_keys, request);
|
||||
keys_delete(request_keys);
|
||||
pdu_send(request);
|
||||
pdu_delete(request);
|
||||
}
|
||||
|
||||
static void
|
||||
login_verify_mutual(const struct pdu *response)
|
||||
{
|
||||
struct connection *conn;
|
||||
struct keys *response_keys;
|
||||
const char *chap_n, *chap_r;
|
||||
char *response_bin, expected_response_bin[MD5_DIGEST_LENGTH];
|
||||
size_t response_bin_len;
|
||||
int error;
|
||||
|
||||
conn = response->pdu_connection;
|
||||
|
||||
response_keys = keys_new();
|
||||
keys_load(response_keys, response);
|
||||
|
||||
chap_n = keys_find(response_keys, "CHAP_N");
|
||||
if (chap_n == NULL)
|
||||
log_errx(1, "received CHAP Response PDU without CHAP_N");
|
||||
chap_r = keys_find(response_keys, "CHAP_R");
|
||||
if (chap_r == NULL)
|
||||
log_errx(1, "received CHAP Response PDU without CHAP_R");
|
||||
error = login_hex2bin(chap_r, &response_bin, &response_bin_len);
|
||||
if (error != 0)
|
||||
log_errx(1, "received CHAP Response PDU with malformed CHAP_R");
|
||||
|
||||
if (strcmp(chap_n, conn->conn_conf.isc_mutual_user) != 0) {
|
||||
fail(conn, "Mutual CHAP failed");
|
||||
log_errx(1, "mutual CHAP authentication failed: wrong user");
|
||||
}
|
||||
|
||||
login_compute_md5(conn->conn_mutual_id,
|
||||
conn->conn_conf.isc_mutual_secret, conn->conn_mutual_challenge,
|
||||
sizeof(conn->conn_mutual_challenge), expected_response_bin,
|
||||
sizeof(expected_response_bin));
|
||||
|
||||
if (memcmp(response_bin, expected_response_bin,
|
||||
sizeof(expected_response_bin)) != 0) {
|
||||
fail(conn, "Mutual CHAP failed");
|
||||
log_errx(1, "mutual CHAP authentication failed: wrong secret");
|
||||
}
|
||||
|
||||
keys_delete(response_keys);
|
||||
free(response_bin);
|
||||
|
||||
log_debugx("mutual CHAP authentication succeeded");
|
||||
}
|
||||
|
||||
static void
|
||||
login_chap(struct connection *conn)
|
||||
{
|
||||
struct pdu *response;
|
||||
|
||||
log_debugx("beginning CHAP authentication; sending CHAP_A");
|
||||
login_send_chap_a(conn);
|
||||
|
||||
log_debugx("waiting for CHAP_A/CHAP_C/CHAP_I");
|
||||
response = login_receive(conn, false);
|
||||
|
||||
log_debugx("sending CHAP_N/CHAP_R");
|
||||
login_send_chap_r(response);
|
||||
pdu_delete(response);
|
||||
|
||||
/*
|
||||
* XXX: Make sure this is not susceptible to MITM.
|
||||
*/
|
||||
|
||||
log_debugx("waiting for CHAP result");
|
||||
response = login_receive(conn, false);
|
||||
if (conn->conn_conf.isc_mutual_user[0] != '\0')
|
||||
login_verify_mutual(response);
|
||||
pdu_delete(response);
|
||||
|
||||
log_debugx("CHAP authentication done");
|
||||
}
|
||||
|
||||
static void
|
||||
login_create_isid(struct connection *conn)
|
||||
{
|
||||
int rv;
|
||||
|
||||
/*
|
||||
* RFC 3720, 10.12.5: 10b, "Random" ISID.
|
||||
*
|
||||
*/
|
||||
conn->conn_isid[0] = 0x80;
|
||||
|
||||
rv = RAND_bytes(&conn->conn_isid[1], 3);
|
||||
if (rv != 1) {
|
||||
log_errx(1, "RAND_bytes failed: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
login(struct connection *conn)
|
||||
{
|
||||
struct pdu *request, *response;
|
||||
struct keys *request_keys, *response_keys;
|
||||
struct iscsi_bhs_login_request *bhslr;
|
||||
struct iscsi_bhs_login_response *bhslr2;
|
||||
const char *auth_method;
|
||||
int i;
|
||||
|
||||
login_create_isid(conn);
|
||||
|
||||
log_debugx("beginning Login phase; sending Login PDU");
|
||||
request = login_new_request(conn);
|
||||
|
||||
bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs;
|
||||
bhslr->bhslr_flags |= BHSLR_FLAGS_TRANSIT;
|
||||
|
||||
request_keys = keys_new();
|
||||
if (conn->conn_conf.isc_user[0] == '\0')
|
||||
keys_add(request_keys, "AuthMethod", "None");
|
||||
else
|
||||
keys_add(request_keys, "AuthMethod", "CHAP,None");
|
||||
keys_add(request_keys, "InitiatorName",
|
||||
conn->conn_conf.isc_initiator);
|
||||
if (conn->conn_conf.isc_initiator_alias[0] != '\0') {
|
||||
keys_add(request_keys, "InitiatorAlias",
|
||||
conn->conn_conf.isc_initiator_alias);
|
||||
}
|
||||
if (conn->conn_conf.isc_discovery == 0) {
|
||||
keys_add(request_keys, "SessionType", "Normal");
|
||||
keys_add(request_keys,
|
||||
"TargetName", conn->conn_conf.isc_target);
|
||||
} else {
|
||||
keys_add(request_keys, "SessionType", "Discovery");
|
||||
}
|
||||
keys_save(request_keys, request);
|
||||
keys_delete(request_keys);
|
||||
pdu_send(request);
|
||||
pdu_delete(request);
|
||||
|
||||
response = login_receive(conn, true);
|
||||
|
||||
response_keys = keys_new();
|
||||
keys_load(response_keys, response);
|
||||
|
||||
for (i = 0; i < KEYS_MAX; i++) {
|
||||
if (response_keys->keys_names[i] == NULL)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Not interested in AuthMethod at this point; we only need
|
||||
* to parse things such as TargetAlias.
|
||||
*
|
||||
* XXX: This is somewhat ugly. We should have a way to apply
|
||||
* all the keys to the session and use that by default
|
||||
* instead of discarding them.
|
||||
*/
|
||||
if (strcmp(response_keys->keys_names[i], "AuthMethod") == 0)
|
||||
continue;
|
||||
|
||||
login_negotiate_key(conn,
|
||||
response_keys->keys_names[i], response_keys->keys_values[i]);
|
||||
}
|
||||
|
||||
bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs;
|
||||
if ((bhslr2->bhslr_flags & BHSLR_FLAGS_TRANSIT) != 0 &&
|
||||
login_nsg(response) == BHSLR_STAGE_OPERATIONAL_NEGOTIATION) {
|
||||
log_debugx("target requested transition "
|
||||
"to operational negotiation");
|
||||
|
||||
keys_delete(response_keys);
|
||||
pdu_delete(response);
|
||||
login_negotiate(conn);
|
||||
return;
|
||||
}
|
||||
|
||||
auth_method = keys_find(response_keys, "AuthMethod");
|
||||
if (auth_method == NULL)
|
||||
log_errx(1, "received response without AuthMethod");
|
||||
if (strcmp(auth_method, "None") == 0) {
|
||||
log_debugx("target does not require authentication");
|
||||
keys_delete(response_keys);
|
||||
pdu_delete(response);
|
||||
login_negotiate(conn);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(auth_method, "CHAP") != 0) {
|
||||
fail(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");
|
||||
log_errx(1, "target requests CHAP authentication, but we don't "
|
||||
"have user and secret");
|
||||
}
|
||||
|
||||
keys_delete(response_keys);
|
||||
response_keys = NULL;
|
||||
pdu_delete(response);
|
||||
response = NULL;
|
||||
|
||||
login_chap(conn);
|
||||
login_negotiate(conn);
|
||||
}
|
281
usr.sbin/iscsid/pdu.c
Normal file
281
usr.sbin/iscsid/pdu.c
Normal file
@ -0,0 +1,281 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.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(sizeof(*pdu), 1);
|
||||
if (pdu == NULL)
|
||||
log_err(1, "calloc");
|
||||
|
||||
pdu->pdu_bhs = calloc(sizeof(*pdu->pdu_bhs), 1);
|
||||
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
|
||||
|
||||
void
|
||||
pdu_receive(struct pdu *pdu)
|
||||
{
|
||||
struct iscsi_daemon_receive *idr;
|
||||
size_t len;
|
||||
int error;
|
||||
|
||||
pdu->pdu_data = malloc(ISCSI_MAX_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 = pdu->pdu_connection->conn_session_id;
|
||||
idr->idr_bhs = pdu->pdu_bhs;
|
||||
idr->idr_data_segment_len = ISCSI_MAX_DATA_SEGMENT_LENGTH;
|
||||
idr->idr_data_segment = pdu->pdu_data;
|
||||
|
||||
error = ioctl(pdu->pdu_connection->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 <= ISCSI_MAX_DATA_SEGMENT_LENGTH);
|
||||
pdu->pdu_data_len = len;
|
||||
|
||||
free(idr);
|
||||
}
|
||||
|
||||
void
|
||||
pdu_send(struct pdu *pdu)
|
||||
{
|
||||
struct iscsi_daemon_send *ids;
|
||||
int error;
|
||||
|
||||
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 = pdu->pdu_connection->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(pdu->pdu_connection->conn_iscsi_fd, ISCSIDSEND, ids);
|
||||
if (error != 0)
|
||||
log_err(1, "ISCSIDSEND");
|
||||
|
||||
free(ids);
|
||||
}
|
||||
|
||||
#else /* !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(int fd, char *data, size_t len)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
while (len > 0) {
|
||||
ret = read(fd, data, len);
|
||||
if (ret < 0) {
|
||||
if (timed_out())
|
||||
log_errx(1, "exiting due to timeout");
|
||||
log_err(1, "read");
|
||||
} else if (ret == 0)
|
||||
log_errx(1, "read: connection lost");
|
||||
len -= ret;
|
||||
data += ret;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pdu_receive(struct pdu *pdu)
|
||||
{
|
||||
size_t len, padding;
|
||||
char dummy[4];
|
||||
|
||||
pdu_read(pdu->pdu_connection->conn_socket,
|
||||
(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 > ISCSI_MAX_DATA_SEGMENT_LENGTH) {
|
||||
log_errx(1, "protocol error: received PDU "
|
||||
"with DataSegmentLength exceeding %d",
|
||||
ISCSI_MAX_DATA_SEGMENT_LENGTH);
|
||||
}
|
||||
|
||||
pdu->pdu_data_len = len;
|
||||
pdu->pdu_data = malloc(len);
|
||||
if (pdu->pdu_data == NULL)
|
||||
log_err(1, "malloc");
|
||||
|
||||
pdu_read(pdu->pdu_connection->conn_socket,
|
||||
(char *)pdu->pdu_data, pdu->pdu_data_len);
|
||||
|
||||
padding = pdu_padding(pdu);
|
||||
if (padding != 0) {
|
||||
assert(padding < sizeof(dummy));
|
||||
pdu_read(pdu->pdu_connection->conn_socket,
|
||||
(char *)dummy, padding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pdu_send(struct pdu *pdu)
|
||||
{
|
||||
ssize_t ret, total_len;
|
||||
size_t padding;
|
||||
uint32_t zero = 0;
|
||||
struct iovec iov[3];
|
||||
int iovcnt;
|
||||
|
||||
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(pdu->pdu_connection->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");
|
||||
}
|
||||
|
||||
#endif /* !ICL_KERNEL_PROXY */
|
||||
|
||||
void
|
||||
pdu_delete(struct pdu *pdu)
|
||||
{
|
||||
|
||||
free(pdu->pdu_data);
|
||||
free(pdu->pdu_bhs);
|
||||
free(pdu);
|
||||
}
|
Loading…
Reference in New Issue
Block a user