diff -urN p4/freebsd/src/etc/defaults/rc.conf p4/iscsi/etc/defaults/rc.conf --- p4/freebsd/src/etc/defaults/rc.conf 2013-07-24 07:52:00.000000000 +0200 +++ p4/iscsi/etc/defaults/rc.conf 2013-07-25 19:17:06.000000000 +0200 @@ -263,9 +263,13 @@ 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. diff -urN p4/freebsd/src/etc/rc.d/Makefile p4/iscsi/etc/rc.d/Makefile --- p4/freebsd/src/etc/rc.d/Makefile 2013-07-24 07:52:00.000000000 +0200 +++ p4/iscsi/etc/rc.d/Makefile 2013-07-25 19:17:06.000000000 +0200 @@ -30,6 +30,7 @@ cleanvar \ cleartmp \ cron \ + ctld \ ddb \ defaultroute \ devd \ @@ -62,6 +63,8 @@ ipnat \ ipsec \ ${_ipxrouted} \ + iscsictl \ + iscsid \ jail \ kadmind \ kerberos \ diff -urN p4/freebsd/src/etc/rc.d/ctld p4/iscsi/etc/rc.d/ctld --- p4/freebsd/src/etc/rc.d/ctld 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/etc/rc.d/ctld 2013-07-23 23:41:34.000000000 +0200 @@ -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" diff -urN p4/freebsd/src/etc/rc.d/iscsictl p4/iscsi/etc/rc.d/iscsictl --- p4/freebsd/src/etc/rc.d/iscsictl 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/etc/rc.d/iscsictl 2013-07-23 23:41:34.000000000 +0200 @@ -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" diff -urN p4/freebsd/src/etc/rc.d/iscsid p4/iscsi/etc/rc.d/iscsid --- p4/freebsd/src/etc/rc.d/iscsid 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/etc/rc.d/iscsid 2013-07-23 23:41:34.000000000 +0200 @@ -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" diff -urN p4/freebsd/src/sbin/iscontrol/auth_subr.c p4/iscsi/sbin/iscontrol/auth_subr.c --- p4/freebsd/src/sbin/iscontrol/auth_subr.c 2013-06-19 11:02:14.000000000 +0200 +++ p4/iscsi/sbin/iscontrol/auth_subr.c 2013-07-23 23:42:14.000000000 +0200 @@ -49,7 +49,7 @@ #include #include -#include +#include #include "iscontrol.h" static int diff -urN p4/freebsd/src/sbin/iscontrol/config.c p4/iscsi/sbin/iscontrol/config.c --- p4/freebsd/src/sbin/iscontrol/config.c 2013-04-20 19:57:59.000000000 +0200 +++ p4/iscsi/sbin/iscontrol/config.c 2013-07-23 23:42:14.000000000 +0200 @@ -41,7 +41,7 @@ #include #include -#include +#include #include "iscontrol.h" /* diff -urN p4/freebsd/src/sbin/iscontrol/fsm.c p4/iscsi/sbin/iscontrol/fsm.c --- p4/freebsd/src/sbin/iscontrol/fsm.c 2013-06-19 11:02:14.000000000 +0200 +++ p4/iscsi/sbin/iscontrol/fsm.c 2013-07-23 23:42:14.000000000 +0200 @@ -53,7 +53,7 @@ #include #include -#include +#include #include "iscontrol.h" typedef enum { diff -urN p4/freebsd/src/sbin/iscontrol/iscontrol.8 p4/iscsi/sbin/iscontrol/iscontrol.8 --- p4/freebsd/src/sbin/iscontrol/iscontrol.8 2013-04-20 19:57:59.000000000 +0200 +++ p4/iscsi/sbin/iscontrol/iscontrol.8 2013-07-23 23:42:14.000000000 +0200 @@ -112,6 +112,7 @@ .Xr iscsi_initiator 4 , .Xr sa 4 , .Xr iscsi.conf 5 , +.Xr iscsictl 8 , .Xr camcontrol 8 .Sh STANDARDS RFC 3720 diff -urN p4/freebsd/src/sbin/iscontrol/iscontrol.c p4/iscsi/sbin/iscontrol/iscontrol.c --- p4/freebsd/src/sbin/iscontrol/iscontrol.c 2013-04-20 19:57:59.000000000 +0200 +++ p4/iscsi/sbin/iscontrol/iscontrol.c 2013-07-23 23:42:14.000000000 +0200 @@ -55,7 +55,7 @@ #include #include -#include +#include #include "iscontrol.h" static char version[] = "2.3.1"; // keep in sync with iscsi_initiator diff -urN p4/freebsd/src/sbin/iscontrol/iscsi.conf.5 p4/iscsi/sbin/iscontrol/iscsi.conf.5 --- p4/freebsd/src/sbin/iscontrol/iscsi.conf.5 2013-06-19 11:02:14.000000000 +0200 +++ p4/iscsi/sbin/iscontrol/iscsi.conf.5 2013-07-23 23:42:14.000000000 +0200 @@ -201,6 +201,7 @@ error messages. .Sh SEE ALSO .Xr iscsi_initiator 4 , +.Xr iscsictl 8 , .Xr iscontrol 8 .Sh STANDARDS ISCSI RFC 3720 diff -urN p4/freebsd/src/sbin/iscontrol/login.c p4/iscsi/sbin/iscontrol/login.c --- p4/freebsd/src/sbin/iscontrol/login.c 2013-06-19 11:02:14.000000000 +0200 +++ p4/iscsi/sbin/iscontrol/login.c 2013-07-23 23:42:14.000000000 +0200 @@ -44,7 +44,7 @@ #include #include -#include +#include #include "iscontrol.h" static char *status_class1[] = { diff -urN p4/freebsd/src/sbin/iscontrol/misc.c p4/iscsi/sbin/iscontrol/misc.c --- p4/freebsd/src/sbin/iscontrol/misc.c 2013-06-19 11:02:14.000000000 +0200 +++ p4/iscsi/sbin/iscontrol/misc.c 2013-07-23 23:42:14.000000000 +0200 @@ -44,7 +44,7 @@ #include #include -#include +#include #include "iscontrol.h" static inline char diff -urN p4/freebsd/src/sbin/iscontrol/pdu.c p4/iscsi/sbin/iscontrol/pdu.c --- p4/freebsd/src/sbin/iscontrol/pdu.c 2013-04-20 19:58:00.000000000 +0200 +++ p4/iscsi/sbin/iscontrol/pdu.c 2013-07-23 23:42:14.000000000 +0200 @@ -43,7 +43,7 @@ #include #include -#include +#include #include "iscontrol.h" static void pukeText(char *it, pdu_t *pp); diff -urN p4/freebsd/src/share/man/man4/ctl.4 p4/iscsi/share/man/man4/ctl.4 --- p4/freebsd/src/share/man/man4/ctl.4 2013-04-20 20:00:27.000000000 +0200 +++ p4/iscsi/share/man/man4/ctl.4 2013-07-23 23:42:39.000000000 +0200 @@ -75,8 +75,11 @@ .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 diff -urN p4/freebsd/src/sys/cam/ctl/ctl.c p4/iscsi/sys/cam/ctl/ctl.c --- p4/freebsd/src/sys/cam/ctl/ctl.c 2013-04-20 20:02:13.000000000 +0200 +++ p4/iscsi/sys/cam/ctl/ctl.c 2013-07-23 23:42:57.000000000 +0200 @@ -894,8 +894,13 @@ struct ctl_lun *lun; struct ctl_page_index *page_index; struct copan_aps_subpage *current_sp; + uint32_t targ_lun; - lun = ctl_softc->ctl_luns[msg_info.hdr.nexus.targ_lun]; + targ_lun = msg_info.hdr.nexus.targ_lun; + if (msg_info.hdr.nexus.lun_map_fn != NULL) + targ_lun = msg_info.hdr.nexus.lun_map_fn(msg_info.hdr.nexus.lun_map_arg, targ_lun); + + lun = ctl_softc->ctl_luns[targ_lun]; page_index = &lun->mode_pages.index[index_to_aps_page]; current_sp = (struct copan_aps_subpage *) (page_index->page_data + @@ -1070,9 +1075,11 @@ softc->emergency_pool = emergency_pool; softc->othersc_pool = other_pool; + mtx_lock(&softc->ctl_lock); ctl_pool_acquire(internal_pool); ctl_pool_acquire(emergency_pool); ctl_pool_acquire(other_pool); + mtx_unlock(&softc->ctl_lock); /* * We used to allocate a processor LUN here. The new scheme is to @@ -1088,13 +1095,16 @@ "ctl_thrd"); if (error != 0) { printf("error creating CTL work thread!\n"); + mtx_lock(&softc->ctl_lock); ctl_free_lun(lun); ctl_pool_free(softc, internal_pool); ctl_pool_free(softc, emergency_pool); ctl_pool_free(softc, other_pool); + mtx_unlock(&softc->ctl_lock); return (error); } - printf("ctl: CAM Target Layer loaded\n"); + if (bootverbose) + printf("ctl: CAM Target Layer loaded\n"); /* * Initialize the initiator and portname mappings @@ -1190,7 +1200,8 @@ free(control_softc, M_DEVBUF); control_softc = NULL; - printf("ctl: CAM Target Layer unloaded\n"); + if (bootverbose) + printf("ctl: CAM Target Layer unloaded\n"); } static int @@ -1372,7 +1383,6 @@ /* * Remove an initiator by port number and initiator ID. * Returns 0 for success, 1 for failure. - * Assumes the caller does NOT hold the CTL lock. */ int ctl_remove_initiator(int32_t targ_port, uint32_t iid) @@ -1381,6 +1391,8 @@ softc = control_softc; + mtx_assert(&softc->ctl_lock, MA_NOTOWNED); + if ((targ_port < 0) || (targ_port > CTL_MAX_PORTS)) { printf("%s: invalid port number %d\n", __func__, targ_port); @@ -1404,7 +1416,6 @@ /* * Add an initiator to the initiator map. * Returns 0 for success, 1 for failure. - * Assumes the caller does NOT hold the CTL lock. */ int ctl_add_initiator(uint64_t wwpn, int32_t targ_port, uint32_t iid) @@ -1414,6 +1425,8 @@ softc = control_softc; + mtx_assert(&softc->ctl_lock, MA_NOTOWNED); + retval = 0; if ((targ_port < 0) @@ -1672,12 +1685,16 @@ union ctl_ha_msg msg_info; struct ctl_lun *lun; int retval = 0; + uint32_t targ_lun; ctl_softc = control_softc; if (have_lock == 0) mtx_lock(&ctl_softc->ctl_lock); - lun = ctl_softc->ctl_luns[ctsio->io_hdr.nexus.targ_lun]; + targ_lun = ctsio->io_hdr.nexus.targ_lun; + if (ctsio->io_hdr.nexus.lun_map_fn != NULL) + targ_lun = ctsio->io_hdr.nexus.lun_map_fn(ctsio->io_hdr.nexus.lun_map_arg, targ_lun); + lun = ctl_softc->ctl_luns[targ_lun]; if (lun==NULL) { /* @@ -1970,7 +1987,6 @@ } /* - * Must be called with the ctl_lock held. * Returns 0 for success, errno for failure. */ static int @@ -1982,6 +1998,8 @@ retval = 0; + mtx_assert(&control_softc->ctl_lock, MA_OWNED); + for (io = (union ctl_io *)TAILQ_FIRST(&lun->ooa_queue); (io != NULL); (*cur_fill_num)++, io = (union ctl_io *)TAILQ_NEXT(&io->io_hdr, ooa_links)) { @@ -2973,6 +2991,7 @@ struct sbuf *sb; struct ctl_lun *lun; struct ctl_lun_list *list; + struct ctl_be_lun_option *opt; list = (struct ctl_lun_list *)addr; @@ -3090,17 +3109,16 @@ if (retval != 0) break; - if (lun->backend->lun_info == NULL) { - retval = sbuf_printf(sb, "\n"); + if (lun->backend->lun_info != NULL) { + retval = lun->backend->lun_info(lun->be_lun->be_lun, sb); + if (retval != 0) + break; + } + STAILQ_FOREACH(opt, &lun->be_lun->options, links) { + retval = sbuf_printf(sb, "<%s>%s", opt->name, opt->value, opt->name); if (retval != 0) break; - continue; } - - retval =lun->backend->lun_info(lun->be_lun->be_lun, sb); - - if (retval != 0) - break; retval = sbuf_printf(sb, "\n"); @@ -3130,6 +3148,28 @@ 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 @@ -3395,12 +3435,12 @@ return (retval); } -/* - * Caller must hold ctl_softc->ctl_lock. - */ int ctl_pool_acquire(struct ctl_io_pool *pool) { + + mtx_assert(&control_softc->ctl_lock, MA_OWNED); + if (pool == NULL) return (-EINVAL); @@ -3412,12 +3452,12 @@ return (0); } -/* - * Caller must hold ctl_softc->ctl_lock. - */ int ctl_pool_invalidate(struct ctl_io_pool *pool) { + + mtx_assert(&control_softc->ctl_lock, MA_OWNED); + if (pool == NULL) return (-EINVAL); @@ -3426,12 +3466,12 @@ return (0); } -/* - * Caller must hold ctl_softc->ctl_lock. - */ int ctl_pool_release(struct ctl_io_pool *pool) { + + mtx_assert(&control_softc->ctl_lock, MA_OWNED); + if (pool == NULL) return (-EINVAL); @@ -3443,14 +3483,13 @@ return (0); } -/* - * Must be called with ctl_softc->ctl_lock held. - */ void ctl_pool_free(struct ctl_softc *ctl_softc, struct ctl_io_pool *pool) { union ctl_io *cur_io, *next_io; + mtx_assert(&ctl_softc->ctl_lock, MA_OWNED); + for (cur_io = (union ctl_io *)STAILQ_FIRST(&pool->free_queue); cur_io != NULL; cur_io = next_io) { next_io = (union ctl_io *)STAILQ_NEXT(&cur_io->io_hdr, @@ -4392,7 +4431,6 @@ /* * Delete a LUN. * Assumptions: - * - caller holds ctl_softc->ctl_lock. * - LUN has already been marked invalid and any pending I/O has been taken * care of. */ @@ -4409,6 +4447,8 @@ softc = lun->ctl_softc; + mtx_assert(&softc->ctl_lock, MA_OWNED); + STAILQ_REMOVE(&softc->lun_list, lun, ctl_lun, links); ctl_clear_mask(softc->ctl_lun_mask, lun->lun); @@ -4425,9 +4465,14 @@ */ for (io = (union ctl_io *)STAILQ_FIRST(&softc->rtr_queue); io != NULL; io = next_io) { + uint32_t targ_lun; + next_io = (union ctl_io *)STAILQ_NEXT(&io->io_hdr, links); + targ_lun = io->io_hdr.nexus.targ_lun; + if (io->io_hdr.nexus.lun_map_fn != NULL) + targ_lun = io->io_hdr.nexus.lun_map_fn(io->io_hdr.nexus.lun_map_arg, targ_lun); if ((io->io_hdr.nexus.targ_target.id == lun->target.id) - && (io->io_hdr.nexus.targ_lun == lun->lun)) + && (targ_lun == lun->lun)) STAILQ_REMOVE(&softc->rtr_queue, &io->io_hdr, ctl_io_hdr, links); } @@ -8240,12 +8285,16 @@ struct ctl_lun *lun; struct ctl_softc *softc; int i; + uint32_t targ_lun; softc = control_softc; mtx_lock(&softc->ctl_lock); - lun = softc->ctl_luns[msg->hdr.nexus.targ_lun]; + targ_lun = msg->hdr.nexus.targ_lun; + if (msg->hdr.nexus.lun_map_fn != NULL) + targ_lun = msg->hdr.nexus.lun_map_fn(msg->hdr.nexus.lun_map_arg, targ_lun); + lun = softc->ctl_luns[targ_lun]; switch(msg->pr.pr_info.action) { case CTL_PR_REG_KEY: if (!lun->per_res[msg->pr.pr_info.residx].registered) { @@ -8594,7 +8643,7 @@ int num_luns, retval; uint32_t alloc_len, lun_datalen; int num_filled, well_known; - uint32_t initidx; + uint32_t initidx, targ_lun_id, lun_id; retval = CTL_RETVAL_COMPLETE; well_known = 0; @@ -8655,63 +8704,47 @@ lun_data = (struct scsi_report_luns_data *)ctsio->kern_data_ptr; ctsio->kern_sg_entries = 0; - if (lun_datalen < alloc_len) { - ctsio->residual = alloc_len - lun_datalen; - ctsio->kern_data_len = lun_datalen; - ctsio->kern_total_len = lun_datalen; - } else { - ctsio->residual = 0; - ctsio->kern_data_len = alloc_len; - ctsio->kern_total_len = alloc_len; - } - ctsio->kern_data_resid = 0; - ctsio->kern_rel_offset = 0; - ctsio->kern_sg_entries = 0; - initidx = ctl_get_initindex(&ctsio->io_hdr.nexus); - /* - * We set this to the actual data length, regardless of how much - * space we actually have to return results. If the user looks at - * this value, he'll know whether or not he allocated enough space - * and reissue the command if necessary. We don't support well - * known logical units, so if the user asks for that, return none. - */ - scsi_ulto4b(lun_datalen - 8, lun_data->length); - mtx_lock(&control_softc->ctl_lock); - for (num_filled = 0, lun = STAILQ_FIRST(&control_softc->lun_list); - (lun != NULL) && (num_filled < num_luns); - lun = STAILQ_NEXT(lun, links)) { + for (targ_lun_id = 0, num_filled = 0; targ_lun_id < CTL_MAX_LUNS && num_filled < num_luns; targ_lun_id++) { + lun_id = targ_lun_id; + if (ctsio->io_hdr.nexus.lun_map_fn != NULL) + lun_id = ctsio->io_hdr.nexus.lun_map_fn(ctsio->io_hdr.nexus.lun_map_arg, lun_id); + if (lun_id >= CTL_MAX_LUNS) + continue; + lun = control_softc->ctl_luns[lun_id]; + if (lun == NULL) + continue; - if (lun->lun <= 0xff) { + if (targ_lun_id <= 0xff) { /* * Peripheral addressing method, bus number 0. */ lun_data->luns[num_filled].lundata[0] = RPL_LUNDATA_ATYP_PERIPH; - lun_data->luns[num_filled].lundata[1] = lun->lun; + lun_data->luns[num_filled].lundata[1] = targ_lun_id; num_filled++; - } else if (lun->lun <= 0x3fff) { + } else if (targ_lun_id <= 0x3fff) { /* * Flat addressing method. */ lun_data->luns[num_filled].lundata[0] = RPL_LUNDATA_ATYP_FLAT | - (lun->lun & RPL_LUNDATA_FLAT_LUN_MASK); + (targ_lun_id & RPL_LUNDATA_FLAT_LUN_MASK); #ifdef OLDCTLHEADERS (SRLD_ADDR_FLAT << SRLD_ADDR_SHIFT) | - (lun->lun & SRLD_BUS_LUN_MASK); + (targ_lun_id & SRLD_BUS_LUN_MASK); #endif lun_data->luns[num_filled].lundata[1] = #ifdef OLDCTLHEADERS - lun->lun >> SRLD_BUS_LUN_BITS; + targ_lun_id >> SRLD_BUS_LUN_BITS; #endif - lun->lun >> RPL_LUNDATA_FLAT_LUN_BITS; + targ_lun_id >> RPL_LUNDATA_FLAT_LUN_BITS; num_filled++; } else { printf("ctl_report_luns: bogus LUN number %jd, " - "skipping\n", (intmax_t)lun->lun); + "skipping\n", (intmax_t)targ_lun_id); } /* * According to SPC-3, rev 14 section 6.21: @@ -8736,6 +8769,35 @@ mtx_unlock(&control_softc->ctl_lock); /* + * It's quite possible that we've returned fewer LUNs than we allocated + * space for. Trim it. + */ + lun_datalen = sizeof(*lun_data) + + (num_filled * sizeof(struct scsi_report_luns_lundata)); + + if (lun_datalen < alloc_len) { + ctsio->residual = alloc_len - lun_datalen; + ctsio->kern_data_len = lun_datalen; + ctsio->kern_total_len = lun_datalen; + } else { + ctsio->residual = 0; + ctsio->kern_data_len = alloc_len; + ctsio->kern_total_len = alloc_len; + } + ctsio->kern_data_resid = 0; + ctsio->kern_rel_offset = 0; + ctsio->kern_sg_entries = 0; + + /* + * We set this to the actual data length, regardless of how much + * space we actually have to return results. If the user looks at + * this value, he'll know whether or not he allocated enough space + * and reissue the command if necessary. We don't support well + * known logical units, so if the user asks for that, return none. + */ + scsi_ulto4b(lun_datalen - 8, lun_data->length); + + /* * We can only return SCSI_STATUS_CHECK_COND when we can't satisfy * this request. */ @@ -9087,6 +9149,14 @@ int devid_len; ctl_softc = control_softc; + + mtx_lock(&ctl_softc->ctl_lock); + fe = ctl_softc->ctl_ports[ctl_port_idx(ctsio->io_hdr.nexus.targ_port)]; + mtx_unlock(&ctl_softc->ctl_lock); + + if (fe->devid != NULL) + return ((fe->devid)(ctsio, alloc_len)); + lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr; devid_len = sizeof(struct scsi_vpd_device_id) + @@ -9147,8 +9217,6 @@ mtx_lock(&ctl_softc->ctl_lock); - fe = ctl_softc->ctl_ports[ctl_port_idx(ctsio->io_hdr.nexus.targ_port)]; - /* * For Fibre channel, */ @@ -9803,7 +9871,6 @@ /* * Check for blockage or overlaps against the OOA (Order Of Arrival) queue. * Assumptions: - * - caller holds ctl_lock * - pending_io is generally either incoming, or on the blocked queue * - starting I/O is the I/O we want to start the check with. */ @@ -9814,6 +9881,8 @@ union ctl_io *ooa_io; ctl_action action; + mtx_assert(&control_softc->ctl_lock, MA_OWNED); + /* * Run back along the OOA queue, starting with the current * blocked I/O and going through every I/O before it on the @@ -9854,13 +9923,14 @@ * Assumptions: * - An I/O has just completed, and has been removed from the per-LUN OOA * queue, so some items on the blocked queue may now be unblocked. - * - The caller holds ctl_softc->ctl_lock */ static int ctl_check_blocked(struct ctl_lun *lun) { union ctl_io *cur_blocked, *next_blocked; + mtx_assert(&control_softc->ctl_lock, MA_OWNED); + /* * Run forward from the head of the blocked queue, checking each * entry against the I/Os prior to it on the OOA queue to see if @@ -10372,7 +10442,7 @@ struct ctl_lun *lun; struct ctl_cmd_entry *entry; uint8_t opcode; - uint32_t initidx; + uint32_t initidx, targ_lun; int retval; retval = 0; @@ -10383,9 +10453,12 @@ mtx_lock(&ctl_softc->ctl_lock); - if ((ctsio->io_hdr.nexus.targ_lun < CTL_MAX_LUNS) - && (ctl_softc->ctl_luns[ctsio->io_hdr.nexus.targ_lun] != NULL)) { - lun = ctl_softc->ctl_luns[ctsio->io_hdr.nexus.targ_lun]; + targ_lun = ctsio->io_hdr.nexus.targ_lun; + if (ctsio->io_hdr.nexus.lun_map_fn != NULL) + targ_lun = ctsio->io_hdr.nexus.lun_map_fn(ctsio->io_hdr.nexus.lun_map_arg, targ_lun); + if ((targ_lun < CTL_MAX_LUNS) + && (ctl_softc->ctl_luns[targ_lun] != NULL)) { + lun = ctl_softc->ctl_luns[targ_lun]; /* * If the LUN is invalid, pretend that it doesn't exist. * It will go away as soon as all pending I/O has been @@ -10425,6 +10498,7 @@ ctl_set_unsupported_lun(ctsio); mtx_unlock(&ctl_softc->ctl_lock); ctl_done((union ctl_io *)ctsio); + CTL_DEBUG_PRINT(("ctl_scsiio_precheck: bailing out due to invalid LUN\n")); goto bailout; } else { /* @@ -10791,6 +10865,7 @@ char printbuf[128]; #endif int found; + uint32_t targ_lun; ctl_softc = control_softc; found = 0; @@ -10798,9 +10873,12 @@ /* * Look up the LUN. */ - if ((io->io_hdr.nexus.targ_lun < CTL_MAX_LUNS) - && (ctl_softc->ctl_luns[io->io_hdr.nexus.targ_lun] != NULL)) - lun = ctl_softc->ctl_luns[io->io_hdr.nexus.targ_lun]; + targ_lun = io->io_hdr.nexus.targ_lun; + if (io->io_hdr.nexus.lun_map_fn != NULL) + targ_lun = io->io_hdr.nexus.lun_map_fn(io->io_hdr.nexus.lun_map_arg, targ_lun); + if ((targ_lun < CTL_MAX_LUNS) + && (ctl_softc->ctl_luns[targ_lun] != NULL)) + lun = ctl_softc->ctl_luns[targ_lun]; else goto bailout; @@ -10924,8 +11002,6 @@ } /* - * Assumptions: caller holds ctl_softc->ctl_lock - * * This routine cannot block! It must be callable from an interrupt * handler as well as from the work thread. */ @@ -10934,6 +11010,8 @@ { union ctl_io *io, *next_io; + mtx_assert(&ctl_softc->ctl_lock, MA_OWNED); + CTL_DEBUG_PRINT(("ctl_run_task_queue\n")); for (io = (union ctl_io *)STAILQ_FIRST(&ctl_softc->task_queue); @@ -10990,6 +11068,8 @@ int retval; targ_lun = io->io_hdr.nexus.targ_lun; + if (io->io_hdr.nexus.lun_map_fn != NULL) + targ_lun = io->io_hdr.nexus.lun_map_fn(io->io_hdr.nexus.lun_map_arg, targ_lun); if ((targ_lun < CTL_MAX_LUNS) && (ctl_softc->ctl_luns[targ_lun] != NULL)) @@ -11064,7 +11144,7 @@ (uintmax_t)io->io_hdr.nexus.initid.id, io->io_hdr.nexus.targ_port, (uintmax_t)io->io_hdr.nexus.targ_target.id, - io->io_hdr.nexus.targ_lun, + io->io_hdr.nexus.targ_lun /* XXX */, (io->io_hdr.io_type == CTL_IO_TASK) ? io->taskio.tag_num : io->scsiio.tag_num); STAILQ_REMOVE(&ctl_softc->task_queue, &io->io_hdr, @@ -11088,10 +11168,14 @@ int free_io; struct ctl_lun *lun; struct ctl_softc *ctl_softc; + uint32_t targ_lun; ctl_softc = control_softc; - lun = ctl_softc->ctl_luns[io->io_hdr.nexus.targ_lun]; + targ_lun = io->io_hdr.nexus.targ_lun; + if (io->io_hdr.nexus.lun_map_fn != NULL) + targ_lun = io->io_hdr.nexus.lun_map_fn(io->io_hdr.nexus.lun_map_arg, targ_lun); + lun = ctl_softc->ctl_luns[targ_lun]; switch (io->io_hdr.msg_type) { case CTL_MSG_SERIALIZE: @@ -11235,14 +11319,13 @@ return (filtered_pattern); } -/* - * Called with the CTL lock held. - */ static void ctl_inject_error(struct ctl_lun *lun, union ctl_io *io) { struct ctl_error_desc *desc, *desc2; + mtx_assert(&control_softc->ctl_lock, MA_OWNED); + STAILQ_FOREACH_SAFE(desc, &lun->error_list, links, desc2) { ctl_lun_error_pattern pattern; /* @@ -11312,14 +11395,13 @@ } #endif /* CTL_IO_DELAY */ -/* - * Assumption: caller does NOT hold ctl_lock - */ void ctl_datamove(union ctl_io *io) { void (*fe_datamove)(union ctl_io *io); + mtx_assert(&control_softc->ctl_lock, MA_NOTOWNED); + CTL_DEBUG_PRINT(("ctl_datamove\n")); #ifdef CTL_TIME_IO @@ -12165,8 +12247,6 @@ * first. Once that is complete, the data gets DMAed into the remote * controller's memory. For reads, we DMA from the remote controller's * memory into our memory first, and then move it out to the FETD. - * - * Should be called without the ctl_lock held. */ static void ctl_datamove_remote(union ctl_io *io) @@ -12175,6 +12255,8 @@ softc = control_softc; + mtx_assert(&softc->ctl_lock, MA_NOTOWNED); + /* * Note that we look for an aborted I/O here, but don't do some of * the other checks that ctl_datamove() normally does. We don't @@ -12649,7 +12731,7 @@ { struct ctl_lun *lun; struct ctl_softc *ctl_softc; - uint32_t initidx; + uint32_t initidx, targ_lun; ctl_softc = control_softc; @@ -12668,9 +12750,12 @@ * If we don't have a LUN for this, just toss the sense * information. */ - if ((io->io_hdr.nexus.targ_lun < CTL_MAX_LUNS) - && (ctl_softc->ctl_luns[io->io_hdr.nexus.targ_lun] != NULL)) - lun = ctl_softc->ctl_luns[io->io_hdr.nexus.targ_lun]; + targ_lun = io->io_hdr.nexus.targ_lun; + if (io->io_hdr.nexus.lun_map_fn != NULL) + targ_lun = io->io_hdr.nexus.lun_map_fn(io->io_hdr.nexus.lun_map_arg, targ_lun); + if ((targ_lun < CTL_MAX_LUNS) + && (ctl_softc->ctl_luns[targ_lun] != NULL)) + lun = ctl_softc->ctl_luns[targ_lun]; else goto bailout; @@ -13071,6 +13156,8 @@ { ctl_ha_comp_status ret = CTL_HA_COMP_STATUS_OK; + printf("%s: go\n", __func__); + // UNKNOWN->HA or UNKNOWN->SINGLE (bootstrap) if (c->state == CTL_HA_STATE_UNKNOWN ) { ctl_is_single = 0; diff -urN p4/freebsd/src/sys/cam/ctl/ctl.h p4/iscsi/sys/cam/ctl/ctl.h --- p4/freebsd/src/sys/cam/ctl/ctl.h 2013-04-20 20:02:13.000000000 +0200 +++ p4/iscsi/sys/cam/ctl/ctl.h 2013-07-23 23:42:57.000000000 +0200 @@ -52,6 +52,7 @@ CTL_PORT_SCSI = 0x02, CTL_PORT_IOCTL = 0x04, CTL_PORT_INTERNAL = 0x08, + CTL_PORT_ISCSI = 0x10, CTL_PORT_ALL = 0xff, CTL_PORT_ISC = 0x100 // FC port for inter-shelf communication } ctl_port_type; diff -urN p4/freebsd/src/sys/cam/ctl/ctl_backend.h p4/iscsi/sys/cam/ctl/ctl_backend.h --- p4/freebsd/src/sys/cam/ctl/ctl_backend.h 2013-04-20 20:02:13.000000000 +0200 +++ p4/iscsi/sys/cam/ctl/ctl_backend.h 2013-07-23 23:42:57.000000000 +0200 @@ -173,6 +173,12 @@ * The links field is for CTL internal use only, and should not be used by * the backend. */ +struct ctl_be_lun_option { + STAILQ_ENTRY(ctl_be_lun_option) links; + char *name; + char *value; +}; + struct ctl_be_lun { uint8_t lun_type; /* passed to CTL */ ctl_backend_lun_flags flags; /* passed to CTL */ @@ -187,6 +193,7 @@ be_lun_config_t lun_config_status; /* passed to CTL */ struct ctl_backend_driver *be; /* passed to CTL */ void *ctl_lun; /* used by CTL */ + STAILQ_HEAD(, ctl_be_lun_option) options; /* passed to CTL */ STAILQ_ENTRY(ctl_be_lun) links; /* used by CTL */ }; diff -urN p4/freebsd/src/sys/cam/ctl/ctl_backend_block.c p4/iscsi/sys/cam/ctl/ctl_backend_block.c --- p4/freebsd/src/sys/cam/ctl/ctl_backend_block.c 2013-04-20 20:02:14.000000000 +0200 +++ p4/iscsi/sys/cam/ctl/ctl_backend_block.c 2013-07-23 23:42:57.000000000 +0200 @@ -1639,6 +1639,7 @@ STAILQ_INIT(&be_lun->input_queue); STAILQ_INIT(&be_lun->config_write_queue); STAILQ_INIT(&be_lun->datamove_queue); + STAILQ_INIT(&be_lun->ctl_be_lun.options); sprintf(be_lun->lunname, "cblk%d", softc->num_luns); mtx_init(&be_lun->lock, be_lun->lunname, NULL, MTX_DEF); @@ -1740,6 +1741,16 @@ } num_threads = tmp_num_threads; + } else if (strcmp(req->kern_be_args[i].kname, "file") != 0 && + strcmp(req->kern_be_args[i].kname, "dev") != 0) { + struct ctl_be_lun_option *opt; + + opt = malloc(sizeof(*opt), M_CTLBLK, M_WAITOK); + opt->name = malloc(strlen(req->kern_be_args[i].kname) + 1, M_CTLBLK, M_WAITOK); + strcpy(opt->name, req->kern_be_args[i].kname); + opt->value = malloc(strlen(req->kern_be_args[i].kvalue) + 1, M_CTLBLK, M_WAITOK); + strcpy(opt->value, req->kern_be_args[i].kvalue); + STAILQ_INSERT_TAIL(&be_lun->ctl_be_lun.options, opt, links); } } diff -urN p4/freebsd/src/sys/cam/ctl/ctl_backend_ramdisk.c p4/iscsi/sys/cam/ctl/ctl_backend_ramdisk.c --- p4/freebsd/src/sys/cam/ctl/ctl_backend_ramdisk.c 2013-07-24 07:52:02.000000000 +0200 +++ p4/iscsi/sys/cam/ctl/ctl_backend_ramdisk.c 2013-07-25 19:17:08.000000000 +0200 @@ -491,7 +491,7 @@ struct ctl_lun_create_params *params; uint32_t blocksize; char tmpstr[32]; - int retval; + int i, retval; retval = 0; params = &req->reqdata.create; @@ -509,6 +509,7 @@ sizeof(*be_lun)); goto bailout_error; } + STAILQ_INIT(&be_lun->ctl_be_lun.options); if (params->flags & CTL_LUN_FLAG_DEV_TYPE) be_lun->ctl_be_lun.lun_type = params->device_type; @@ -545,6 +546,17 @@ be_lun->softc = softc; + for (i = 0; i < req->num_be_args; i++) { + struct ctl_be_lun_option *opt; + + opt = malloc(sizeof(*opt), M_RAMDISK, M_WAITOK); + opt->name = malloc(strlen(req->kern_be_args[i].kname) + 1, M_RAMDISK, M_WAITOK); + strcpy(opt->name, req->kern_be_args[i].kname); + opt->value = malloc(strlen(req->kern_be_args[i].kvalue) + 1, M_RAMDISK, M_WAITOK); + strcpy(opt->value, req->kern_be_args[i].kvalue); + STAILQ_INSERT_TAIL(&be_lun->ctl_be_lun.options, opt, links); + } + be_lun->flags = CTL_BE_RAMDISK_LUN_UNCONFIGURED; be_lun->ctl_be_lun.flags = CTL_LUN_FLAG_PRIMARY; be_lun->ctl_be_lun.be_lun = be_lun; diff -urN p4/freebsd/src/sys/cam/ctl/ctl_frontend.h p4/iscsi/sys/cam/ctl/ctl_frontend.h --- p4/freebsd/src/sys/cam/ctl/ctl_frontend.h 2013-04-20 20:02:14.000000000 +0200 +++ p4/iscsi/sys/cam/ctl/ctl_frontend.h 2013-07-23 23:42:57.000000000 +0200 @@ -49,6 +49,9 @@ typedef void (*port_func_t)(void *onoff_arg); typedef int (*targ_func_t)(void *arg, struct ctl_id targ_id); typedef int (*lun_func_t)(void *arg, struct ctl_id targ_id, int lun_id); +typedef int (*fe_ioctl_t)(struct cdev *dev, u_long cmd, caddr_t addr, int flag, + struct thread *td); +typedef int (*fe_devid_t)(struct ctl_scsiio *ctsio, int alloc_len); /* * The ctl_frontend structure is the registration mechanism between a FETD @@ -213,6 +216,8 @@ targ_func_t targ_disable; /* passed to CTL */ lun_func_t lun_enable; /* passed to CTL */ lun_func_t lun_disable; /* passed to CTL */ + fe_ioctl_t ioctl; /* passed to CTL */ + fe_devid_t devid; /* passed to CTL */ void *targ_lun_arg; /* passed to CTL */ void (*fe_datamove)(union ctl_io *io); /* passed to CTL */ void (*fe_done)(union ctl_io *io); /* passed to CTL */ diff -urN p4/freebsd/src/sys/cam/ctl/ctl_frontend_iscsi.c p4/iscsi/sys/cam/ctl/ctl_frontend_iscsi.c --- p4/freebsd/src/sys/cam/ctl/ctl_frontend_iscsi.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/sys/cam/ctl/ctl_frontend_iscsi.c 2013-08-18 13:07:25.000000000 +0200 @@ -0,0 +1,2637 @@ +/*- + * 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$ + */ + +/* + * CTL frontend for the iSCSI protocol. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../dev/iscsi/icl.h" +#include "ctl_frontend_iscsi.h" + +#ifdef ICL_KERNEL_PROXY +#include +#endif + +MALLOC_DEFINE(M_CFISCSI, "cfiscsi", "Memory used for CTL iSCSI frontend"); +static uma_zone_t cfiscsi_data_wait_zone; + +SYSCTL_NODE(_kern_cam_ctl, OID_AUTO, iscsi, CTLFLAG_RD, 0, + "CAM Target Layer iSCSI Frontend"); +static int debug = 3; +TUNABLE_INT("kern.cam.ctl.iscsi.debug", &debug); +SYSCTL_INT(_kern_cam_ctl_iscsi, OID_AUTO, debug, CTLFLAG_RW, + &debug, 1, "Enable debug messages"); +static int ping_timeout = 5; +TUNABLE_INT("kern.cam.ctl.iscsi.ping_timeout", &ping_timeout); +SYSCTL_INT(_kern_cam_ctl_iscsi, OID_AUTO, ping_timeout, CTLFLAG_RW, + &ping_timeout, 5, "Interval between ping (NOP-Out) requests, in seconds"); +static int login_timeout = 60; +TUNABLE_INT("kern.cam.ctl.iscsi.login_timeout", &login_timeout); +SYSCTL_INT(_kern_cam_ctl_iscsi, OID_AUTO, login_timeout, CTLFLAG_RW, + &login_timeout, 60, "Time to wait for ctld(8) to finish Login Phase, in seconds"); +static int maxcmdsn_delta = 256; +TUNABLE_INT("kern.cam.ctl.iscsi.maxcmdsn_delta", &maxcmdsn_delta); +SYSCTL_INT(_kern_cam_ctl_iscsi, OID_AUTO, maxcmdsn_delta, CTLFLAG_RW, + &maxcmdsn_delta, 256, "Number of commands the initiator can send " + "without confirmation"); + +#define CFISCSI_DEBUG(X, ...) \ + if (debug > 1) { \ + printf("%s: " X "\n", __func__, ## __VA_ARGS__);\ + } while (0) + +#define CFISCSI_WARN(X, ...) \ + if (debug > 0) { \ + printf("WARNING: %s: " X "\n", \ + __func__, ## __VA_ARGS__); \ + } while (0) + +#define CFISCSI_SESSION_DEBUG(S, X, ...) \ + if (debug > 1) { \ + printf("%s: %s (%s): " X "\n", \ + __func__, S->cs_initiator_addr, \ + S->cs_initiator_name, ## __VA_ARGS__); \ + } while (0) + +#define CFISCSI_SESSION_WARN(S, X, ...) \ + if (debug > 0) { \ + printf("WARNING: %s (%s): " X "\n", \ + S->cs_initiator_addr, \ + S->cs_initiator_name, ## __VA_ARGS__); \ + } while (0) + +#define CFISCSI_SESSION_LOCK(X) mtx_lock(&X->cs_lock) +#define CFISCSI_SESSION_UNLOCK(X) mtx_unlock(&X->cs_lock) +#define CFISCSI_SESSION_LOCK_ASSERT(X) mtx_assert(&X->cs_lock, MA_OWNED) + +#define CONN_SESSION(X) ((struct cfiscsi_session *)(X)->ic_prv0) +#define PDU_SESSION(X) CONN_SESSION((X)->ip_conn) +#define PDU_EXPDATASN(X) (X)->ip_prv0 +#define PDU_TOTAL_TRANSFER_LEN(X) (X)->ip_prv1 +#define PDU_R2TSN(X) (X)->ip_prv2 + +int cfiscsi_init(void); +static void cfiscsi_online(void *arg); +static void cfiscsi_offline(void *arg); +static int cfiscsi_targ_enable(void *arg, struct ctl_id targ_id); +static int cfiscsi_targ_disable(void *arg, struct ctl_id targ_id); +static int cfiscsi_lun_enable(void *arg, + struct ctl_id target_id, int lun_id); +static int cfiscsi_lun_disable(void *arg, + struct ctl_id target_id, int lun_id); +static int cfiscsi_ioctl(struct cdev *dev, + u_long cmd, caddr_t addr, int flag, struct thread *td); +static int cfiscsi_devid(struct ctl_scsiio *ctsio, int alloc_len); +static void cfiscsi_datamove(union ctl_io *io); +static void cfiscsi_done(union ctl_io *io); +static uint32_t cfiscsi_map_lun(void *arg, uint32_t lun); +static void cfiscsi_pdu_update_cmdsn(const struct icl_pdu *request); +static void cfiscsi_pdu_handle_nop_out(struct icl_pdu *request); +static void cfiscsi_pdu_handle_scsi_command(struct icl_pdu *request); +static void cfiscsi_pdu_handle_task_request(struct icl_pdu *request); +static void cfiscsi_pdu_handle_data_out(struct icl_pdu *request); +static void cfiscsi_pdu_handle_logout_request(struct icl_pdu *request); +static void cfiscsi_session_terminate(struct cfiscsi_session *cs); +static struct cfiscsi_target *cfiscsi_target_find(struct cfiscsi_softc + *softc, const char *name); +static void cfiscsi_target_release(struct cfiscsi_target *ct); +static void cfiscsi_session_delete(struct cfiscsi_session *cs); + +static struct cfiscsi_softc cfiscsi_softc; +extern struct ctl_softc *control_softc; + +static int cfiscsi_module_event_handler(module_t, int /*modeventtype_t*/, void *); + +static moduledata_t cfiscsi_moduledata = { + "ctlcfiscsi", + cfiscsi_module_event_handler, + NULL +}; + +DECLARE_MODULE(ctlcfiscsi, cfiscsi_moduledata, SI_SUB_CONFIGURE, SI_ORDER_FOURTH); +MODULE_VERSION(ctlcfiscsi, 1); +MODULE_DEPEND(ctlcfiscsi, ctl, 1, 1, 1); +MODULE_DEPEND(ctlcfiscsi, icl, 1, 1, 1); + +static struct icl_pdu * +cfiscsi_pdu_new_response(struct icl_pdu *request, int flags) +{ + + return (icl_pdu_new_bhs(request->ip_conn, flags)); +} + +static void +cfiscsi_pdu_update_cmdsn(const struct icl_pdu *request) +{ + const struct iscsi_bhs_scsi_command *bhssc; + struct cfiscsi_session *cs; + uint32_t cmdsn, expstatsn; + + cs = PDU_SESSION(request); + + /* + * Every incoming PDU - not just NOP-Out - resets the ping timer. + * The purpose of the timeout is to reset the connection when it stalls; + * we don't want this to happen when NOP-In or NOP-Out ends up delayed + * in some queue. + * + * XXX: Locking? + */ + cs->cs_timeout = 0; + + /* + * Data-Out PDUs don't contain CmdSN. + */ + if ((request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) == + ISCSI_BHS_OPCODE_SCSI_DATA_OUT) + return; + + /* + * We're only using fields common for all the request + * (initiator -> target) PDUs. + */ + bhssc = (const struct iscsi_bhs_scsi_command *)request->ip_bhs; + cmdsn = ntohl(bhssc->bhssc_cmdsn); + expstatsn = ntohl(bhssc->bhssc_expstatsn); + + CFISCSI_SESSION_LOCK(cs); +#if 0 + if (expstatsn != cs->cs_statsn) { + CFISCSI_SESSION_DEBUG(cs, "received PDU with ExpStatSN %d, " + "while current StatSN is %d", expstatsn, + cs->cs_statsn); + } +#endif + + /* + * XXX: The target MUST silently ignore any non-immediate command + * outside of this range or non-immediate duplicates within + * the range. + */ + if (cmdsn != cs->cs_cmdsn) { + CFISCSI_SESSION_WARN(cs, "received PDU with CmdSN %d, " + "while expected CmdSN was %d", cmdsn, cs->cs_cmdsn); + cs->cs_cmdsn = cmdsn + 1; + CFISCSI_SESSION_UNLOCK(cs); + return; + } + + /* + * XXX: The CmdSN of the rejected command PDU (if it is a non-immediate + * command) MUST NOT be considered received by the target + * (i.e., a command sequence gap must be assumed for the CmdSN) + */ + + if ((request->ip_bhs->bhs_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0) + cs->cs_cmdsn++; + + CFISCSI_SESSION_UNLOCK(cs); +} + +static void +cfiscsi_pdu_handle(struct icl_pdu *request) +{ + struct cfiscsi_session *cs; + + cs = PDU_SESSION(request); + + cfiscsi_pdu_update_cmdsn(request); + + /* + * Handle the PDU; this includes e.g. receiving the remaining + * part of PDU and submitting the SCSI command to CTL + * or queueing a reply. The handling routine is responsible + * for freeing the PDU when it's no longer needed. + */ + switch (request->ip_bhs->bhs_opcode & + ~ISCSI_BHS_OPCODE_IMMEDIATE) { + case ISCSI_BHS_OPCODE_NOP_OUT: + cfiscsi_pdu_handle_nop_out(request); + break; + case ISCSI_BHS_OPCODE_SCSI_COMMAND: + cfiscsi_pdu_handle_scsi_command(request); + break; + case ISCSI_BHS_OPCODE_TASK_REQUEST: + cfiscsi_pdu_handle_task_request(request); + break; + case ISCSI_BHS_OPCODE_SCSI_DATA_OUT: + cfiscsi_pdu_handle_data_out(request); + break; + case ISCSI_BHS_OPCODE_LOGOUT_REQUEST: + cfiscsi_pdu_handle_logout_request(request); + break; + default: + CFISCSI_SESSION_WARN(cs, "received PDU with unsupported " + "opcode 0x%x; dropping connection", + request->ip_bhs->bhs_opcode); + cfiscsi_session_terminate(cs); + icl_pdu_free(request); + } + +} + +static void +cfiscsi_receive_callback(struct icl_pdu *request) +{ + struct cfiscsi_session *cs; + + cs = PDU_SESSION(request); + +#ifdef ICL_KERNEL_PROXY + if (cs->cs_waiting_for_ctld || cs->cs_login_phase) { + if (cs->cs_login_pdu == NULL) + cs->cs_login_pdu = request; + else + icl_pdu_free(request); + cv_signal(&cs->cs_login_cv); + return; + } +#endif + + cfiscsi_pdu_handle(request); +} + +static void +cfiscsi_error_callback(struct icl_conn *ic) +{ + struct cfiscsi_session *cs; + + cs = CONN_SESSION(ic); + + CFISCSI_SESSION_WARN(cs, "connection error; dropping connection"); + cfiscsi_session_terminate(cs); +} + +static int +cfiscsi_pdu_prepare(struct icl_pdu *response) +{ + struct cfiscsi_session *cs; + struct iscsi_bhs_scsi_response *bhssr; + bool advance_statsn = true; + + cs = PDU_SESSION(response); + + CFISCSI_SESSION_LOCK_ASSERT(cs); + + /* + * We're only using fields common for all the response + * (target -> initiator) PDUs. + */ + bhssr = (struct iscsi_bhs_scsi_response *)response->ip_bhs; + + /* + * 10.8.3: "The StatSN for this connection is not advanced + * after this PDU is sent." + */ + if (bhssr->bhssr_opcode == ISCSI_BHS_OPCODE_R2T) + advance_statsn = false; + + /* + * 10.19.2: "However, when the Initiator Task Tag is set to 0xffffffff, + * StatSN for the connection is not advanced after this PDU is sent." + */ + if (bhssr->bhssr_opcode == ISCSI_BHS_OPCODE_NOP_IN && + bhssr->bhssr_initiator_task_tag == 0xffffffff) + advance_statsn = false; + + /* + * See the comment below - StatSN is not meaningful and must + * not be advanced. + */ + if (bhssr->bhssr_opcode == ISCSI_BHS_OPCODE_SCSI_DATA_IN) + advance_statsn = false; + + /* + * 10.7.3: "The fields StatSN, Status, and Residual Count + * only have meaningful content if the S bit is set to 1." + */ + if (bhssr->bhssr_opcode != ISCSI_BHS_OPCODE_SCSI_DATA_IN) + bhssr->bhssr_statsn = htonl(cs->cs_statsn); + bhssr->bhssr_expcmdsn = htonl(cs->cs_cmdsn); + bhssr->bhssr_maxcmdsn = htonl(cs->cs_cmdsn + maxcmdsn_delta); + + if (advance_statsn) + cs->cs_statsn++; + + return (0); +} + +static void +cfiscsi_pdu_queue(struct icl_pdu *response) +{ + struct cfiscsi_session *cs; + + cs = PDU_SESSION(response); + + CFISCSI_SESSION_LOCK(cs); + cfiscsi_pdu_prepare(response); + icl_pdu_queue(response); + CFISCSI_SESSION_UNLOCK(cs); +} + +static uint32_t +cfiscsi_decode_lun(uint64_t encoded) +{ + uint8_t lun[8]; + uint32_t result; + + /* + * The LUN field in iSCSI PDUs may look like an ordinary 64 bit number, + * but is in fact an evil, multidimensional structure defined + * in SCSI Architecture Model 5 (SAM-5), section 4.6. + */ + memcpy(lun, &encoded, sizeof(lun)); + switch (lun[0] & 0xC0) { + case 0x00: + if ((lun[0] & 0x3f) != 0 || lun[2] != 0 || lun[3] != 0 || + lun[4] != 0 || lun[5] != 0 || lun[6] != 0 || lun[7] != 0) { + CFISCSI_WARN("malformed LUN " + "(peripheral device addressing method): 0x%jx", + (uintmax_t)encoded); + result = 0xffffffff; + break; + } + result = lun[1]; + break; + case 0x40: + if (lun[2] != 0 || lun[3] != 0 || lun[4] != 0 || lun[5] != 0 || + lun[6] != 0 || lun[7] != 0) { + CFISCSI_WARN("malformed LUN " + "(flat address space addressing method): 0x%jx", + (uintmax_t)encoded); + result = 0xffffffff; + break; + } + result = ((lun[0] & 0x3f) << 8) + lun[1]; + break; + case 0xC0: + if (lun[0] != 0xD2 || lun[4] != 0 || lun[5] != 0 || + lun[6] != 0 || lun[7] != 0) { + CFISCSI_WARN("malformed LUN (extended flat " + "address space addressing method): 0x%jx", + (uintmax_t)encoded); + result = 0xffffffff; + break; + } + result = (lun[1] << 16) + (lun[2] << 8) + lun[3]; + default: + CFISCSI_WARN("unsupported LUN format 0x%jx", + (uintmax_t)encoded); + result = 0xffffffff; + break; + } + + return (result); +} + +static void +cfiscsi_pdu_handle_nop_out(struct icl_pdu *request) +{ + struct cfiscsi_session *cs; + struct iscsi_bhs_nop_out *bhsno; + struct iscsi_bhs_nop_in *bhsni; + struct icl_pdu *response; + + cs = PDU_SESSION(request); + bhsno = (struct iscsi_bhs_nop_out *)request->ip_bhs; + + if (bhsno->bhsno_initiator_task_tag == 0xffffffff) { + /* + * Nothing to do, iscsi_pdu_update_statsn() already + * zeroed the timeout. + */ + icl_pdu_free(request); + return; + } + + response = cfiscsi_pdu_new_response(request, M_NOWAIT); + if (response == NULL) { + icl_pdu_free(request); + return; + } + bhsni = (struct iscsi_bhs_nop_in *)response->ip_bhs; + bhsni->bhsni_opcode = ISCSI_BHS_OPCODE_NOP_IN; + bhsni->bhsni_flags = 0x80; + bhsni->bhsni_initiator_task_tag = bhsno->bhsno_initiator_task_tag; + bhsni->bhsni_target_transfer_tag = 0xffffffff; + +#if 0 + /* XXX */ + response->ip_data_len = request->ip_data_len; + response->ip_data_mbuf = request->ip_data_mbuf; + request->ip_data_len = 0; + request->ip_data_mbuf = NULL; +#endif + + icl_pdu_free(request); + cfiscsi_pdu_queue(response); +} + +static void +cfiscsi_pdu_handle_scsi_command(struct icl_pdu *request) +{ + struct iscsi_bhs_scsi_command *bhssc; + struct cfiscsi_session *cs; + union ctl_io *io; + int error; + + cs = PDU_SESSION(request); + bhssc = (struct iscsi_bhs_scsi_command *)request->ip_bhs; + //CFISCSI_SESSION_DEBUG(cs, "initiator task tag 0x%x", + // bhssc->bhssc_initiator_task_tag); + + if (request->ip_data_len > 0 && cs->cs_immediate_data == false) { + CFISCSI_SESSION_WARN(cs, "unsolicited data with " + "ImmediateData=No; dropping connection"); + cfiscsi_session_terminate(cs); + icl_pdu_free(request); + return; + } + io = ctl_alloc_io(cs->cs_target->ct_softc->fe.ctl_pool_ref); + if (io == NULL) { + CFISCSI_SESSION_WARN(cs, "can't allocate ctl_io"); + icl_pdu_free(request); + return; + } + ctl_zero_io(io); + io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = request; + io->io_hdr.io_type = CTL_IO_SCSI; + io->io_hdr.nexus.initid.id = cs->cs_ctl_initid; + io->io_hdr.nexus.targ_port = cs->cs_target->ct_softc->fe.targ_port; + io->io_hdr.nexus.targ_target.id = 0; + io->io_hdr.nexus.targ_lun = cfiscsi_decode_lun(bhssc->bhssc_lun); + io->io_hdr.nexus.lun_map_fn = cfiscsi_map_lun; + io->io_hdr.nexus.lun_map_arg = cs; + io->scsiio.tag_num = bhssc->bhssc_initiator_task_tag; + switch ((bhssc->bhssc_flags & BHSSC_FLAGS_ATTR)) { + case BHSSC_FLAGS_ATTR_UNTAGGED: + io->scsiio.tag_type = CTL_TAG_UNTAGGED; + break; + case BHSSC_FLAGS_ATTR_SIMPLE: + io->scsiio.tag_type = CTL_TAG_SIMPLE; + break; + case BHSSC_FLAGS_ATTR_ORDERED: + io->scsiio.tag_type = CTL_TAG_ORDERED; + break; + case BHSSC_FLAGS_ATTR_HOQ: + io->scsiio.tag_type = CTL_TAG_HEAD_OF_QUEUE; + break; + case BHSSC_FLAGS_ATTR_ACA: + io->scsiio.tag_type = CTL_TAG_ACA; + break; + default: + io->scsiio.tag_type = CTL_TAG_UNTAGGED; + CFISCSI_SESSION_WARN(cs, "unhandled tag type %d", + bhssc->bhssc_flags & BHSSC_FLAGS_ATTR); + break; + } + io->scsiio.cdb_len = sizeof(bhssc->bhssc_cdb); /* Which is 16. */ + memcpy(io->scsiio.cdb, bhssc->bhssc_cdb, sizeof(bhssc->bhssc_cdb)); + refcount_acquire(&cs->cs_outstanding_ctl_pdus); + error = ctl_queue(io); + if (error != CTL_RETVAL_COMPLETE) { + CFISCSI_SESSION_WARN(cs, "ctl_queue() failed; error %d", error); + ctl_free_io(io); + refcount_release(&cs->cs_outstanding_ctl_pdus); + icl_pdu_free(request); + } +} + +static void +cfiscsi_pdu_handle_task_request(struct icl_pdu *request) +{ + struct iscsi_bhs_task_management_request *bhstmr; + struct iscsi_bhs_task_management_response *bhstmr2; + struct icl_pdu *response; + struct cfiscsi_session *cs; + union ctl_io *io; + int error; + + cs = PDU_SESSION(request); + bhstmr = (struct iscsi_bhs_task_management_request *)request->ip_bhs; + io = ctl_alloc_io(cs->cs_target->ct_softc->fe.ctl_pool_ref); + if (io == NULL) { + CFISCSI_SESSION_WARN(cs, "can't allocate ctl_io"); + icl_pdu_free(request); + return; + } + ctl_zero_io(io); + io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = request; + io->io_hdr.io_type = CTL_IO_TASK; + io->io_hdr.nexus.initid.id = cs->cs_ctl_initid; + io->io_hdr.nexus.targ_port = cs->cs_target->ct_softc->fe.targ_port; + io->io_hdr.nexus.targ_target.id = 0; + io->io_hdr.nexus.targ_lun = cfiscsi_decode_lun(bhstmr->bhstmr_lun); + io->io_hdr.nexus.lun_map_fn = cfiscsi_map_lun; + io->io_hdr.nexus.lun_map_arg = cs; + io->taskio.tag_type = CTL_TAG_SIMPLE; /* XXX */ + + switch (bhstmr->bhstmr_function & ~0x80) { + case BHSTMR_FUNCTION_ABORT_TASK: +#if 0 + CFISCSI_SESSION_DEBUG(cs, "BHSTMR_FUNCTION_ABORT_TASK"); +#endif + io->taskio.task_action = CTL_TASK_ABORT_TASK; + io->taskio.tag_num = bhstmr->bhstmr_referenced_task_tag; + break; + case BHSTMR_FUNCTION_LOGICAL_UNIT_RESET: +#if 0 + CFISCSI_SESSION_DEBUG(cs, "BHSTMR_FUNCTION_LOGICAL_UNIT_RESET"); +#endif + io->taskio.task_action = CTL_TASK_LUN_RESET; + break; + case BHSTMR_FUNCTION_TARGET_COLD_RESET: +#if 0 + CFISCSI_SESSION_DEBUG(cs, "BHSTMR_FUNCTION_TARGET_COLD_RESET"); +#endif + io->taskio.task_action = CTL_TASK_BUS_RESET; + break; + default: + CFISCSI_SESSION_DEBUG(cs, "unsupported function 0x%x", + bhstmr->bhstmr_function & ~0x80); + ctl_free_io(io); + + response = cfiscsi_pdu_new_response(request, M_NOWAIT); + if (response == NULL) { + icl_pdu_free(request); + return; + } + bhstmr2 = (struct iscsi_bhs_task_management_response *) + response->ip_bhs; + bhstmr2->bhstmr_opcode = ISCSI_BHS_OPCODE_TASK_RESPONSE; + bhstmr2->bhstmr_flags = 0x80; + bhstmr2->bhstmr_response = + BHSTMR_RESPONSE_FUNCTION_NOT_SUPPORTED; + bhstmr2->bhstmr_initiator_task_tag = + bhstmr->bhstmr_initiator_task_tag; + icl_pdu_free(request); + cfiscsi_pdu_queue(response); + return; + } + + refcount_acquire(&cs->cs_outstanding_ctl_pdus); + error = ctl_queue(io); + if (error != CTL_RETVAL_COMPLETE) { + CFISCSI_SESSION_WARN(cs, "ctl_queue() failed; error %d", error); + ctl_free_io(io); + refcount_release(&cs->cs_outstanding_ctl_pdus); + icl_pdu_free(request); + } +} + +static bool +cfiscsi_handle_data_segment(struct icl_pdu *request, struct cfiscsi_data_wait *cdw) +{ + struct iscsi_bhs_data_out *bhsdo; + struct cfiscsi_session *cs; + struct ctl_sg_entry ctl_sg_entry, *ctl_sglist; + size_t copy_len, off, buffer_offset; + int ctl_sg_count; + union ctl_io *io; + + cs = PDU_SESSION(request); + + KASSERT((request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) == + ISCSI_BHS_OPCODE_SCSI_DATA_OUT || + (request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) == + ISCSI_BHS_OPCODE_SCSI_COMMAND, + ("bad opcode 0x%x", request->ip_bhs->bhs_opcode)); + + /* + * We're only using fields common for Data Out and SCSI Command PDUs. + */ + bhsdo = (struct iscsi_bhs_data_out *)request->ip_bhs; + + io = cdw->cdw_ctl_io; + KASSERT((io->io_hdr.flags & CTL_FLAG_DATA_MASK) != CTL_FLAG_DATA_IN, + ("CTL_FLAG_DATA_IN")); + +#if 0 + CFISCSI_SESSION_DEBUG(cs, "received %zd bytes out of %d", + request->ip_data_len, io->scsiio.kern_total_len); +#endif + + if (io->scsiio.kern_sg_entries > 0) { + ctl_sglist = (struct ctl_sg_entry *)io->scsiio.kern_data_ptr; + ctl_sg_count = io->scsiio.kern_sg_entries; + } else { + ctl_sglist = &ctl_sg_entry; + ctl_sglist->addr = io->scsiio.kern_data_ptr; + ctl_sglist->len = io->scsiio.kern_data_len; + ctl_sg_count = 1; + } +#if 0 + if (ctl_sg_count > 1) + CFISCSI_SESSION_DEBUG(cs, "ctl_sg_count = %d", ctl_sg_count); +#endif + + if ((request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) == + ISCSI_BHS_OPCODE_SCSI_DATA_OUT) + buffer_offset = ntohl(bhsdo->bhsdo_buffer_offset); + else + buffer_offset = 0; + + /* + * Make sure the offset, as sent by the initiator, matches the offset + * we're supposed to be at in the scatter-gather list. + */ + if (buffer_offset != io->scsiio.ext_data_filled) { + CFISCSI_SESSION_WARN(cs, "received bad buffer offset %zd, " + "expected %zd", buffer_offset, io->scsiio.ext_data_filled); + cfiscsi_session_terminate(cs); + return (true); + } + + off = 0; + for (;;) { + KASSERT(cdw->cdw_sg_index < ctl_sg_count, + ("cdw->cdw_sg_index >= ctl_sg_count")); + if (cdw->cdw_sg_len == 0) { + cdw->cdw_sg_addr = ctl_sglist[cdw->cdw_sg_index].addr; + cdw->cdw_sg_len = ctl_sglist[cdw->cdw_sg_index].len; + } + copy_len = icl_pdu_data_segment_length(request) - off; + if (copy_len > cdw->cdw_sg_len) + copy_len = cdw->cdw_sg_len; + + icl_pdu_get_data(request, off, cdw->cdw_sg_addr, copy_len); + cdw->cdw_sg_addr += copy_len; + cdw->cdw_sg_len -= copy_len; + off += copy_len; + io->scsiio.ext_data_filled += copy_len; + + if (cdw->cdw_sg_len == 0) { + if (cdw->cdw_sg_index == ctl_sg_count - 1) + break; + cdw->cdw_sg_index++; + } + if (off == icl_pdu_data_segment_length(request)) + break; + } + + if (off < icl_pdu_data_segment_length(request)) { + CFISCSI_SESSION_WARN(cs, "received too much data: got %zd bytes, " + "expected %zd", icl_pdu_data_segment_length(request), off); + cfiscsi_session_terminate(cs); + return (true); + } + + if (bhsdo->bhsdo_flags & BHSDO_FLAGS_F || + io->scsiio.ext_data_filled == io->scsiio.kern_total_len) { + if ((bhsdo->bhsdo_flags & BHSDO_FLAGS_F) == 0) { + CFISCSI_SESSION_WARN(cs, "got the final packet without " + "the F flag; flags = 0x%x; dropping connection", + bhsdo->bhsdo_flags); + cfiscsi_session_terminate(cs); + return (true); + } + + if (io->scsiio.ext_data_filled != io->scsiio.kern_total_len) { + if ((request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) == + ISCSI_BHS_OPCODE_SCSI_DATA_OUT) { + CFISCSI_SESSION_WARN(cs, "got the final packet, but the " + "transmitted size was %zd bytes instead of %d; " + "dropping connection", + (size_t)io->scsiio.ext_data_filled, + io->scsiio.kern_total_len); + cfiscsi_session_terminate(cs); + return (true); + } else { + /* + * For SCSI Command PDU, this just means we need to + * solicit more data by sending R2T. + */ + return (false); + } + } +#if 0 + CFISCSI_SESSION_DEBUG(cs, "no longer expecting Data-Out with target " + "transfer tag 0x%x", cdw->cdw_target_transfer_tag); +#endif + + return (true); + } + + return (false); +} + +static void +cfiscsi_pdu_handle_data_out(struct icl_pdu *request) +{ + struct iscsi_bhs_data_out *bhsdo; + struct cfiscsi_session *cs; + struct cfiscsi_data_wait *cdw = NULL; + union ctl_io *io; + bool done; + + cs = PDU_SESSION(request); + bhsdo = (struct iscsi_bhs_data_out *)request->ip_bhs; + + CFISCSI_SESSION_LOCK(cs); + TAILQ_FOREACH(cdw, &cs->cs_waiting_for_data_out, cdw_next) { +#if 0 + CFISCSI_SESSION_DEBUG(cs, "have ttt 0x%x, itt 0x%x; looking for " + "ttt 0x%x, itt 0x%x", + bhsdo->bhsdo_target_transfer_tag, + bhsdo->bhsdo_initiator_task_tag, + cdw->cdw_target_transfer_tag, cdw->cdw_initiator_task_tag)); +#endif + if (bhsdo->bhsdo_target_transfer_tag == + cdw->cdw_target_transfer_tag) + break; + } + CFISCSI_SESSION_UNLOCK(cs); + if (cdw == NULL) { + CFISCSI_SESSION_WARN(cs, "data transfer tag 0x%x, initiator task tag " + "0x%x, not found", bhsdo->bhsdo_target_transfer_tag, + bhsdo->bhsdo_initiator_task_tag); + icl_pdu_free(request); + cfiscsi_session_terminate(cs); + return; + } + + io = cdw->cdw_ctl_io; + KASSERT((io->io_hdr.flags & CTL_FLAG_DATA_MASK) != CTL_FLAG_DATA_IN, + ("CTL_FLAG_DATA_IN")); + + done = cfiscsi_handle_data_segment(request, cdw); + if (done) { + CFISCSI_SESSION_LOCK(cs); + TAILQ_REMOVE(&cs->cs_waiting_for_data_out, cdw, cdw_next); + CFISCSI_SESSION_UNLOCK(cs); + uma_zfree(cfiscsi_data_wait_zone, cdw); + io->scsiio.be_move_done(io); + } + + icl_pdu_free(request); +} + +static void +cfiscsi_pdu_handle_logout_request(struct icl_pdu *request) +{ + struct iscsi_bhs_logout_request *bhslr; + struct iscsi_bhs_logout_response *bhslr2; + struct icl_pdu *response; + struct cfiscsi_session *cs; + + cs = PDU_SESSION(request); + bhslr = (struct iscsi_bhs_logout_request *)request->ip_bhs; + switch (bhslr->bhslr_reason & 0x7f) { + case BHSLR_REASON_CLOSE_SESSION: + case BHSLR_REASON_CLOSE_CONNECTION: + response = cfiscsi_pdu_new_response(request, M_NOWAIT); + if (response == NULL) { + icl_pdu_free(request); + cfiscsi_session_terminate(cs); + return; + } + bhslr2 = (struct iscsi_bhs_logout_response *)response->ip_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; + icl_pdu_free(request); + cfiscsi_pdu_queue(response); + cfiscsi_session_terminate(cs); + break; + case BHSLR_REASON_REMOVE_FOR_RECOVERY: + response = cfiscsi_pdu_new_response(request, M_NOWAIT); + if (response == NULL) { + icl_pdu_free(request); + cfiscsi_session_terminate(cs); + return; + } + bhslr2 = (struct iscsi_bhs_logout_response *)response->ip_bhs; + bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_RESPONSE; + bhslr2->bhslr_flags = 0x80; + bhslr2->bhslr_response = BHSLR_RESPONSE_RECOVERY_NOT_SUPPORTED; + bhslr2->bhslr_initiator_task_tag = + bhslr->bhslr_initiator_task_tag; + icl_pdu_free(request); + cfiscsi_pdu_queue(response); + break; + default: + CFISCSI_SESSION_WARN(cs, "invalid reason 0%x; dropping connection", + bhslr->bhslr_reason); + icl_pdu_free(request); + cfiscsi_session_terminate(cs); + break; + } +} + +static void +cfiscsi_callout(void *context) +{ + struct icl_pdu *cp; + struct iscsi_bhs_nop_in *bhsni; + struct cfiscsi_session *cs; + + cs = context; + + if (cs->cs_terminating) + return; + + callout_schedule(&cs->cs_callout, 1 * hz); + + CFISCSI_SESSION_LOCK(cs); + cs->cs_timeout++; + CFISCSI_SESSION_UNLOCK(cs); + +#ifdef ICL_KERNEL_PROXY + if (cs->cs_waiting_for_ctld || cs->cs_login_phase) { + if (cs->cs_timeout > login_timeout) { + CFISCSI_SESSION_WARN(cs, "login timed out after " + "%d seconds; dropping connection", cs->cs_timeout); + cfiscsi_session_terminate(cs); + } + return; + } +#endif + + if (cs->cs_timeout >= ping_timeout) { + CFISCSI_SESSION_WARN(cs, "no ping reply (NOP-Out) after %d seconds; " + "dropping connection", ping_timeout); + cfiscsi_session_terminate(cs); + return; + } + + /* + * If the ping was reset less than one second ago - which means + * that we've received some PDU during the last second - assume + * the traffic flows correctly and don't bother sending a NOP-Out. + * + * (It's 2 - one for one second, and one for incrementing is_timeout + * earlier in this routine.) + */ + if (cs->cs_timeout < 2) + return; + + cp = icl_pdu_new_bhs(cs->cs_conn, M_WAITOK); + bhsni = (struct iscsi_bhs_nop_in *)cp->ip_bhs; + bhsni->bhsni_opcode = ISCSI_BHS_OPCODE_NOP_IN; + bhsni->bhsni_flags = 0x80; + bhsni->bhsni_initiator_task_tag = 0xffffffff; + + cfiscsi_pdu_queue(cp); +} + +static void +cfiscsi_session_terminate_tasks(struct cfiscsi_session *cs) +{ + struct cfiscsi_data_wait *cdw, *tmpcdw; + union ctl_io *io; + int error; + +#ifdef notyet + io = ctl_alloc_io(cs->cs_target->ct_softc->fe.ctl_pool_ref); + if (io == NULL) { + CFISCSI_SESSION_WARN(cs, "can't allocate ctl_io"); + return; + } + ctl_zero_io(io); + io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = NULL; + io->io_hdr.io_type = CTL_IO_TASK; + io->io_hdr.nexus.initid.id = cs->cs_ctl_initid; + io->io_hdr.nexus.targ_port = cs->cs_target->ct_softc->fe.targ_port; + io->io_hdr.nexus.targ_target.id = 0; + io->io_hdr.nexus.targ_lun = lun; + io->taskio.tag_type = CTL_TAG_SIMPLE; /* XXX */ + io->taskio.task_action = CTL_TASK_ABORT_TASK_SET; + error = ctl_queue(io); + if (error != CTL_RETVAL_COMPLETE) { + CFISCSI_SESSION_WARN(cs, "ctl_queue() failed; error %d", error); + ctl_free_io(io); + } +#else + /* + * CTL doesn't currently support CTL_TASK_ABORT_TASK_SET, so instead + * just iterate over tasks that are waiting for something - data - and + * terminate those. + */ + CFISCSI_SESSION_LOCK(cs); + TAILQ_FOREACH_SAFE(cdw, + &cs->cs_waiting_for_data_out, cdw_next, tmpcdw) { + io = ctl_alloc_io(cs->cs_target->ct_softc->fe.ctl_pool_ref); + if (io == NULL) { + CFISCSI_SESSION_WARN(cs, "can't allocate ctl_io"); + return; + } + ctl_zero_io(io); + io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = NULL; + io->io_hdr.io_type = CTL_IO_TASK; + io->io_hdr.nexus.initid.id = cs->cs_ctl_initid; + io->io_hdr.nexus.targ_port = + cs->cs_target->ct_softc->fe.targ_port; + io->io_hdr.nexus.targ_target.id = 0; + //io->io_hdr.nexus.targ_lun = lun; /* Not needed? */ + io->taskio.tag_type = CTL_TAG_SIMPLE; /* XXX */ + io->taskio.task_action = CTL_TASK_ABORT_TASK; + io->taskio.tag_num = cdw->cdw_initiator_task_tag; + error = ctl_queue(io); + if (error != CTL_RETVAL_COMPLETE) { + CFISCSI_SESSION_WARN(cs, "ctl_queue() failed; error %d", error); + ctl_free_io(io); + return; + } +#if 0 + CFISCSI_SESSION_DEBUG(cs, "removing csw for initiator task tag " + "0x%x", cdw->cdw_initiator_task_tag); +#endif + cdw->cdw_ctl_io->scsiio.be_move_done(cdw->cdw_ctl_io); + TAILQ_REMOVE(&cs->cs_waiting_for_data_out, cdw, cdw_next); + uma_zfree(cfiscsi_data_wait_zone, cdw); + } + CFISCSI_SESSION_UNLOCK(cs); +#endif +} + +static void +cfiscsi_maintenance_thread(void *arg) +{ + struct cfiscsi_session *cs; + + cs = arg; + + for (;;) { + CFISCSI_SESSION_LOCK(cs); + if (cs->cs_terminating == false) + cv_wait(&cs->cs_maintenance_cv, &cs->cs_lock); + CFISCSI_SESSION_UNLOCK(cs); + + if (cs->cs_terminating) { + cfiscsi_session_terminate_tasks(cs); + callout_drain(&cs->cs_callout); + + icl_conn_shutdown(cs->cs_conn); + icl_conn_close(cs->cs_conn); + + cs->cs_terminating++; + + /* + * XXX: We used to wait up to 30 seconds to deliver queued PDUs + * to the initiator. We also tried hard to deliver SCSI Responses + * for the aborted PDUs. We don't do that anymore. We might need + * to revisit that. + */ + + cfiscsi_session_delete(cs); + kthread_exit(); + return; + } + CFISCSI_SESSION_DEBUG(cs, "nothing to do"); + } +} + +static void +cfiscsi_session_terminate(struct cfiscsi_session *cs) +{ + + if (cs->cs_terminating != 0) + return; + cs->cs_terminating = 1; + cv_signal(&cs->cs_maintenance_cv); +} + +static int +cfiscsi_session_register_initiator(struct cfiscsi_session *cs) +{ + int error, i; + struct cfiscsi_softc *softc; + + KASSERT(cs->cs_ctl_initid == -1, ("already registered")); + + softc = &cfiscsi_softc; + + mtx_lock(&softc->lock); + for (i = 0; i < softc->max_initiators; i++) { + if (softc->ctl_initids[i] == 0) + break; + } + if (i == softc->max_initiators) { + CFISCSI_SESSION_WARN(cs, "too many concurrent sessions (%d)", + softc->max_initiators); + mtx_unlock(&softc->lock); + return (1); + } + softc->ctl_initids[i] = 1; + mtx_unlock(&softc->lock); + +#if 0 + CFISCSI_SESSION_DEBUG(cs, "adding initiator id %d, max %d", + i, softc->max_initiators); +#endif + cs->cs_ctl_initid = i; + error = ctl_add_initiator(0x0, softc->fe.targ_port, cs->cs_ctl_initid); + if (error != 0) { + CFISCSI_SESSION_WARN(cs, "ctl_add_initiator failed with error %d", error); + mtx_lock(&softc->lock); + softc->ctl_initids[cs->cs_ctl_initid] = 0; + mtx_unlock(&softc->lock); + cs->cs_ctl_initid = -1; + return (1); + } + + return (0); +} + +static void +cfiscsi_session_unregister_initiator(struct cfiscsi_session *cs) +{ + int error; + struct cfiscsi_softc *softc; + + if (cs->cs_ctl_initid == -1) + return; + + softc = &cfiscsi_softc; + + error = ctl_remove_initiator(softc->fe.targ_port, cs->cs_ctl_initid); + if (error != 0) { + CFISCSI_SESSION_WARN(cs, "ctl_remove_initiator failed with error %d", + error); + } + mtx_lock(&softc->lock); + softc->ctl_initids[cs->cs_ctl_initid] = 0; + mtx_unlock(&softc->lock); + cs->cs_ctl_initid = -1; +} + +static struct cfiscsi_session * +cfiscsi_session_new(struct cfiscsi_softc *softc) +{ + struct cfiscsi_session *cs; + int error; + + cs = malloc(sizeof(*cs), M_CFISCSI, M_NOWAIT | M_ZERO); + if (cs == NULL) { + CFISCSI_WARN("malloc failed"); + return (NULL); + } + cs->cs_ctl_initid = -1; + + refcount_init(&cs->cs_outstanding_ctl_pdus, 0); + TAILQ_INIT(&cs->cs_waiting_for_data_out); + mtx_init(&cs->cs_lock, "cfiscsi_lock", NULL, MTX_DEF); + cv_init(&cs->cs_maintenance_cv, "cfiscsi_mt"); +#ifdef ICL_KERNEL_PROXY + cv_init(&cs->cs_login_cv, "cfiscsi_login"); +#endif + + cs->cs_conn = icl_conn_new(); + cs->cs_conn->ic_receive = cfiscsi_receive_callback; + cs->cs_conn->ic_error = cfiscsi_error_callback; + cs->cs_conn->ic_prv0 = cs; + + error = kthread_add(cfiscsi_maintenance_thread, cs, NULL, NULL, 0, 0, "cfiscsimt"); + if (error != 0) { + CFISCSI_SESSION_WARN(cs, "kthread_add(9) failed with error %d", error); + free(cs, M_CFISCSI); + return (NULL); + } + + mtx_lock(&softc->lock); + cs->cs_id = softc->last_session_id + 1; + softc->last_session_id++; + mtx_unlock(&softc->lock); + + mtx_lock(&softc->lock); + TAILQ_INSERT_TAIL(&softc->sessions, cs, cs_next); + mtx_unlock(&softc->lock); + + /* + * Start pinging the initiator. + */ + callout_init(&cs->cs_callout, 1); + callout_reset(&cs->cs_callout, 1 * hz, cfiscsi_callout, cs); + + return (cs); +} + +static void +cfiscsi_session_delete(struct cfiscsi_session *cs) +{ + struct cfiscsi_softc *softc; + + softc = &cfiscsi_softc; + + KASSERT(cs->cs_outstanding_ctl_pdus == 0, + ("destroying session with outstanding CTL pdus")); + KASSERT(TAILQ_EMPTY(&cs->cs_waiting_for_data_out), + ("destroying session with non-empty queue")); + + cfiscsi_session_unregister_initiator(cs); + if (cs->cs_target != NULL) + cfiscsi_target_release(cs->cs_target); + icl_conn_close(cs->cs_conn); + icl_conn_free(cs->cs_conn); + + mtx_lock(&softc->lock); + TAILQ_REMOVE(&softc->sessions, cs, cs_next); + mtx_unlock(&softc->lock); + + free(cs, M_CFISCSI); +} + +int +cfiscsi_init(void) +{ + struct cfiscsi_softc *softc; + struct ctl_frontend *fe; + int retval; + + softc = &cfiscsi_softc; + retval = 0; + bzero(softc, sizeof(*softc)); + mtx_init(&softc->lock, "cfiscsi", NULL, MTX_DEF); + +#ifdef ICL_KERNEL_PROXY + cv_init(&softc->accept_cv, "cfiscsi_accept"); +#endif + TAILQ_INIT(&softc->sessions); + TAILQ_INIT(&softc->targets); + + fe = &softc->fe; + fe->port_type = CTL_PORT_ISCSI; + /* XXX KDM what should the real number be here? */ + fe->num_requested_ctl_io = 4096; + snprintf(softc->port_name, sizeof(softc->port_name), "iscsi"); + fe->port_name = softc->port_name; + fe->port_online = cfiscsi_online; + fe->port_offline = cfiscsi_offline; + fe->onoff_arg = softc; + fe->targ_enable = cfiscsi_targ_enable; + fe->targ_disable = cfiscsi_targ_disable; + fe->lun_enable = cfiscsi_lun_enable; + fe->lun_disable = cfiscsi_lun_disable; + fe->targ_lun_arg = softc; + fe->ioctl = cfiscsi_ioctl; + fe->devid = cfiscsi_devid; + fe->fe_datamove = cfiscsi_datamove; + fe->fe_done = cfiscsi_done; + + /* XXX KDM what should we report here? */ + /* XXX These should probably be fetched from CTL. */ + fe->max_targets = 1; + fe->max_target_id = 15; + + retval = ctl_frontend_register(fe, /*master_SC*/ 1); + if (retval != 0) { + CFISCSI_WARN("ctl_frontend_register() failed with error %d", + retval); + retval = 1; + goto bailout; + } + + softc->max_initiators = fe->max_initiators; + + cfiscsi_data_wait_zone = uma_zcreate("cfiscsi_data_wait", + sizeof(struct cfiscsi_data_wait), NULL, NULL, NULL, NULL, + UMA_ALIGN_PTR, UMA_ZONE_NOFREE); + + return (0); + +bailout: + return (retval); +} + +static int +cfiscsi_module_event_handler(module_t mod, int what, void *arg) +{ + + switch (what) { + case MOD_LOAD: + return (cfiscsi_init()); + case MOD_UNLOAD: + return (EBUSY); + default: + return (EOPNOTSUPP); + } +} + +#ifdef ICL_KERNEL_PROXY +static void +cfiscsi_accept(struct socket *so) +{ + struct cfiscsi_session *cs; + + cs = cfiscsi_session_new(&cfiscsi_softc); + if (cs == NULL) { + CFISCSI_WARN("failed to create session"); + return; + } + + icl_conn_handoff_sock(cs->cs_conn, so); + cs->cs_waiting_for_ctld = true; + cv_signal(&cfiscsi_softc.accept_cv); +} +#endif + +static void +cfiscsi_online(void *arg) +{ + struct cfiscsi_softc *softc; + + softc = (struct cfiscsi_softc *)arg; + + softc->online = 1; +#ifdef ICL_KERNEL_PROXY + if (softc->listener != NULL) + icl_listen_free(softc->listener); + softc->listener = icl_listen_new(cfiscsi_accept); +#endif +} + +static void +cfiscsi_offline(void *arg) +{ + struct cfiscsi_softc *softc; + struct cfiscsi_session *cs; + + softc = (struct cfiscsi_softc *)arg; + + softc->online = 0; + + mtx_lock(&softc->lock); + TAILQ_FOREACH(cs, &softc->sessions, cs_next) + cfiscsi_session_terminate(cs); + mtx_unlock(&softc->lock); + +#ifdef ICL_KERNEL_PROXY + icl_listen_free(softc->listener); + softc->listener = NULL; +#endif +} + +static int +cfiscsi_targ_enable(void *arg, struct ctl_id targ_id) +{ + + return (0); +} + +static int +cfiscsi_targ_disable(void *arg, struct ctl_id targ_id) +{ + + return (0); +} + +static void +cfiscsi_ioctl_handoff(struct ctl_iscsi *ci) +{ + struct cfiscsi_softc *softc; + struct cfiscsi_session *cs; + struct cfiscsi_target *ct; + struct ctl_iscsi_handoff_params *cihp; +#ifndef ICL_KERNEL_PROXY + int error; +#endif + + cihp = (struct ctl_iscsi_handoff_params *)&(ci->data); + softc = &cfiscsi_softc; + + CFISCSI_DEBUG("new connection from %s (%s) to %s", + cihp->initiator_name, cihp->initiator_addr, + cihp->target_name); + + if (softc->online == 0) { + ci->status = CTL_ISCSI_ERROR; + snprintf(ci->error_str, sizeof(ci->error_str), + "%s: port offline", __func__); + return; + } + + ct = cfiscsi_target_find(softc, cihp->target_name); + if (ct == NULL) { + ci->status = CTL_ISCSI_ERROR; + snprintf(ci->error_str, sizeof(ci->error_str), + "%s: target not found", __func__); + return; + } + +#ifdef ICL_KERNEL_PROXY + mtx_lock(&cfiscsi_softc.lock); + TAILQ_FOREACH(cs, &cfiscsi_softc.sessions, cs_next) { + if (cs->cs_id == cihp->socket) + break; + } + if (cs == NULL) { + mtx_unlock(&cfiscsi_softc.lock); + snprintf(ci->error_str, sizeof(ci->error_str), "connection not found"); + ci->status = CTL_ISCSI_ERROR; + return; + } + mtx_unlock(&cfiscsi_softc.lock); +#else + cs = cfiscsi_session_new(softc); + if (cs == NULL) { + ci->status = CTL_ISCSI_ERROR; + snprintf(ci->error_str, sizeof(ci->error_str), + "%s: cfiscsi_session_new failed", __func__); + cfiscsi_target_release(ct); + return; + } +#endif + cs->cs_target = ct; + + /* + * First PDU of Full Feature phase has the same CmdSN as the last + * PDU from the Login Phase received from the initiator. Thus, + * the -1 below. + */ + cs->cs_portal_group_tag = cihp->portal_group_tag; + cs->cs_cmdsn = cihp->cmdsn; + cs->cs_statsn = cihp->statsn; + cs->cs_max_data_segment_length = cihp->max_recv_data_segment_length; + cs->cs_max_burst_length = cihp->max_burst_length; + cs->cs_immediate_data = !!cihp->immediate_data; + if (cihp->header_digest == CTL_ISCSI_DIGEST_CRC32C) + cs->cs_conn->ic_header_crc32c = true; + if (cihp->data_digest == CTL_ISCSI_DIGEST_CRC32C) + cs->cs_conn->ic_data_crc32c = true; + + strlcpy(cs->cs_initiator_name, + cihp->initiator_name, sizeof(cs->cs_initiator_name)); + strlcpy(cs->cs_initiator_addr, + cihp->initiator_addr, sizeof(cs->cs_initiator_addr)); + strlcpy(cs->cs_initiator_alias, + cihp->initiator_alias, sizeof(cs->cs_initiator_alias)); + +#ifdef ICL_KERNEL_PROXY + cs->cs_login_phase = false; +#else + error = icl_conn_handoff(cs->cs_conn, cihp->socket); + if (error != 0) { + cfiscsi_session_delete(cs); + ci->status = CTL_ISCSI_ERROR; + snprintf(ci->error_str, sizeof(ci->error_str), + "%s: icl_conn_handoff failed with error %d", + __func__, error); + return; + } +#endif + + /* + * Register initiator with CTL. + */ + cfiscsi_session_register_initiator(cs); + +#ifdef ICL_KERNEL_PROXY + /* + * First PDU of the Full Feature phase has likely already arrived. + * We have to pick it up and execute properly. + */ + if (cs->cs_login_pdu != NULL) { + CFISCSI_SESSION_DEBUG(cs, "picking up first PDU"); + cfiscsi_pdu_handle(cs->cs_login_pdu); + cs->cs_login_pdu = NULL; + } +#endif + + ci->status = CTL_ISCSI_OK; +} + +static void +cfiscsi_ioctl_list(struct ctl_iscsi *ci) +{ + struct ctl_iscsi_list_params *cilp; + struct cfiscsi_session *cs; + struct cfiscsi_softc *softc; + struct sbuf *sb; + int error; + + cilp = (struct ctl_iscsi_list_params *)&(ci->data); + softc = &cfiscsi_softc; + + sb = sbuf_new(NULL, NULL, cilp->alloc_len, SBUF_FIXEDLEN); + if (sb == NULL) { + ci->status = CTL_ISCSI_ERROR; + snprintf(ci->error_str, sizeof(ci->error_str), + "Unable to allocate %d bytes for iSCSI session list", + cilp->alloc_len); + return; + } + + sbuf_printf(sb, "\n"); + mtx_lock(&softc->lock); + TAILQ_FOREACH(cs, &softc->sessions, cs_next) { +#ifdef ICL_KERNEL_PROXY + if (cs->cs_target == NULL) + continue; +#endif + error = sbuf_printf(sb, "" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s" + "%zd" + "%zd" + "%d" + "\n", + cs->cs_id, + cs->cs_initiator_name, cs->cs_initiator_addr, cs->cs_initiator_alias, + cs->cs_target->ct_name, cs->cs_target->ct_alias, + cs->cs_conn->ic_header_crc32c ? "CRC32C" : "None", + cs->cs_conn->ic_data_crc32c ? "CRC32C" : "None", + cs->cs_max_data_segment_length, + cs->cs_immediate_data, + cs->cs_conn->ic_iser); + if (error != 0) + break; + } + mtx_unlock(&softc->lock); + error = sbuf_printf(sb, "\n"); + if (error != 0) { + sbuf_delete(sb); + ci->status = CTL_ISCSI_LIST_NEED_MORE_SPACE; + snprintf(ci->error_str, sizeof(ci->error_str), + "Out of space, %d bytes is too small", cilp->alloc_len); + return; + } + sbuf_finish(sb); + + error = copyout(sbuf_data(sb), cilp->conn_xml, sbuf_len(sb) + 1); + cilp->fill_len = sbuf_len(sb) + 1; + ci->status = CTL_ISCSI_OK; + sbuf_delete(sb); +} + +static void +cfiscsi_ioctl_terminate(struct ctl_iscsi *ci) +{ + struct icl_pdu *response; + struct iscsi_bhs_asynchronous_message *bhsam; + struct ctl_iscsi_terminate_params *citp; + struct cfiscsi_session *cs; + struct cfiscsi_softc *softc; + int found = 0; + + citp = (struct ctl_iscsi_terminate_params *)&(ci->data); + softc = &cfiscsi_softc; + + mtx_lock(&softc->lock); + TAILQ_FOREACH(cs, &softc->sessions, cs_next) { + if (citp->all == 0 && cs->cs_id != citp->connection_id && + strcmp(cs->cs_initiator_name, citp->initiator_name) != 0 && + strcmp(cs->cs_initiator_addr, citp->initiator_addr) != 0) + continue; + + response = icl_pdu_new_bhs(cs->cs_conn, M_NOWAIT); + if (response == NULL) { + /* + * Oh well. Just terminate the connection. + */ + } else { + bhsam = (struct iscsi_bhs_asynchronous_message *) + response->ip_bhs; + bhsam->bhsam_opcode = ISCSI_BHS_OPCODE_ASYNC_MESSAGE; + bhsam->bhsam_flags = 0x80; + bhsam->bhsam_0xffffffff = 0xffffffff; + bhsam->bhsam_async_event = + BHSAM_EVENT_TARGET_TERMINATES_SESSION; + cfiscsi_pdu_queue(response); + } + cfiscsi_session_terminate(cs); + found++; + } + mtx_unlock(&softc->lock); + + if (found == 0) { + ci->status = CTL_ISCSI_SESSION_NOT_FOUND; + snprintf(ci->error_str, sizeof(ci->error_str), + "No matching connections found"); + return; + } + + ci->status = CTL_ISCSI_OK; +} + +static void +cfiscsi_ioctl_logout(struct ctl_iscsi *ci) +{ + struct icl_pdu *response; + struct iscsi_bhs_asynchronous_message *bhsam; + struct ctl_iscsi_logout_params *cilp; + struct cfiscsi_session *cs; + struct cfiscsi_softc *softc; + int found = 0; + + cilp = (struct ctl_iscsi_logout_params *)&(ci->data); + softc = &cfiscsi_softc; + + mtx_lock(&softc->lock); + TAILQ_FOREACH(cs, &softc->sessions, cs_next) { + if (cilp->all == 0 && cs->cs_id != cilp->connection_id && + strcmp(cs->cs_initiator_name, cilp->initiator_name) != 0 && + strcmp(cs->cs_initiator_addr, cilp->initiator_addr) != 0) + continue; + + response = icl_pdu_new_bhs(cs->cs_conn, M_NOWAIT); + if (response == NULL) { + ci->status = CTL_ISCSI_ERROR; + snprintf(ci->error_str, sizeof(ci->error_str), + "Unable to allocate memory"); + mtx_unlock(&softc->lock); + return; + } + bhsam = + (struct iscsi_bhs_asynchronous_message *)response->ip_bhs; + bhsam->bhsam_opcode = ISCSI_BHS_OPCODE_ASYNC_MESSAGE; + bhsam->bhsam_flags = 0x80; + bhsam->bhsam_async_event = BHSAM_EVENT_TARGET_REQUESTS_LOGOUT; + bhsam->bhsam_parameter3 = htons(10); + cfiscsi_pdu_queue(response); + found++; + } + mtx_unlock(&softc->lock); + + if (found == 0) { + ci->status = CTL_ISCSI_SESSION_NOT_FOUND; + snprintf(ci->error_str, sizeof(ci->error_str), + "No matching connections found"); + return; + } + + ci->status = CTL_ISCSI_OK; +} + +#ifdef ICL_KERNEL_PROXY +static void +cfiscsi_ioctl_listen(struct ctl_iscsi *ci) +{ + struct ctl_iscsi_listen_params *cilp; + struct sockaddr *sa; + int error; + + cilp = (struct ctl_iscsi_listen_params *)&(ci->data); + + if (cfiscsi_softc.listener == NULL) { + CFISCSI_DEBUG("no listener"); + snprintf(ci->error_str, sizeof(ci->error_str), "no listener"); + ci->status = CTL_ISCSI_ERROR; + return; + } + + error = getsockaddr(&sa, (void *)cilp->addr, cilp->addrlen); + if (error != 0) { + CFISCSI_DEBUG("getsockaddr, error %d", error); + snprintf(ci->error_str, sizeof(ci->error_str), "getsockaddr failed"); + ci->status = CTL_ISCSI_ERROR; + return; + } + + error = icl_listen_add(cfiscsi_softc.listener, cilp->iser, cilp->domain, + cilp->socktype, cilp->protocol, sa); + if (error != 0) { + free(sa, M_SONAME); + CFISCSI_DEBUG("icl_listen_add, error %d", error); + snprintf(ci->error_str, sizeof(ci->error_str), + "icl_listen_add failed, error %d", error); + ci->status = CTL_ISCSI_ERROR; + return; + } + + ci->status = CTL_ISCSI_OK; +} + +static void +cfiscsi_ioctl_accept(struct ctl_iscsi *ci) +{ + struct ctl_iscsi_accept_params *ciap; + struct cfiscsi_session *cs; + int error; + + ciap = (struct ctl_iscsi_accept_params *)&(ci->data); + + mtx_lock(&cfiscsi_softc.lock); + for (;;) { + TAILQ_FOREACH(cs, &cfiscsi_softc.sessions, cs_next) { + if (cs->cs_waiting_for_ctld) + break; + } + if (cs != NULL) + break; + error = cv_wait_sig(&cfiscsi_softc.accept_cv, &cfiscsi_softc.lock); + if (error != 0) { + mtx_unlock(&cfiscsi_softc.lock); + snprintf(ci->error_str, sizeof(ci->error_str), "interrupted"); + ci->status = CTL_ISCSI_ERROR; + return; + } + } + mtx_unlock(&cfiscsi_softc.lock); + + cs->cs_waiting_for_ctld = false; + cs->cs_login_phase = true; + + ciap->connection_id = cs->cs_id; + ci->status = CTL_ISCSI_OK; +} + +static void +cfiscsi_ioctl_send(struct ctl_iscsi *ci) +{ + struct ctl_iscsi_send_params *cisp; + struct cfiscsi_session *cs; + struct icl_pdu *ip; + size_t datalen; + void *data; + int error; + + cisp = (struct ctl_iscsi_send_params *)&(ci->data); + + mtx_lock(&cfiscsi_softc.lock); + TAILQ_FOREACH(cs, &cfiscsi_softc.sessions, cs_next) { + if (cs->cs_id == cisp->connection_id) + break; + } + if (cs == NULL) { + mtx_unlock(&cfiscsi_softc.lock); + snprintf(ci->error_str, sizeof(ci->error_str), "connection not found"); + ci->status = CTL_ISCSI_ERROR; + return; + } + mtx_unlock(&cfiscsi_softc.lock); + +#if 0 + if (cs->cs_login_phase == false) + return (EBUSY); +#endif + + if (cs->cs_terminating) { + snprintf(ci->error_str, sizeof(ci->error_str), "connection is terminating"); + ci->status = CTL_ISCSI_ERROR; + return; + } + + datalen = cisp->data_segment_len; + /* + * XXX + */ + //if (datalen > CFISCSI_MAX_DATA_SEGMENT_LENGTH) { + if (datalen > 65535) { + snprintf(ci->error_str, sizeof(ci->error_str), "data segment too big"); + ci->status = CTL_ISCSI_ERROR; + return; + } + if (datalen > 0) { + data = malloc(datalen, M_CFISCSI, M_WAITOK); + error = copyin(cisp->data_segment, data, datalen); + if (error != 0) { + free(data, M_CFISCSI); + snprintf(ci->error_str, sizeof(ci->error_str), "copyin error %d", error); + ci->status = CTL_ISCSI_ERROR; + return; + } + } + + ip = icl_pdu_new_bhs(cs->cs_conn, M_WAITOK); + memcpy(ip->ip_bhs, cisp->bhs, sizeof(*ip->ip_bhs)); + if (datalen > 0) { + icl_pdu_append_data(ip, data, datalen); + free(data, M_CFISCSI); + } + icl_pdu_queue(ip); + ci->status = CTL_ISCSI_OK; +} + +static void +cfiscsi_ioctl_receive(struct ctl_iscsi *ci) +{ + struct ctl_iscsi_receive_params *cirp; + struct cfiscsi_session *cs; + struct icl_pdu *ip; + void *data; + + cirp = (struct ctl_iscsi_receive_params *)&(ci->data); + + mtx_lock(&cfiscsi_softc.lock); + TAILQ_FOREACH(cs, &cfiscsi_softc.sessions, cs_next) { + if (cs->cs_id == cirp->connection_id) + break; + } + if (cs == NULL) { + mtx_unlock(&cfiscsi_softc.lock); + snprintf(ci->error_str, sizeof(ci->error_str), "connection not found"); + ci->status = CTL_ISCSI_ERROR; + return; + } + mtx_unlock(&cfiscsi_softc.lock); + +#if 0 + if (is->is_login_phase == false) + return (EBUSY); +#endif + + CFISCSI_SESSION_LOCK(cs); + while (cs->cs_login_pdu == NULL && + cs->cs_terminating == false) + cv_wait(&cs->cs_login_cv, &cs->cs_lock); + if (cs->cs_terminating) { + CFISCSI_SESSION_UNLOCK(cs); + snprintf(ci->error_str, sizeof(ci->error_str), "connection terminating"); + ci->status = CTL_ISCSI_ERROR; + return; + } + ip = cs->cs_login_pdu; + cs->cs_login_pdu = NULL; + CFISCSI_SESSION_UNLOCK(cs); + + if (ip->ip_data_len > cirp->data_segment_len) { + icl_pdu_free(ip); + snprintf(ci->error_str, sizeof(ci->error_str), "data segment too big"); + ci->status = CTL_ISCSI_ERROR; + return; + } + + copyout(ip->ip_bhs, cirp->bhs, sizeof(*ip->ip_bhs)); + if (ip->ip_data_len > 0) { + data = malloc(ip->ip_data_len, M_CFISCSI, M_WAITOK); + icl_pdu_get_data(ip, 0, data, ip->ip_data_len); + copyout(data, cirp->data_segment, ip->ip_data_len); + free(data, M_CFISCSI); + } + + icl_pdu_free(ip); + ci->status = CTL_ISCSI_OK; +} + +static void +cfiscsi_ioctl_close(struct ctl_iscsi *ci) +{ + /* + * XXX + */ +} +#endif /* !ICL_KERNEL_PROXY */ + +static int +cfiscsi_ioctl(struct cdev *dev, + u_long cmd, caddr_t addr, int flag, struct thread *td) +{ + struct ctl_iscsi *ci; + + if (cmd != CTL_ISCSI) + return (ENOTTY); + + ci = (struct ctl_iscsi *)addr; + switch (ci->type) { + case CTL_ISCSI_HANDOFF: + cfiscsi_ioctl_handoff(ci); + break; + case CTL_ISCSI_LIST: + cfiscsi_ioctl_list(ci); + break; + case CTL_ISCSI_TERMINATE: + cfiscsi_ioctl_terminate(ci); + break; + case CTL_ISCSI_LOGOUT: + cfiscsi_ioctl_logout(ci); + break; +#ifdef ICL_KERNEL_PROXY + case CTL_ISCSI_LISTEN: + cfiscsi_ioctl_listen(ci); + break; + case CTL_ISCSI_ACCEPT: + cfiscsi_ioctl_accept(ci); + break; + case CTL_ISCSI_SEND: + cfiscsi_ioctl_send(ci); + break; + case CTL_ISCSI_RECEIVE: + cfiscsi_ioctl_receive(ci); + break; + case CTL_ISCSI_CLOSE: + cfiscsi_ioctl_close(ci); + break; +#endif /* ICL_KERNEL_PROXY */ + default: + ci->status = CTL_ISCSI_ERROR; + snprintf(ci->error_str, sizeof(ci->error_str), + "%s: invalid iSCSI request type %d", __func__, ci->type); + break; + } + + return (0); +} + +static int +cfiscsi_devid(struct ctl_scsiio *ctsio, int alloc_len) +{ + struct cfiscsi_session *cs; + struct scsi_vpd_device_id *devid_ptr; + struct scsi_vpd_id_descriptor *desc, *desc1; + struct scsi_vpd_id_descriptor *desc2, *desc3; /* for types 4h and 5h */ + struct scsi_vpd_id_t10 *t10id; + struct ctl_lun *lun; + const struct icl_pdu *request; + size_t devid_len, wwpn_len; + + lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr; + request = ctsio->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr; + cs = PDU_SESSION(request); + + wwpn_len = strlen(cs->cs_target->ct_name); + wwpn_len += strlen(",t,0x01"); + wwpn_len += 1; /* '\0' */ + if ((wwpn_len % 4) != 0) + wwpn_len += (4 - (wwpn_len % 4)); + + devid_len = sizeof(struct scsi_vpd_device_id) + + sizeof(struct scsi_vpd_id_descriptor) + + sizeof(struct scsi_vpd_id_t10) + CTL_DEVID_LEN + + sizeof(struct scsi_vpd_id_descriptor) + wwpn_len + + sizeof(struct scsi_vpd_id_descriptor) + + sizeof(struct scsi_vpd_id_rel_trgt_port_id) + + sizeof(struct scsi_vpd_id_descriptor) + + sizeof(struct scsi_vpd_id_trgt_port_grp_id); + + ctsio->kern_data_ptr = malloc(devid_len, M_CTL, M_WAITOK | M_ZERO); + devid_ptr = (struct scsi_vpd_device_id *)ctsio->kern_data_ptr; + ctsio->kern_sg_entries = 0; + + if (devid_len < alloc_len) { + ctsio->residual = alloc_len - devid_len; + ctsio->kern_data_len = devid_len; + ctsio->kern_total_len = devid_len; + } else { + ctsio->residual = 0; + ctsio->kern_data_len = alloc_len; + ctsio->kern_total_len = alloc_len; + } + ctsio->kern_data_resid = 0; + ctsio->kern_rel_offset = 0; + ctsio->kern_sg_entries = 0; + + desc = (struct scsi_vpd_id_descriptor *)devid_ptr->desc_list; + t10id = (struct scsi_vpd_id_t10 *)&desc->identifier[0]; + desc1 = (struct scsi_vpd_id_descriptor *)(&desc->identifier[0] + + sizeof(struct scsi_vpd_id_t10) + CTL_DEVID_LEN); + desc2 = (struct scsi_vpd_id_descriptor *)(&desc1->identifier[0] + + wwpn_len); + desc3 = (struct scsi_vpd_id_descriptor *)(&desc2->identifier[0] + + sizeof(struct scsi_vpd_id_rel_trgt_port_id)); + + if (lun != NULL) + devid_ptr->device = (SID_QUAL_LU_CONNECTED << 5) | + lun->be_lun->lun_type; + else + devid_ptr->device = (SID_QUAL_LU_OFFLINE << 5) | T_DIRECT; + + devid_ptr->page_code = SVPD_DEVICE_ID; + + scsi_ulto2b(devid_len - 4, devid_ptr->length); + + /* + * We're using a LUN association here. i.e., this device ID is a + * per-LUN identifier. + */ + desc->proto_codeset = (SCSI_PROTO_ISCSI << 4) | SVPD_ID_CODESET_ASCII; + desc->id_type = SVPD_ID_PIV | SVPD_ID_ASSOC_LUN | SVPD_ID_TYPE_T10; + desc->length = sizeof(*t10id) + CTL_DEVID_LEN; + strncpy((char *)t10id->vendor, CTL_VENDOR, sizeof(t10id->vendor)); + + /* + * If we've actually got a backend, copy the device id from the + * per-LUN data. Otherwise, set it to all spaces. + */ + if (lun != NULL) { + /* + * Copy the backend's LUN ID. + */ + strncpy((char *)t10id->vendor_spec_id, + (char *)lun->be_lun->device_id, CTL_DEVID_LEN); + } else { + /* + * No backend, set this to spaces. + */ + memset(t10id->vendor_spec_id, 0x20, CTL_DEVID_LEN); + } + + /* + * desc1 is for the WWPN which is a port asscociation. + */ + desc1->proto_codeset = (SCSI_PROTO_ISCSI << 4) | SVPD_ID_CODESET_UTF8; + desc1->id_type = SVPD_ID_PIV | SVPD_ID_ASSOC_PORT | + SVPD_ID_TYPE_SCSI_NAME; + desc1->length = wwpn_len; + snprintf(desc1->identifier, wwpn_len, "%s,t,0x%x", + cs->cs_target->ct_name, cs->cs_portal_group_tag); + + /* + * desc2 is for the Relative Target Port(type 4h) identifier + */ + desc2->proto_codeset = (SCSI_PROTO_ISCSI << 4) | SVPD_ID_CODESET_BINARY; + desc2->id_type = SVPD_ID_PIV | SVPD_ID_ASSOC_PORT | + SVPD_ID_TYPE_RELTARG; + desc2->length = 4; + desc2->identifier[3] = 1; + + /* + * desc3 is for the Target Port Group(type 5h) identifier + */ + desc3->proto_codeset = (SCSI_PROTO_ISCSI << 4) | SVPD_ID_CODESET_BINARY; + desc3->id_type = SVPD_ID_PIV | SVPD_ID_ASSOC_PORT | + SVPD_ID_TYPE_TPORTGRP; + desc3->length = 4; + desc3->identifier[3] = 1; + + ctsio->scsi_status = SCSI_STATUS_OK; + + ctsio->be_move_done = ctl_config_move_done; + ctl_datamove((union ctl_io *)ctsio); + + return (CTL_RETVAL_COMPLETE); +} + +static void +cfiscsi_target_hold(struct cfiscsi_target *ct) +{ + + refcount_acquire(&ct->ct_refcount); +} + +static void +cfiscsi_target_release(struct cfiscsi_target *ct) +{ + int old; + struct cfiscsi_softc *softc; + + softc = ct->ct_softc; + + old = ct->ct_refcount; + if (old > 1 && atomic_cmpset_int(&ct->ct_refcount, old, old - 1)) + return; + + mtx_lock(&softc->lock); + if (refcount_release(&ct->ct_refcount)) { + TAILQ_REMOVE(&softc->targets, ct, ct_next); + mtx_unlock(&softc->lock); + free(ct, M_CFISCSI); + + return; + } + mtx_unlock(&softc->lock); +} + +static struct cfiscsi_target * +cfiscsi_target_find(struct cfiscsi_softc *softc, const char *name) +{ + struct cfiscsi_target *ct; + + mtx_lock(&softc->lock); + TAILQ_FOREACH(ct, &softc->targets, ct_next) { + if (strcmp(name, ct->ct_name) != 0) + continue; + cfiscsi_target_hold(ct); + mtx_unlock(&softc->lock); + return (ct); + } + mtx_unlock(&softc->lock); + + return (NULL); +} + +static struct cfiscsi_target * +cfiscsi_target_find_or_create(struct cfiscsi_softc *softc, const char *name, + const char *alias) +{ + struct cfiscsi_target *ct, *newct; + int i; + + if (name[0] == '\0' || strlen(name) >= CTL_ISCSI_NAME_LEN) + return (NULL); + + newct = malloc(sizeof(*newct), M_CFISCSI, M_WAITOK | M_ZERO); + + mtx_lock(&softc->lock); + TAILQ_FOREACH(ct, &softc->targets, ct_next) { + if (strcmp(name, ct->ct_name) != 0) + continue; + cfiscsi_target_hold(ct); + mtx_unlock(&softc->lock); + free(newct, M_CFISCSI); + return (ct); + } + + for (i = 0; i < CTL_MAX_LUNS; i++) + newct->ct_luns[i] = -1; + + strlcpy(newct->ct_name, name, sizeof(newct->ct_name)); + if (alias != NULL) + strlcpy(newct->ct_alias, alias, sizeof(newct->ct_alias)); + refcount_init(&newct->ct_refcount, 1); + newct->ct_softc = softc; + TAILQ_INSERT_TAIL(&softc->targets, newct, ct_next); + mtx_unlock(&softc->lock); + + return (newct); +} + +/* + * Takes LUN from the target space and returns LUN from the CTL space. + */ +static uint32_t +cfiscsi_map_lun(void *arg, uint32_t lun) +{ + struct cfiscsi_session *cs; + + cs = arg; + + if (lun >= CTL_MAX_LUNS) { + CFISCSI_DEBUG("requested lun number %d is higher " + "than maximum %d", lun, CTL_MAX_LUNS - 1); + return (0xffffffff); + } + + if (cs->cs_target->ct_luns[lun] < 0) + return (0xffffffff); + + return (cs->cs_target->ct_luns[lun]); +} + +static int +cfiscsi_target_set_lun(struct cfiscsi_target *ct, + unsigned long lun_id, unsigned long ctl_lun_id) +{ + + if (lun_id >= CTL_MAX_LUNS) { + CFISCSI_WARN("requested lun number %ld is higher " + "than maximum %d", lun_id, CTL_MAX_LUNS - 1); + return (-1); + } + + if (ct->ct_luns[lun_id] >= 0) { + /* + * CTL calls cfiscsi_lun_enable() twice for each LUN - once + * when the LUN is created, and a second time just before + * the port is brought online; don't emit warnings + * for that case. + */ + if (ct->ct_luns[lun_id] == ctl_lun_id) + return (0); + CFISCSI_WARN("lun %ld already allocated", lun_id); + return (-1); + } + +#if 0 + CFISCSI_DEBUG("adding mapping for lun %ld, target %s " + "to ctl lun %ld", lun_id, ct->ct_name, ctl_lun_id); +#endif + + ct->ct_luns[lun_id] = ctl_lun_id; + cfiscsi_target_hold(ct); + + return (0); +} + +static int +cfiscsi_target_unset_lun(struct cfiscsi_target *ct, unsigned long lun_id) +{ + + if (ct->ct_luns[lun_id] < 0) { + CFISCSI_WARN("lun %ld not allocated", lun_id); + return (-1); + } + + ct->ct_luns[lun_id] = -1; + cfiscsi_target_release(ct); + + return (0); +} + +static int +cfiscsi_lun_enable(void *arg, struct ctl_id target_id, int lun_id) +{ + struct cfiscsi_softc *softc; + struct cfiscsi_target *ct; + struct ctl_be_lun_option *opt; + const char *ctld_target = NULL, *ctld_target_alias = NULL; + const char *ctld_lun = NULL; + unsigned long ctld_lun_id; + + softc = (struct cfiscsi_softc *)arg; + + STAILQ_FOREACH(opt, + &control_softc->ctl_luns[lun_id]->be_lun->options, links) { + if (strcmp(opt->name, "ctld_target") == 0) + ctld_target = opt->value; + else if (strcmp(opt->name, "ctld_target_alias") == 0) + ctld_target_alias = opt->value; + else if (strcmp(opt->name, "ctld_lun") == 0) + ctld_lun = opt->value; + } + + if (ctld_target == NULL && ctld_lun == NULL) + return (0); + + if (ctld_target == NULL || ctld_lun == NULL) { + CFISCSI_WARN("lun added with ctld_target, but without " + "ctld_lun, or the other way around; ignoring"); + return (0); + } + + ct = cfiscsi_target_find_or_create(softc, ctld_target, ctld_target_alias); + if (ct == NULL) { + CFISCSI_WARN("failed to create target \"%s\"", ctld_target); + return (0); + } + + ctld_lun_id = strtoul(ctld_lun, NULL, 10); + cfiscsi_target_set_lun(ct, ctld_lun_id, lun_id); + return (0); +} + +static int +cfiscsi_lun_disable(void *arg, struct ctl_id target_id, int lun_id) +{ + struct cfiscsi_softc *softc; + struct cfiscsi_target *ct; + int i; + + softc = (struct cfiscsi_softc *)arg; + + mtx_lock(&softc->lock); + TAILQ_FOREACH(ct, &softc->targets, ct_next) { + for (i = 0; i < CTL_MAX_LUNS; i++) { + if (ct->ct_luns[i] < 0) + continue; + if (ct->ct_luns[i] != lun_id) + continue; + cfiscsi_target_unset_lun(ct, i); + break; + } + } + mtx_unlock(&softc->lock); + return (0); +} + +static void +cfiscsi_datamove(union ctl_io *io) +{ + struct cfiscsi_session *cs; + struct icl_pdu *request, *response; + const struct iscsi_bhs_scsi_command *bhssc; + struct iscsi_bhs_data_in *bhsdi; + struct iscsi_bhs_r2t *bhsr2t; + struct cfiscsi_data_wait *cdw; + struct ctl_sg_entry ctl_sg_entry, *ctl_sglist; + size_t copy_len, len, off; + const char *addr; + int ctl_sg_count, i; + uint32_t target_transfer_tag; + bool done; + + request = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr; + cs = PDU_SESSION(request); + + bhssc = (const struct iscsi_bhs_scsi_command *)request->ip_bhs; + KASSERT((bhssc->bhssc_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) == + ISCSI_BHS_OPCODE_SCSI_COMMAND, + ("bhssc->bhssc_opcode != ISCSI_BHS_OPCODE_SCSI_COMMAND")); + + if (io->scsiio.kern_sg_entries > 0) { + ctl_sglist = (struct ctl_sg_entry *)io->scsiio.kern_data_ptr; + ctl_sg_count = io->scsiio.kern_sg_entries; + } else { + ctl_sglist = &ctl_sg_entry; + ctl_sglist->addr = io->scsiio.kern_data_ptr; + ctl_sglist->len = io->scsiio.kern_data_len; + ctl_sg_count = 1; + } + + /* + * We need to record it so that we can properly report + * underflow/underflow. + */ + PDU_TOTAL_TRANSFER_LEN(request) = io->scsiio.kern_total_len; + + if ((io->io_hdr.flags & CTL_FLAG_DATA_MASK) == CTL_FLAG_DATA_IN) { +#if 0 + if (ctl_sg_count > 1) + CFISCSI_SESSION_DEBUG(cs, "ctl_sg_count = %d", ctl_sg_count); +#endif + + /* + * This is the offset within the current SCSI command; + * i.e. for the first call of datamove(), it will be 0, + * and for subsequent ones it will be the sum of lengths + * of previous ones. + */ + off = htonl(io->scsiio.kern_rel_offset); + if (off > 1) + CFISCSI_SESSION_DEBUG(cs, "off = %zd", off); + + i = 0; + addr = NULL; + len = 0; + response = NULL; + bhsdi = NULL; + for (;;) { + KASSERT(i < ctl_sg_count, ("i >= ctl_sg_count")); + if (response == NULL) { + response = + cfiscsi_pdu_new_response(request, M_WAITOK); + bhsdi = (struct iscsi_bhs_data_in *) + response->ip_bhs; + bhsdi->bhsdi_opcode = + ISCSI_BHS_OPCODE_SCSI_DATA_IN; + bhsdi->bhsdi_initiator_task_tag = + bhssc->bhssc_initiator_task_tag; + bhsdi->bhsdi_datasn = + htonl(PDU_EXPDATASN(request)); + PDU_EXPDATASN(request)++; + bhsdi->bhsdi_buffer_offset = htonl(off); + } + + if (len == 0) { + addr = ctl_sglist[i].addr; + len = ctl_sglist[i].len; + KASSERT(len > 0, ("len <= 0")); + } + + copy_len = len; + if (response->ip_data_len + copy_len > + cs->cs_max_data_segment_length) + copy_len = cs->cs_max_data_segment_length - + response->ip_data_len; + KASSERT(copy_len <= len, ("copy_len > len")); + icl_pdu_append_data(response, addr, copy_len); + addr += copy_len; + len -= copy_len; + off += copy_len; + io->scsiio.ext_data_filled += copy_len; + + if (len == 0) { + /* + * End of scatter-gather segment; + * proceed to the next one... + */ + if (i == ctl_sg_count - 1) { + /* + * ... unless this was the last one. + */ + break; + } + i++; + } + + if (response->ip_data_len == + cs->cs_max_data_segment_length) { + /* + * Can't stuff more data into the current PDU; + * queue it. Note that's not enough to check + * for kern_data_resid == 0 instead; there + * may be several Data-In PDUs for the final + * call to cfiscsi_datamove(), and we want + * to set the F flag only on the last of them. + */ + if (off == io->scsiio.kern_total_len) + bhsdi->bhsdi_flags |= BHSDI_FLAGS_F; + KASSERT(response->ip_data_len > 0, + ("sending empty Data-In")); + cfiscsi_pdu_queue(response); + response = NULL; + bhsdi = NULL; + } + } + KASSERT(i == ctl_sg_count - 1, ("missed SG segment")); + KASSERT(len == 0, ("missed data from SG segment")); + if (response != NULL) { + if (off == io->scsiio.kern_total_len) { + bhsdi->bhsdi_flags |= BHSDI_FLAGS_F; + } else { + CFISCSI_SESSION_DEBUG(cs, "not setting the F flag; " + "have %zd, need %zd", off, + (size_t)io->scsiio.kern_total_len); + } + KASSERT(response->ip_data_len > 0, + ("sending empty Data-In")); + cfiscsi_pdu_queue(response); + } + + io->scsiio.be_move_done(io); + } else { + CFISCSI_SESSION_LOCK(cs); + target_transfer_tag = cs->cs_target_transfer_tag; + cs->cs_target_transfer_tag++; + CFISCSI_SESSION_UNLOCK(cs); + +#if 0 + CFISCSI_SESSION_DEBUG(cs, "expecting Data-Out with initiator " + "task tag 0x%x, target transfer tag 0x%x", + bhssc->bhssc_initiator_task_tag, target_transfer_tag); +#endif + cdw = uma_zalloc(cfiscsi_data_wait_zone, M_WAITOK | M_ZERO); + cdw->cdw_ctl_io = io; + cdw->cdw_target_transfer_tag = htonl(target_transfer_tag); + cdw->cdw_initiator_task_tag = bhssc->bhssc_initiator_task_tag; + + if (cs->cs_immediate_data && + icl_pdu_data_segment_length(request) > 0) { + done = cfiscsi_handle_data_segment(request, cdw); + if (done) { + uma_zfree(cfiscsi_data_wait_zone, cdw); + io->scsiio.be_move_done(io); + return; + } + +#if 0 + if (io->scsiio.ext_data_filled != 0) + CFISCSI_SESSION_DEBUG(cs, "got %zd bytes of immediate data, need %zd", + io->scsiio.ext_data_filled, io->scsiio.kern_data_len); +#endif + } + + CFISCSI_SESSION_LOCK(cs); + TAILQ_INSERT_TAIL(&cs->cs_waiting_for_data_out, cdw, cdw_next); + CFISCSI_SESSION_UNLOCK(cs); + + /* + * XXX: We should limit the number of outstanding R2T PDUs + * per task to MaxOutstandingR2T. + */ + response = cfiscsi_pdu_new_response(request, M_WAITOK); + bhsr2t = (struct iscsi_bhs_r2t *)response->ip_bhs; + bhsr2t->bhsr2t_opcode = ISCSI_BHS_OPCODE_R2T; + bhsr2t->bhsr2t_flags = 0x80; + bhsr2t->bhsr2t_lun = bhssc->bhssc_lun; + bhsr2t->bhsr2t_initiator_task_tag = + bhssc->bhssc_initiator_task_tag; + bhsr2t->bhsr2t_target_transfer_tag = + htonl(target_transfer_tag); + /* + * XXX: Here we assume that cfiscsi_datamove() won't ever + * be running concurrently on several CPUs for a given + * command. + */ + bhsr2t->bhsr2t_r2tsn = htonl(PDU_R2TSN(request)); + PDU_R2TSN(request)++; + /* + * This is the offset within the current SCSI command; + * i.e. for the first call of datamove(), it will be 0, + * and for subsequent ones it will be the sum of lengths + * of previous ones. + * + * The ext_data_filled is to account for unsolicited + * (immediate) data that might have already arrived. + */ + bhsr2t->bhsr2t_buffer_offset = + htonl(io->scsiio.kern_rel_offset + io->scsiio.ext_data_filled); + /* + * This is the total length (sum of S/G lengths) this call + * to cfiscsi_datamove() is supposed to handle. + * + * XXX: Limit it to MaxBurstLength. + */ + bhsr2t->bhsr2t_desired_data_transfer_length = + htonl(io->scsiio.kern_data_len - io->scsiio.ext_data_filled); + cfiscsi_pdu_queue(response); + } +} + +static void +cfiscsi_scsi_command_done(union ctl_io *io) +{ + struct icl_pdu *request, *response; + struct iscsi_bhs_scsi_command *bhssc; + struct iscsi_bhs_scsi_response *bhssr; +#ifdef DIAGNOSTIC + struct cfiscsi_data_wait *cdw; +#endif + struct cfiscsi_session *cs; + uint16_t sense_length; + + request = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr; + cs = PDU_SESSION(request); + bhssc = (struct iscsi_bhs_scsi_command *)request->ip_bhs; + KASSERT((bhssc->bhssc_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) == + ISCSI_BHS_OPCODE_SCSI_COMMAND, + ("replying to wrong opcode 0x%x", bhssc->bhssc_opcode)); + + //CFISCSI_SESSION_DEBUG(cs, "initiator task tag 0x%x", + // bhssc->bhssc_initiator_task_tag); + +#ifdef DIAGNOSTIC + CFISCSI_SESSION_LOCK(cs); + TAILQ_FOREACH(cdw, &cs->cs_waiting_for_data_out, cdw_next) + KASSERT(bhssc->bhssc_initiator_task_tag != + cdw->cdw_initiator_task_tag, ("dangling cdw")); + CFISCSI_SESSION_UNLOCK(cs); +#endif + + response = cfiscsi_pdu_new_response(request, M_WAITOK); + bhssr = (struct iscsi_bhs_scsi_response *)response->ip_bhs; + bhssr->bhssr_opcode = ISCSI_BHS_OPCODE_SCSI_RESPONSE; + bhssr->bhssr_flags = 0x80; + /* + * XXX: We don't deal with bidirectional under/overflows; + * does anything actually support those? + */ + if (PDU_TOTAL_TRANSFER_LEN(request) < + ntohl(bhssc->bhssc_expected_data_transfer_length)) { + bhssr->bhssr_flags |= BHSSR_FLAGS_RESIDUAL_UNDERFLOW; + bhssr->bhssr_residual_count = + htonl(ntohl(bhssc->bhssc_expected_data_transfer_length) - + PDU_TOTAL_TRANSFER_LEN(request)); + //CFISCSI_SESSION_DEBUG(cs, "underflow; residual count %d", + // ntohl(bhssr->bhssr_residual_count)); + } else if (PDU_TOTAL_TRANSFER_LEN(request) > + ntohl(bhssc->bhssc_expected_data_transfer_length)) { + bhssr->bhssr_flags |= BHSSR_FLAGS_RESIDUAL_OVERFLOW; + bhssr->bhssr_residual_count = + htonl(PDU_TOTAL_TRANSFER_LEN(request) - + ntohl(bhssc->bhssc_expected_data_transfer_length)); + //CFISCSI_SESSION_DEBUG(cs, "overflow; residual count %d", + // ntohl(bhssr->bhssr_residual_count)); + } + bhssr->bhssr_response = BHSSR_RESPONSE_COMMAND_COMPLETED; + bhssr->bhssr_status = io->scsiio.scsi_status; + bhssr->bhssr_initiator_task_tag = bhssc->bhssc_initiator_task_tag; + bhssr->bhssr_expdatasn = htonl(PDU_EXPDATASN(request)); + + if (io->scsiio.sense_len > 0) { +#if 0 + CFISCSI_SESSION_DEBUG(cs, "returning %d bytes of sense data", + io->scsiio.sense_len); +#endif + sense_length = htons(io->scsiio.sense_len); + icl_pdu_append_data(response, + &sense_length, sizeof(sense_length)); + icl_pdu_append_data(response, + &io->scsiio.sense_data, io->scsiio.sense_len); + } + + ctl_free_io(io); + icl_pdu_free(request); + cfiscsi_pdu_queue(response); +} + +static void +cfiscsi_task_management_done(union ctl_io *io) +{ + struct icl_pdu *request, *response; + struct iscsi_bhs_task_management_request *bhstmr; + struct iscsi_bhs_task_management_response *bhstmr2; + struct cfiscsi_data_wait *cdw, *tmpcdw; + struct cfiscsi_session *cs; + + request = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr; + cs = PDU_SESSION(request); + bhstmr = (struct iscsi_bhs_task_management_request *)request->ip_bhs; + KASSERT((bhstmr->bhstmr_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) == + ISCSI_BHS_OPCODE_TASK_REQUEST, + ("replying to wrong opcode 0x%x", bhstmr->bhstmr_opcode)); + +#if 0 + CFISCSI_SESSION_DEBUG(cs, "initiator task tag 0x%x; referenced task tag 0x%x", + bhstmr->bhstmr_initiator_task_tag, + bhstmr->bhstmr_referenced_task_tag); +#endif + + if ((bhstmr->bhstmr_function & ~0x80) == + BHSTMR_FUNCTION_ABORT_TASK) { + /* + * Make sure we no longer wait for Data-Out for this command. + */ + CFISCSI_SESSION_LOCK(cs); + TAILQ_FOREACH_SAFE(cdw, + &cs->cs_waiting_for_data_out, cdw_next, tmpcdw) { + if (bhstmr->bhstmr_referenced_task_tag != + cdw->cdw_initiator_task_tag) + continue; + +#if 0 + CFISCSI_SESSION_DEBUG(cs, "removing csw for initiator task " + "tag 0x%x", bhstmr->bhstmr_initiator_task_tag); +#endif + TAILQ_REMOVE(&cs->cs_waiting_for_data_out, + cdw, cdw_next); + cdw->cdw_ctl_io->scsiio.be_move_done(cdw->cdw_ctl_io); + uma_zfree(cfiscsi_data_wait_zone, cdw); + } + CFISCSI_SESSION_UNLOCK(cs); + } + + response = cfiscsi_pdu_new_response(request, M_WAITOK); + bhstmr2 = (struct iscsi_bhs_task_management_response *) + response->ip_bhs; + bhstmr2->bhstmr_opcode = ISCSI_BHS_OPCODE_TASK_RESPONSE; + bhstmr2->bhstmr_flags = 0x80; + if (io->io_hdr.status == CTL_SUCCESS) { + bhstmr2->bhstmr_response = BHSTMR_RESPONSE_FUNCTION_COMPLETE; + } else { + /* + * XXX: How to figure out what exactly went wrong? iSCSI spec + * expects us to provide detailed error, e.g. "Task does + * not exist" or "LUN does not exist". + */ + CFISCSI_SESSION_DEBUG(cs, "BHSTMR_RESPONSE_FUNCTION_NOT_SUPPORTED"); + bhstmr2->bhstmr_response = + BHSTMR_RESPONSE_FUNCTION_NOT_SUPPORTED; + } + bhstmr2->bhstmr_initiator_task_tag = bhstmr->bhstmr_initiator_task_tag; + + ctl_free_io(io); + icl_pdu_free(request); + cfiscsi_pdu_queue(response); +} + +static void +cfiscsi_done(union ctl_io *io) +{ + struct icl_pdu *request; + struct cfiscsi_session *cs; + + KASSERT(((io->io_hdr.status & CTL_STATUS_MASK) != CTL_STATUS_NONE), + ("invalid CTL status %#x", io->io_hdr.status)); + + request = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr; + if (request == NULL) { + /* + * Implicit task termination has just completed; nothing to do. + */ + return; + } + + cs = PDU_SESSION(request); + refcount_release(&cs->cs_outstanding_ctl_pdus); + + switch (request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) { + case ISCSI_BHS_OPCODE_SCSI_COMMAND: + cfiscsi_scsi_command_done(io); + break; + case ISCSI_BHS_OPCODE_TASK_REQUEST: + cfiscsi_task_management_done(io); + break; + default: + panic("cfiscsi_done called with wrong opcode 0x%x", + request->ip_bhs->bhs_opcode); + } +} diff -urN p4/freebsd/src/sys/cam/ctl/ctl_frontend_iscsi.h p4/iscsi/sys/cam/ctl/ctl_frontend_iscsi.h --- p4/freebsd/src/sys/cam/ctl/ctl_frontend_iscsi.h 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/sys/cam/ctl/ctl_frontend_iscsi.h 2013-08-18 13:07:25.000000000 +0200 @@ -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 */ diff -urN p4/freebsd/src/sys/cam/ctl/ctl_io.h p4/iscsi/sys/cam/ctl/ctl_io.h --- p4/freebsd/src/sys/cam/ctl/ctl_io.h 2013-04-20 20:02:14.000000000 +0200 +++ p4/iscsi/sys/cam/ctl/ctl_io.h 2013-07-23 23:42:57.000000000 +0200 @@ -204,6 +204,8 @@ uint32_t targ_port; /* Target port, filled in by PORT */ struct ctl_id targ_target; /* Destination target */ uint32_t targ_lun; /* Destination lun */ + uint32_t (*lun_map_fn)(void *arg, uint32_t lun); + void *lun_map_arg; }; typedef enum { diff -urN p4/freebsd/src/sys/cam/ctl/ctl_ioctl.h p4/iscsi/sys/cam/ctl/ctl_ioctl.h --- p4/freebsd/src/sys/cam/ctl/ctl_ioctl.h 2013-04-20 20:02:15.000000000 +0200 +++ p4/iscsi/sys/cam/ctl/ctl_ioctl.h 2013-08-18 13:07:25.000000000 +0200 @@ -40,6 +40,12 @@ #ifndef _CTL_IOCTL_H_ #define _CTL_IOCTL_H_ +#ifdef ICL_KERNEL_PROXY +#include +#endif + +#include + #define CTL_DEFAULT_DEV "/dev/cam/ctl" /* * Maximum number of targets we support. @@ -588,6 +594,168 @@ /* 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 @@ #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_ */ diff -urN p4/freebsd/src/sys/cam/ctl/iscsi.h p4/iscsi/sys/cam/ctl/iscsi.h --- p4/freebsd/src/sys/cam/ctl/iscsi.h 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/sys/cam/ctl/iscsi.h 2013-07-23 23:42:57.000000000 +0200 @@ -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_H +#define ISCSI_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_H */ diff -urN p4/freebsd/src/sys/cam/scsi/scsi_all.h p4/iscsi/sys/cam/scsi/scsi_all.h --- p4/freebsd/src/sys/cam/scsi/scsi_all.h 2013-07-24 07:52:02.000000000 +0200 +++ p4/iscsi/sys/cam/scsi/scsi_all.h 2013-07-25 19:17:08.000000000 +0200 @@ -1288,7 +1288,7 @@ #define SCSI_PROTO_SSA 0x02 #define SCSI_PROTO_1394 0x03 #define SCSI_PROTO_RDMA 0x04 -#define SCSI_PROTO_iSCSI 0x05 +#define SCSI_PROTO_ISCSI 0x05 #define SCSI_PROTO_SAS 0x06 #define SCSI_PROTO_ADT 0x07 #define SCSI_PROTO_ATA 0x08 diff -urN p4/freebsd/src/sys/conf/NOTES p4/iscsi/sys/conf/NOTES --- p4/freebsd/src/sys/conf/NOTES 2013-08-18 13:08:38.000000000 +0200 +++ p4/iscsi/sys/conf/NOTES 2013-08-18 13:36:29.000000000 +0200 @@ -1529,6 +1529,7 @@ device ahc device ahd device esp +device iscsi device iscsi_initiator device isp hint.isp.0.disable="1" diff -urN p4/freebsd/src/sys/conf/files p4/iscsi/sys/conf/files --- p4/freebsd/src/sys/conf/files 2013-08-18 13:08:38.000000000 +0200 +++ p4/iscsi/sys/conf/files 2013-08-18 13:36:29.000000000 +0200 @@ -124,6 +124,7 @@ 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 @@ -1520,12 +1521,15 @@ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "ipw_monitor.fw" -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 -dev/iscsi/initiator/isc_soc.c optional iscsi_initiator scbus -dev/iscsi/initiator/isc_sm.c optional iscsi_initiator scbus -dev/iscsi/initiator/isc_subr.c optional iscsi_initiator scbus +dev/iscsi/icl.c optional iscsi scbus +dev/iscsi/icl_proxy.c optional iscsi scbus +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 +dev/iscsi_initiator/isc_soc.c optional iscsi_initiator scbus +dev/iscsi_initiator/isc_sm.c optional iscsi_initiator scbus +dev/iscsi_initiator/isc_subr.c optional iscsi_initiator scbus dev/isf/isf.c optional isf dev/isf/isf_fdt.c optional isf fdt dev/isf/isf_nexus.c optional isf diff -urN p4/freebsd/src/sys/dev/iscsi/icl.c p4/iscsi/sys/dev/iscsi/icl.c --- p4/freebsd/src/sys/dev/iscsi/icl.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/sys/dev/iscsi/icl.c 2013-08-18 13:07:26.000000000 +0200 @@ -0,0 +1,1292 @@ +/*- + * 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$ + */ + +/* + * iSCSI Common Layer. It's used by both the initiator and target to send + * and receive iSCSI PDUs. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "icl.h" +#include "../../cam/ctl/iscsi.h" /* XXX */ + +SYSCTL_NODE(_kern, OID_AUTO, icl, CTLFLAG_RD, 0, "iSCSI Common Layer"); +static int debug = 1; +TUNABLE_INT("kern.icl.debug", &debug); +SYSCTL_INT(_kern_icl, OID_AUTO, debug, CTLFLAG_RW, + &debug, 1, "Enable debug messages"); +static int partial_receive_len = 1 * 1024; /* XXX: More? */ +TUNABLE_INT("kern.icl.partial_receive_len", &partial_receive_len); +SYSCTL_INT(_kern_icl, OID_AUTO, partial_receive_len, CTLFLAG_RW, + &partial_receive_len, 1 * 1024, "Minimum read size for partially received " + "data segment"); + +static uma_zone_t icl_conn_zone; +static uma_zone_t icl_pdu_zone; + +static volatile u_int icl_ncons; + +#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) + +#define ICL_CONN_LOCK(X) mtx_lock(&X->ic_lock) +#define ICL_CONN_UNLOCK(X) mtx_unlock(&X->ic_lock) +#define ICL_CONN_LOCK_ASSERT(X) mtx_assert(&X->ic_lock, MA_OWNED) + +static uma_zone_t icl_pdu_zone; + +static void +icl_conn_fail(struct icl_conn *ic) +{ + if (ic->ic_socket == NULL) + return; + + /* + * XXX + */ + ic->ic_socket->so_error = EDOOFUS; + (ic->ic_error)(ic); +} + +static struct mbuf * +icl_conn_receive(struct icl_conn *ic, size_t len) +{ + struct uio uio; + struct socket *so; + struct mbuf *m; + int error, flags; + + so = ic->ic_socket; + + memset(&uio, 0, sizeof(uio)); + uio.uio_resid = len; + + flags = MSG_DONTWAIT; + error = soreceive(so, NULL, &uio, &m, NULL, &flags); + if (error != 0) { + ICL_DEBUG("soreceive error %d", error); + return (NULL); + } + if (uio.uio_resid != 0) { + m_freem(m); + ICL_DEBUG("short read"); + return (NULL); + } + + return (m); +} + +static struct icl_pdu * +icl_pdu_new(struct icl_conn *ic, int flags) +{ + struct icl_pdu *ip; + + refcount_acquire(&ic->ic_outstanding_pdus); + ip = uma_zalloc(icl_pdu_zone, flags | M_ZERO); + if (ip == NULL) { + ICL_WARN("failed to allocate %zd bytes", sizeof(*ip)); + refcount_release(&ic->ic_outstanding_pdus); + return (NULL); + } + + ip->ip_conn = ic; + + return (ip); +} + +void +icl_pdu_free(struct icl_pdu *ip) +{ + struct icl_conn *ic; + + ic = ip->ip_conn; + + m_freem(ip->ip_bhs_mbuf); + m_freem(ip->ip_ahs_mbuf); + m_freem(ip->ip_data_mbuf); + uma_zfree(icl_pdu_zone, ip); + refcount_release(&ic->ic_outstanding_pdus); +} + +/* + * Allocate icl_pdu with empty BHS to fill up by the caller. + */ +struct icl_pdu * +icl_pdu_new_bhs(struct icl_conn *ic, int flags) +{ + struct icl_pdu *ip; + + ip = icl_pdu_new(ic, flags); + if (ip == NULL) + return (NULL); + + ip->ip_bhs_mbuf = m_getm2(NULL, sizeof(struct iscsi_bhs), + flags, MT_DATA, M_PKTHDR); + if (ip->ip_bhs_mbuf == NULL) { + ICL_WARN("failed to allocate %zd bytes", sizeof(*ip)); + icl_pdu_free(ip); + return (NULL); + } + ip->ip_bhs = mtod(ip->ip_bhs_mbuf, struct iscsi_bhs *); + memset(ip->ip_bhs, 0, sizeof(struct iscsi_bhs)); + ip->ip_bhs_mbuf->m_len = sizeof(struct iscsi_bhs); + + return (ip); +} + +static int +icl_pdu_ahs_length(const struct icl_pdu *request) +{ + + return (request->ip_bhs->bhs_total_ahs_len * 4); +} + +int +icl_pdu_data_segment_length(const struct icl_pdu *request) +{ + uint32_t len = 0; + + len += request->ip_bhs->bhs_data_segment_len[0]; + len <<= 8; + len += request->ip_bhs->bhs_data_segment_len[1]; + len <<= 8; + len += request->ip_bhs->bhs_data_segment_len[2]; + + return (len); +} + +static void +icl_pdu_set_data_segment_length(struct icl_pdu *response, uint32_t len) +{ + + response->ip_bhs->bhs_data_segment_len[2] = len; + response->ip_bhs->bhs_data_segment_len[1] = len >> 8; + response->ip_bhs->bhs_data_segment_len[0] = len >> 16; +} + +static size_t +icl_pdu_padding(const struct icl_pdu *ip) +{ + + if ((ip->ip_data_len % 4) != 0) + return (4 - (ip->ip_data_len % 4)); + + return (0); +} + +static size_t +icl_pdu_size(const struct icl_pdu *response) +{ + size_t len; + + KASSERT(response->ip_ahs_len == 0, ("responding with AHS")); + + len = sizeof(struct iscsi_bhs) + response->ip_data_len + + icl_pdu_padding(response); + if (response->ip_conn->ic_header_crc32c) + len += ISCSI_HEADER_DIGEST_SIZE; + if (response->ip_conn->ic_data_crc32c) + len += ISCSI_DATA_DIGEST_SIZE; + + return (len); +} + +static int +icl_pdu_receive_bhs(struct icl_pdu *request, size_t *availablep) +{ + struct mbuf *m; + + m = icl_conn_receive(request->ip_conn, sizeof(struct iscsi_bhs)); + if (m == NULL) { + ICL_DEBUG("failed to receive BHS"); + return (-1); + } + + request->ip_bhs_mbuf = m_pullup(m, sizeof(struct iscsi_bhs)); + if (request->ip_bhs_mbuf == NULL) { + ICL_WARN("m_pullup failed"); + return (-1); + } + request->ip_bhs = mtod(request->ip_bhs_mbuf, struct iscsi_bhs *); + + /* + * XXX: For architectures with strict alignment requirements + * we may need to allocate ip_bhs and copy the data into it. + * For some reason, though, not doing this doesn't seem + * to cause problems; tested on sparc64. + */ + + *availablep -= sizeof(struct iscsi_bhs); + return (0); +} + +static int +icl_pdu_receive_ahs(struct icl_pdu *request, size_t *availablep) +{ + + request->ip_ahs_len = icl_pdu_ahs_length(request); + if (request->ip_ahs_len == 0) + return (0); + + request->ip_ahs_mbuf = icl_conn_receive(request->ip_conn, + request->ip_ahs_len); + if (request->ip_ahs_mbuf == NULL) { + ICL_DEBUG("failed to receive AHS"); + return (-1); + } + + *availablep -= request->ip_ahs_len; + return (0); +} + +static uint32_t +icl_mbuf_to_crc32c(const struct mbuf *m0) +{ + uint32_t digest = 0xffffffff; + const struct mbuf *m; + + for (m = m0; m != NULL; m = m->m_next) + digest = calculate_crc32c(digest, + mtod(m, const void *), m->m_len); + + digest = digest ^ 0xffffffff; + + return (digest); +} + +static int +icl_pdu_check_header_digest(struct icl_pdu *request, size_t *availablep) +{ + struct mbuf *m; + uint32_t received_digest, valid_digest; + + if (request->ip_conn->ic_header_crc32c == false) + return (0); + + m = icl_conn_receive(request->ip_conn, ISCSI_HEADER_DIGEST_SIZE); + if (m == NULL) { + ICL_DEBUG("failed to receive header digest"); + return (-1); + } + + CTASSERT(sizeof(received_digest) == ISCSI_HEADER_DIGEST_SIZE); + memcpy(&received_digest, mtod(m, void *), ISCSI_HEADER_DIGEST_SIZE); + m_freem(m); + + *availablep -= ISCSI_HEADER_DIGEST_SIZE; + + /* + * XXX: Handle AHS. + */ + valid_digest = icl_mbuf_to_crc32c(request->ip_bhs_mbuf); + if (received_digest != valid_digest) { + ICL_WARN("header digest check failed; got 0x%x, " + "should be 0x%x", received_digest, valid_digest); + return (-1); + } + + return (0); +} + +/* + * Return the number of bytes that should be waiting in the receive socket + * before icl_pdu_receive_data_segment() gets called. + */ +static size_t +icl_pdu_data_segment_receive_len(const struct icl_pdu *request) +{ + size_t len; + + len = icl_pdu_data_segment_length(request); + if (len == 0) + return (0); + + /* + * Account for the parts of data segment already read from + * the socket buffer. + */ + KASSERT(len > request->ip_data_len, ("len <= request->ip_data_len")); + len -= request->ip_data_len; + + /* + * Don't always wait for the full data segment to be delivered + * to the socket; this might badly affect performance due to + * TCP window scaling. + */ + if (len > partial_receive_len) { +#if 0 + ICL_DEBUG("need %zd bytes of data, limiting to %zd", + len, partial_receive_len)); +#endif + len = partial_receive_len; + + return (len); + } + + /* + * Account for padding. Note that due to the way code is written, + * the icl_pdu_receive_data_segment() must always receive padding + * along with the last part of data segment, because it would be + * impossible to tell whether we've already received the full data + * segment including padding, or without it. + */ + if ((len % 4) != 0) + len += 4 - (len % 4); + +#if 0 + ICL_DEBUG("need %zd bytes of data", len)); +#endif + + return (len); +} + +static int +icl_pdu_receive_data_segment(struct icl_pdu *request, + size_t *availablep, bool *more_neededp) +{ + struct icl_conn *ic; + size_t len, padding = 0; + struct mbuf *m; + + ic = request->ip_conn; + + *more_neededp = false; + ic->ic_receive_len = 0; + + len = icl_pdu_data_segment_length(request); + if (len == 0) + return (0); + + if ((len % 4) != 0) + padding = 4 - (len % 4); + + /* + * Account for already received parts of data segment. + */ + KASSERT(len > request->ip_data_len, ("len <= request->ip_data_len")); + len -= request->ip_data_len; + + if (len + padding > *availablep) { + /* + * Not enough data in the socket buffer. Receive as much + * as we can. Don't receive padding, since, obviously, it's + * not the end of data segment yet. + */ +#if 0 + ICL_DEBUG("limited from %zd to %zd", + len + padding, *availablep - padding)); +#endif + len = *availablep - padding; + *more_neededp = true; + padding = 0; + } + + /* + * Must not try to receive padding without at least one byte + * of actual data segment. + */ + if (len > 0) { + m = icl_conn_receive(request->ip_conn, len + padding); + if (m == NULL) { + ICL_DEBUG("failed to receive data segment"); + return (-1); + } + + if (request->ip_data_mbuf == NULL) + request->ip_data_mbuf = m; + else + m_cat(request->ip_data_mbuf, m); + + request->ip_data_len += len; + *availablep -= len + padding; + } else + ICL_DEBUG("len 0"); + + if (*more_neededp) + ic->ic_receive_len = + icl_pdu_data_segment_receive_len(request); + + return (0); +} + +static int +icl_pdu_check_data_digest(struct icl_pdu *request, size_t *availablep) +{ + struct mbuf *m; + uint32_t received_digest, valid_digest; + + if (request->ip_conn->ic_data_crc32c == false) + return (0); + + if (request->ip_data_len == 0) + return (0); + + m = icl_conn_receive(request->ip_conn, ISCSI_DATA_DIGEST_SIZE); + if (m == NULL) { + ICL_DEBUG("failed to receive data digest"); + return (-1); + } + + CTASSERT(sizeof(received_digest) == ISCSI_DATA_DIGEST_SIZE); + memcpy(&received_digest, mtod(m, void *), ISCSI_DATA_DIGEST_SIZE); + m_freem(m); + + *availablep -= ISCSI_DATA_DIGEST_SIZE; + + /* + * Note that ip_data_mbuf also contains padding; since digest + * calculation is supposed to include that, we iterate over + * the entire ip_data_mbuf chain, not just ip_data_len bytes of it. + */ + valid_digest = icl_mbuf_to_crc32c(request->ip_data_mbuf); + if (received_digest != valid_digest) { + ICL_WARN("data digest check failed; got 0x%x, " + "should be 0x%x", received_digest, valid_digest); + return (-1); + } + + return (0); +} + +/* + * Somewhat contrary to the name, this attempts to receive only one + * "part" of PDU at a time; call it repeatedly until it returns non-NULL. + */ +static struct icl_pdu * +icl_conn_receive_pdu(struct icl_conn *ic, size_t *availablep) +{ + struct icl_pdu *request; + struct socket *so; + size_t len; + int error; + bool more_needed; + + so = ic->ic_socket; + + if (ic->ic_receive_state == ICL_CONN_STATE_BHS) { + KASSERT(ic->ic_receive_pdu == NULL, + ("ic->ic_receive_pdu != NULL")); + request = icl_pdu_new(ic, M_NOWAIT); + if (request == NULL) { + ICL_DEBUG("failed to allocate PDU; " + "dropping connection"); + icl_conn_fail(ic); + return (NULL); + } + ic->ic_receive_pdu = request; + } else { + KASSERT(ic->ic_receive_pdu != NULL, + ("ic->ic_receive_pdu == NULL")); + request = ic->ic_receive_pdu; + } + + if (*availablep < ic->ic_receive_len) { +#if 0 + ICL_DEBUG("not enough data; need %zd, " + "have %zd", ic->ic_receive_len, *availablep); +#endif + return (NULL); + } + + switch (ic->ic_receive_state) { + case ICL_CONN_STATE_BHS: + //ICL_DEBUG("receiving BHS"); + error = icl_pdu_receive_bhs(request, availablep); + if (error != 0) { + ICL_DEBUG("failed to receive BHS; " + "dropping connection"); + break; + } + + /* + * We don't enforce any limit for AHS length; + * its length is stored in 8 bit field. + */ + + len = icl_pdu_data_segment_length(request); + if (len > ic->ic_max_data_segment_length) { + ICL_WARN("received data segment " + "length %zd is larger than negotiated " + "MaxDataSegmentLength %zd; " + "dropping connection", + len, ic->ic_max_data_segment_length); + break; + } + + ic->ic_receive_state = ICL_CONN_STATE_AHS; + ic->ic_receive_len = icl_pdu_ahs_length(request); + break; + + case ICL_CONN_STATE_AHS: + //ICL_DEBUG("receiving AHS"); + error = icl_pdu_receive_ahs(request, availablep); + if (error != 0) { + ICL_DEBUG("failed to receive AHS; " + "dropping connection"); + break; + } + ic->ic_receive_state = ICL_CONN_STATE_HEADER_DIGEST; + if (ic->ic_header_crc32c == false) + ic->ic_receive_len = 0; + else + ic->ic_receive_len = ISCSI_HEADER_DIGEST_SIZE; + break; + + case ICL_CONN_STATE_HEADER_DIGEST: + //ICL_DEBUG("receiving header digest"); + error = icl_pdu_check_header_digest(request, availablep); + if (error != 0) { + ICL_DEBUG("header digest failed; " + "dropping connection"); + break; + } + + ic->ic_receive_state = ICL_CONN_STATE_DATA; + ic->ic_receive_len = + icl_pdu_data_segment_receive_len(request); + break; + + case ICL_CONN_STATE_DATA: + //ICL_DEBUG("receiving data segment"); + error = icl_pdu_receive_data_segment(request, availablep, + &more_needed); + if (error != 0) { + ICL_DEBUG("failed to receive data segment;" + "dropping connection"); + break; + } + + if (more_needed) + break; + + ic->ic_receive_state = ICL_CONN_STATE_DATA_DIGEST; + if (ic->ic_data_crc32c == false) + ic->ic_receive_len = 0; + else + ic->ic_receive_len = ISCSI_DATA_DIGEST_SIZE; + break; + + case ICL_CONN_STATE_DATA_DIGEST: + //ICL_DEBUG("receiving data digest"); + error = icl_pdu_check_data_digest(request, availablep); + if (error != 0) { + ICL_DEBUG("data digest failed; " + "dropping connection"); + break; + } + + /* + * We've received complete PDU; reset the receive state machine + * and return the PDU. + */ + ic->ic_receive_state = ICL_CONN_STATE_BHS; + ic->ic_receive_len = sizeof(struct iscsi_bhs); + ic->ic_receive_pdu = NULL; + return (request); + + default: + panic("invalid ic_receive_state %d\n", ic->ic_receive_state); + } + + if (error != 0) { + icl_pdu_free(request); + icl_conn_fail(ic); + } + + return (NULL); +} + +static void +icl_conn_receive_pdus(struct icl_conn *ic, size_t available) +{ + struct icl_pdu *response; + struct socket *so; + + so = ic->ic_socket; + + /* + * This can never happen; we're careful to only mess with ic->ic_socket + * pointer when the send/receive threads are not running. + */ + KASSERT(so != NULL, ("NULL socket")); + + for (;;) { + if (ic->ic_disconnecting) + return; + + if (so->so_error != 0) { + ICL_DEBUG("connection error %d; " + "dropping connection", so->so_error); + icl_conn_fail(ic); + return; + } + + /* + * Loop until we have a complete PDU or there is not enough + * data in the socket buffer. + */ + if (available < ic->ic_receive_len) { +#if 0 + ICL_DEBUG("not enough data; have %zd, " + "need %zd", available, + ic->ic_receive_len); +#endif + return; + } + + response = icl_conn_receive_pdu(ic, &available); + if (response == NULL) + continue; + + if (response->ip_ahs_len > 0) { + ICL_WARN("received PDU with unsupported " + "AHS; opcode 0x%x; dropping connection", + response->ip_bhs->bhs_opcode); + icl_pdu_free(response); + icl_conn_fail(ic); + return; + } + + (ic->ic_receive)(response); + } +} + +static void +icl_receive_thread(void *arg) +{ + struct icl_conn *ic; + size_t available; + struct socket *so; + + ic = arg; + so = ic->ic_socket; + + ICL_CONN_LOCK(ic); + ic->ic_receive_running = true; + ICL_CONN_UNLOCK(ic); + + for (;;) { + if (ic->ic_disconnecting) { + //ICL_DEBUG("terminating"); + ICL_CONN_LOCK(ic); + ic->ic_receive_running = false; + ICL_CONN_UNLOCK(ic); + kthread_exit(); + return; + } + + SOCKBUF_LOCK(&so->so_rcv); + available = so->so_rcv.sb_cc; + if (available < ic->ic_receive_len) { + so->so_rcv.sb_lowat = ic->ic_receive_len; + cv_wait(&ic->ic_receive_cv, &so->so_rcv.sb_mtx); + } + SOCKBUF_UNLOCK(&so->so_rcv); + + icl_conn_receive_pdus(ic, available); + } +} + +static int +icl_soupcall_receive(struct socket *so, void *arg, int waitflag) +{ + struct icl_conn *ic; + + ic = arg; + cv_signal(&ic->ic_receive_cv); + return (SU_OK); +} + +static int +icl_pdu_send(struct icl_pdu *request) +{ + size_t padding, pdu_len; + uint32_t digest, zero = 0; + int error, ok; + struct socket *so; + struct icl_conn *ic; + + ic = request->ip_conn; + so = request->ip_conn->ic_socket; + + ICL_CONN_LOCK_ASSERT(ic); + + icl_pdu_set_data_segment_length(request, request->ip_data_len); + + pdu_len = icl_pdu_size(request); + + if (ic->ic_header_crc32c) { + digest = icl_mbuf_to_crc32c(request->ip_bhs_mbuf); + ok = m_append(request->ip_bhs_mbuf, sizeof(digest), + (void *)&digest); + if (ok != 1) { + ICL_WARN("failed to append header digest"); + return (1); + } + } + + if (request->ip_data_len != 0) { + padding = icl_pdu_padding(request); + if (padding > 0) { + ok = m_append(request->ip_data_mbuf, padding, + (void *)&zero); + if (ok != 1) { + ICL_WARN("failed to append padding"); + return (1); + } + } + + if (ic->ic_data_crc32c) { + digest = icl_mbuf_to_crc32c(request->ip_data_mbuf); + + ok = m_append(request->ip_data_mbuf, sizeof(digest), + (void *)&digest); + if (ok != 1) { + ICL_WARN("failed to append header digest"); + return (1); + } + } + + m_cat(request->ip_bhs_mbuf, request->ip_data_mbuf); + request->ip_data_mbuf = NULL; + } + + request->ip_bhs_mbuf->m_pkthdr.len = pdu_len; + + error = sosend(so, NULL, NULL, request->ip_bhs_mbuf, + NULL, MSG_DONTWAIT, curthread); + request->ip_bhs_mbuf = NULL; /* Sosend consumes the mbuf. */ + if (error != 0) { + ICL_DEBUG("sosend error %d", error); + return (error); + } + + return (0); +} + +static void +icl_conn_send_pdus(struct icl_conn *ic) +{ + struct icl_pdu *request; + struct socket *so; + size_t available, size; + int error; + + ICL_CONN_LOCK_ASSERT(ic); + + so = ic->ic_socket; + + SOCKBUF_LOCK(&so->so_snd); + available = sbspace(&so->so_snd); + SOCKBUF_UNLOCK(&so->so_snd); + + while (!TAILQ_EMPTY(&ic->ic_to_send)) { + if (ic->ic_disconnecting) + return; + + request = TAILQ_FIRST(&ic->ic_to_send); + size = icl_pdu_size(request); + if (available < size) { + /* + * Set the low watermark on the socket, + * to avoid waking up until there is enough + * space. + */ + SOCKBUF_LOCK(&so->so_snd); + so->so_snd.sb_lowat = size; + SOCKBUF_UNLOCK(&so->so_snd); +#if 1 + ICL_DEBUG("no space to send; " + "have %zd, need %zd", + available, size); +#endif + return; + } + available -= size; + TAILQ_REMOVE(&ic->ic_to_send, request, ip_next); + error = icl_pdu_send(request); + if (error != 0) { + ICL_DEBUG("failed to send PDU; " + "dropping connection"); + icl_conn_fail(ic); + return; + } + icl_pdu_free(request); + } +} + +static void +icl_send_thread(void *arg) +{ + struct icl_conn *ic; + + ic = arg; + + ICL_CONN_LOCK(ic); + ic->ic_send_running = true; + ICL_CONN_UNLOCK(ic); + + for (;;) { + ICL_CONN_LOCK(ic); + if (ic->ic_disconnecting) { + //ICL_DEBUG("terminating"); + ic->ic_send_running = false; + ICL_CONN_UNLOCK(ic); + kthread_exit(); + return; + } + if (TAILQ_EMPTY(&ic->ic_to_send)) + cv_wait(&ic->ic_send_cv, &ic->ic_lock); + icl_conn_send_pdus(ic); + ICL_CONN_UNLOCK(ic); + } +} + +static int +icl_soupcall_send(struct socket *so, void *arg, int waitflag) +{ + struct icl_conn *ic; + + ic = arg; + cv_signal(&ic->ic_send_cv); + return (SU_OK); +} + +void +icl_pdu_append_data(struct icl_pdu *request, const void *addr, size_t len) +{ + struct mbuf *mb, *newmb; + size_t copylen, off = 0; + + KASSERT(len > 0, ("len == 0")); + + newmb = m_getm2(NULL, len, M_WAITOK, MT_DATA, M_PKTHDR); + if (newmb == NULL) { + ICL_WARN("failed to allocate mbuf for %zd bytes", len); + icl_conn_fail(request->ip_conn); + return; + } + + for (mb = newmb; mb != NULL; mb = mb->m_next) { + copylen = min(M_TRAILINGSPACE(mb), len - off); + memcpy(mtod(mb, char *), (const char *)addr + off, copylen); + mb->m_len = copylen; + off += copylen; + } + KASSERT(off == len, ("%s: off != len", __func__)); + + if (request->ip_data_mbuf == NULL) { + request->ip_data_mbuf = newmb; + request->ip_data_len = len; + } else { + m_cat(request->ip_data_mbuf, newmb); + request->ip_data_len += len; + } +} + +void +icl_pdu_get_data(struct icl_pdu *ip, size_t off, void *addr, size_t len) +{ + + m_copydata(ip->ip_data_mbuf, off, len, addr); +} + +void +icl_pdu_queue(struct icl_pdu *ip) +{ + struct icl_conn *ic; + + ic = ip->ip_conn; + + ICL_CONN_LOCK(ic); + if (ic->ic_disconnecting || ic->ic_socket == NULL) { + ICL_DEBUG("icl_pdu_queue on closed connection"); + ICL_CONN_UNLOCK(ic); + return; + } + TAILQ_INSERT_TAIL(&ic->ic_to_send, ip, ip_next); + ICL_CONN_UNLOCK(ic); + cv_signal(&ic->ic_send_cv); +} + +struct icl_conn * +icl_conn_new(void) +{ + struct icl_conn *ic; + + refcount_acquire(&icl_ncons); + + ic = uma_zalloc(icl_conn_zone, M_WAITOK | M_ZERO); + + TAILQ_INIT(&ic->ic_to_send); + mtx_init(&ic->ic_lock, "icl_lock", NULL, MTX_DEF); + cv_init(&ic->ic_send_cv, "icl_tx"); + cv_init(&ic->ic_receive_cv, "icl_rx"); + refcount_init(&ic->ic_outstanding_pdus, 0); + ic->ic_max_data_segment_length = ICL_MAX_DATA_SEGMENT_LENGTH; + + return (ic); +} + +void +icl_conn_free(struct icl_conn *ic) +{ + + mtx_destroy(&ic->ic_lock); + cv_destroy(&ic->ic_send_cv); + cv_destroy(&ic->ic_receive_cv); + uma_zfree(icl_conn_zone, ic); + refcount_release(&icl_ncons); +} + +static int +icl_conn_start(struct icl_conn *ic) +{ + size_t bufsize; + struct sockopt opt; + int error, one = 1; + + ICL_CONN_LOCK(ic); + + /* + * XXX: Ugly hack. + */ + if (ic->ic_socket == NULL) { + ICL_CONN_UNLOCK(ic); + return (EINVAL); + } + + ic->ic_receive_state = ICL_CONN_STATE_BHS; + ic->ic_receive_len = sizeof(struct iscsi_bhs); + ic->ic_disconnecting = false; + + ICL_CONN_UNLOCK(ic); + + /* + * Use max available sockbuf size for sending. Do it manually + * instead of sbreserve(9) to work around resource limits. + * + * XXX: This kind of sucks. On one hand, we don't currently support + * sending a part of data segment; we always do it in one piece, + * so we have to make sure it can fit in the socket buffer. + * Once I've implemented partial send, we'll get rid of this + * and use autoscaling. + */ + bufsize = (sizeof(struct iscsi_bhs) + + ic->ic_max_data_segment_length) * 8; + error = soreserve(ic->ic_socket, bufsize, bufsize); + if (error != 0) { + ICL_WARN("soreserve failed with error %d", error); + icl_conn_close(ic); + return (error); + } + + /* + * Disable Nagle. + */ + bzero(&opt, sizeof(opt)); + opt.sopt_dir = SOPT_SET; + opt.sopt_level = IPPROTO_TCP; + opt.sopt_name = TCP_NODELAY; + opt.sopt_val = &one; + opt.sopt_valsize = sizeof(one); + error = sosetopt(ic->ic_socket, &opt); + if (error != 0) { + ICL_WARN("disabling TCP_NODELAY failed with error %d", error); + icl_conn_close(ic); + return (error); + } + + /* + * Start threads. + */ + error = kthread_add(icl_send_thread, ic, NULL, NULL, 0, 0, "icltx"); + if (error != 0) { + ICL_WARN("kthread_add(9) failed with error %d", error); + icl_conn_close(ic); + return (error); + } + + error = kthread_add(icl_receive_thread, ic, NULL, NULL, 0, 0, "iclrx"); + if (error != 0) { + ICL_WARN("kthread_add(9) failed with error %d", error); + icl_conn_close(ic); + return (error); + } + + /* + * Register socket upcall, to get notified about incoming PDUs + * and free space to send outgoing ones. + */ + SOCKBUF_LOCK(&ic->ic_socket->so_snd); + soupcall_set(ic->ic_socket, SO_SND, icl_soupcall_send, ic); + SOCKBUF_UNLOCK(&ic->ic_socket->so_snd); + SOCKBUF_LOCK(&ic->ic_socket->so_rcv); + soupcall_set(ic->ic_socket, SO_RCV, icl_soupcall_receive, ic); + SOCKBUF_UNLOCK(&ic->ic_socket->so_rcv); + + return (0); +} + +int +icl_conn_handoff(struct icl_conn *ic, int fd) +{ + struct file *fp; + struct socket *so; + int error; + + /* + * Steal the socket from userland. + */ + error = fget(curthread, fd, CAP_SOCK_ALL, &fp); + if (error != 0) { + return (error); + } + if (fp->f_type != DTYPE_SOCKET) { + fdrop(fp, curthread); + return (EINVAL); + } + so = fp->f_data; + if (so->so_type != SOCK_STREAM) { + fdrop(fp, curthread); + return (EINVAL); + } + + ICL_CONN_LOCK(ic); + + if (ic->ic_socket != NULL) { + ICL_CONN_UNLOCK(ic); + fdrop(fp, curthread); + return (EBUSY); + } + + ic->ic_socket = fp->f_data; + fp->f_ops = &badfileops; + fp->f_data = NULL; + fdrop(fp, curthread); + ICL_CONN_UNLOCK(ic); + + error = icl_conn_start(ic); + + return (error); +} + +void +icl_conn_shutdown(struct icl_conn *ic) +{ + + ICL_CONN_LOCK(ic); + if (ic->ic_socket == NULL) { + ICL_CONN_UNLOCK(ic); + return; + } + ICL_CONN_UNLOCK(ic); + + soshutdown(ic->ic_socket, SHUT_RDWR); +} + +void +icl_conn_close(struct icl_conn *ic) +{ + struct icl_pdu *pdu; + + ICL_CONN_LOCK(ic); + if (ic->ic_socket == NULL) { + ICL_CONN_UNLOCK(ic); + return; + } + + ic->ic_disconnecting = true; + + /* + * Wake up the threads, so they can properly terminate. + */ + cv_signal(&ic->ic_receive_cv); + cv_signal(&ic->ic_send_cv); + while (ic->ic_receive_running || ic->ic_send_running) { + //ICL_DEBUG("waiting for send/receive threads to terminate"); + ICL_CONN_UNLOCK(ic); + cv_signal(&ic->ic_receive_cv); + cv_signal(&ic->ic_send_cv); + pause("icl_close", 1 * hz); + ICL_CONN_LOCK(ic); + } + //ICL_DEBUG("send/receive threads terminated"); + + soclose(ic->ic_socket); + ic->ic_socket = NULL; + + if (ic->ic_receive_pdu != NULL) { + //ICL_DEBUG("freeing partially received PDU"); + icl_pdu_free(ic->ic_receive_pdu); + ic->ic_receive_pdu = NULL; + } + + /* + * Remove any outstanding PDUs from the send queue. + */ + while (!TAILQ_EMPTY(&ic->ic_to_send)) { + pdu = TAILQ_FIRST(&ic->ic_to_send); + TAILQ_REMOVE(&ic->ic_to_send, pdu, ip_next); + icl_pdu_free(pdu); + } + + KASSERT(TAILQ_EMPTY(&ic->ic_to_send), + ("destroying session with non-empty send queue")); + /* + * XXX + */ +#if 0 + KASSERT(ic->ic_outstanding_pdus == 0, + ("destroying session with %d outstanding PDUs", + ic->ic_outstanding_pdus)); +#endif + ICL_CONN_UNLOCK(ic); +} + +bool +icl_conn_connected(struct icl_conn *ic) +{ + + ICL_CONN_LOCK(ic); + if (ic->ic_socket == NULL) { + ICL_CONN_UNLOCK(ic); + return (false); + } + if (ic->ic_socket->so_error != 0) { + ICL_CONN_UNLOCK(ic); + return (false); + } + ICL_CONN_UNLOCK(ic); + return (true); +} + +#ifdef ICL_KERNEL_PROXY +int +icl_conn_handoff_sock(struct icl_conn *ic, struct socket *so) +{ + int error; + + if (so->so_type != SOCK_STREAM) + return (EINVAL); + + ICL_CONN_LOCK(ic); + if (ic->ic_socket != NULL) { + ICL_CONN_UNLOCK(ic); + return (EBUSY); + } + ic->ic_socket = so; + ICL_CONN_UNLOCK(ic); + + error = icl_conn_start(ic); + + return (error); +} +#endif /* ICL_KERNEL_PROXY */ + +static int +icl_unload(void) +{ + + if (icl_ncons != 0) + return (EBUSY); + + uma_zdestroy(icl_conn_zone); + uma_zdestroy(icl_pdu_zone); + + return (0); +} + +static void +icl_load(void) +{ + + icl_conn_zone = uma_zcreate("icl_conn", + sizeof(struct icl_conn), NULL, NULL, NULL, NULL, + UMA_ALIGN_PTR, UMA_ZONE_NOFREE); + icl_pdu_zone = uma_zcreate("icl_pdu", + sizeof(struct icl_pdu), NULL, NULL, NULL, NULL, + UMA_ALIGN_PTR, UMA_ZONE_NOFREE); + + refcount_init(&icl_ncons, 0); +} + +static int +icl_modevent(module_t mod, int what, void *arg) +{ + + switch (what) { + case MOD_LOAD: + icl_load(); + return (0); + case MOD_UNLOAD: + return (icl_unload()); + default: + return (EINVAL); + } +} + +moduledata_t icl_data = { + "icl", + icl_modevent, + 0 +}; + +DECLARE_MODULE(icl, icl_data, SI_SUB_DRIVERS, SI_ORDER_FIRST); +MODULE_VERSION(icl, 1); diff -urN p4/freebsd/src/sys/dev/iscsi/icl.h p4/iscsi/sys/dev/iscsi/icl.h --- p4/freebsd/src/sys/dev/iscsi/icl.h 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/sys/dev/iscsi/icl.h 2013-07-23 23:45:08.000000000 +0200 @@ -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); +int icl_pdu_data_segment_length(const struct icl_pdu *ip); +void icl_pdu_append_data(struct icl_pdu *ip, const void *addr, size_t len); +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 */ diff -urN p4/freebsd/src/sys/dev/iscsi/icl_proxy.c p4/iscsi/sys/dev/iscsi/icl_proxy.c --- p4/freebsd/src/sys/dev/iscsi/icl_proxy.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/sys/dev/iscsi/icl_proxy.c 2013-07-23 23:45:08.000000000 +0200 @@ -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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "icl.h" + +#ifdef ICL_KERNEL_PROXY + +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) + +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 */ diff -urN p4/freebsd/src/sys/dev/iscsi/initiator/isc_cam.c p4/iscsi/sys/dev/iscsi/initiator/isc_cam.c --- p4/freebsd/src/sys/dev/iscsi/initiator/isc_cam.c 2013-04-20 20:09:45.000000000 +0200 +++ p4/iscsi/sys/dev/iscsi/initiator/isc_cam.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,384 +0,0 @@ -/*- - * Copyright (c) 2005-2010 Daniel Braniss - * 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. - * - * 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. - * - */ -/* - | $Id: isc_cam.c 998 2009-12-20 10:32:45Z danny $ - */ -#include -__FBSDID("$FreeBSD: head/sys/dev/iscsi/initiator/isc_cam.c 249468 2013-04-14 09:55:48Z mav $"); - -#include "opt_iscsi_initiator.h" - -#include -#include -#include -#if __FreeBSD_version >= 700000 -#include -#include -#endif -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -static void -_inq(struct cam_sim *sim, union ccb *ccb) -{ - struct ccb_pathinq *cpi = &ccb->cpi; - isc_session_t *sp = cam_sim_softc(sim); - - debug_called(8); - debug(3, "sid=%d target=%d lun=%d", sp->sid, ccb->ccb_h.target_id, ccb->ccb_h.target_lun); - - cpi->version_num = 1; /* XXX??? */ - cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE | PI_WIDE_32; - cpi->target_sprt = 0; - cpi->hba_misc = 0; - cpi->hba_eng_cnt = 0; - cpi->max_target = 0; //ISCSI_MAX_TARGETS - 1; - cpi->initiator_id = ISCSI_MAX_TARGETS; - cpi->max_lun = sp->opt.maxluns - 1; - cpi->bus_id = cam_sim_bus(sim); - cpi->base_transfer_speed = 3300; // 40000; // XXX: - strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); - strncpy(cpi->hba_vid, "iSCSI", HBA_IDLEN); - strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); - cpi->unit_number = cam_sim_unit(sim); - cpi->ccb_h.status = CAM_REQ_CMP; -#if defined(KNOB_VALID_ADDRESS) - cpi->transport = XPORT_ISCSI; - cpi->transport_version = 0; -#endif -} - -static __inline int -_scsi_encap(struct cam_sim *sim, union ccb *ccb) -{ - int ret; - -#if __FreeBSD_version < 700000 - ret = scsi_encap(sim, ccb); -#else - isc_session_t *sp = cam_sim_softc(sim); - - mtx_unlock(&sp->cam_mtx); - ret = scsi_encap(sim, ccb); - mtx_lock(&sp->cam_mtx); -#endif - return ret; -} - -void -ic_lost_target(isc_session_t *sp, int target) -{ - debug_called(8); - sdebug(2, "lost target=%d", target); - - if(sp->cam_path != NULL) { - mtx_lock(&sp->cam_mtx); - xpt_async(AC_LOST_DEVICE, sp->cam_path, NULL); - xpt_free_path(sp->cam_path); - mtx_unlock(&sp->cam_mtx); - sp->cam_path = 0; // XXX - } -} - -static void -scan_callback(struct cam_periph *periph, union ccb *ccb) -{ - isc_session_t *sp = (isc_session_t *)ccb->ccb_h.spriv_ptr0; - - debug_called(8); - - free(ccb, M_TEMP); - - if(sp->flags & ISC_SCANWAIT) { - sp->flags &= ~ISC_SCANWAIT; - wakeup(sp); - } -} - -static int -ic_scan(isc_session_t *sp) -{ - union ccb *ccb; - - debug_called(8); - sdebug(2, "scanning sid=%d", sp->sid); - - if((ccb = malloc(sizeof(union ccb), M_TEMP, M_WAITOK | M_ZERO)) == NULL) { - xdebug("scan failed (can't allocate CCB)"); - return ENOMEM; // XXX - } - - sp->flags &= ~ISC_CAMDEVS; - sp->flags |= ISC_SCANWAIT; - - CAM_LOCK(sp); - if(xpt_create_path(&sp->cam_path, NULL, cam_sim_path(sp->cam_sim), - 0, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { - xdebug("can't create cam path"); - CAM_UNLOCK(sp); - free(ccb, M_TEMP); - return ENODEV; // XXX - } - xpt_setup_ccb(&ccb->ccb_h, sp->cam_path, 5/*priority (low)*/); - ccb->ccb_h.func_code = XPT_SCAN_BUS; - ccb->ccb_h.cbfcnp = scan_callback; - ccb->crcn.flags = CAM_FLAG_NONE; - ccb->ccb_h.spriv_ptr0 = sp; - - xpt_action(ccb); - CAM_UNLOCK(sp); - - while(sp->flags & ISC_SCANWAIT) - tsleep(sp, PRIBIO, "ffp", 5*hz); // the timeout time should - // be configurable - sdebug(2, "# of luns=%d", sp->target_nluns); - - if(sp->target_nluns > 0) { - sp->flags |= ISC_CAMDEVS; - return 0; - } - - return ENODEV; -} - -static void -ic_action(struct cam_sim *sim, union ccb *ccb) -{ - isc_session_t *sp = cam_sim_softc(sim); - struct ccb_hdr *ccb_h = &ccb->ccb_h; - - debug_called(8); - - ccb_h->spriv_ptr0 = sp; - sdebug(4, "func_code=0x%x flags=0x%x status=0x%x target=%d lun=%d retry_count=%d timeout=%d", - ccb_h->func_code, ccb->ccb_h.flags, ccb->ccb_h.status, - ccb->ccb_h.target_id, ccb->ccb_h.target_lun, - ccb->ccb_h.retry_count, ccb_h->timeout); - if(sp == NULL) { - xdebug("sp == NULL! cannot happen"); - return; - } - switch(ccb_h->func_code) { - case XPT_PATH_INQ: - _inq(sim, ccb); - break; - - case XPT_RESET_BUS: // (can just be a stub that does nothing and completes) - { - struct ccb_pathinq *cpi = &ccb->cpi; - - debug(3, "XPT_RESET_BUS"); - cpi->ccb_h.status = CAM_REQ_CMP; - break; - } - - case XPT_SCSI_IO: - { - struct ccb_scsiio* csio = &ccb->csio; - - debug(4, "XPT_SCSI_IO cmd=0x%x", csio->cdb_io.cdb_bytes[0]); - if(sp == NULL) { - ccb_h->status = CAM_REQ_INVALID; //CAM_NO_NEXUS; - debug(4, "xpt_done.status=%d", ccb_h->status); - break; - } - if(ccb_h->target_lun == CAM_LUN_WILDCARD) { - debug(3, "target=%d: bad lun (-1)", ccb_h->target_id); - ccb_h->status = CAM_LUN_INVALID; - break; - } - if(_scsi_encap(sim, ccb) != 0) - return; - break; - } - - case XPT_CALC_GEOMETRY: - { - struct ccb_calc_geometry *ccg; - - ccg = &ccb->ccg; - debug(4, "sid=%d target=%d lun=%d XPT_CALC_GEOMETRY vsize=%jd bsize=%d", - sp->sid, ccb->ccb_h.target_id, ccb->ccb_h.target_lun, - ccg->volume_size, ccg->block_size); - if(ccg->block_size == 0 || - (ccg->volume_size < ccg->block_size)) { - // print error message ... - /* XXX: what error is appropiate? */ - break; - } - else { - int lun, *off, boff; - - lun = ccb->ccb_h.target_lun; - if(lun > ISCSI_MAX_LUNS) { - // XXX: - xdebug("lun %d > ISCSI_MAX_LUNS!\n", lun); - lun %= ISCSI_MAX_LUNS; - } - off = &sp->target_lun[lun / (sizeof(int)*8)]; - boff = BIT(lun % (sizeof(int)*8)); - debug(4, "sp->target_nluns=%d *off=%x boff=%x", - sp->target_nluns, *off, boff); - - if((*off & boff) == 0) { - sp->target_nluns++; - *off |= boff; - } - cam_calc_geometry(ccg, /*extended*/1); - } - break; - } - - case XPT_GET_TRAN_SETTINGS: - default: - ccb_h->status = CAM_REQ_INVALID; - break; - } -#if __FreeBSD_version < 700000 - XPT_DONE(sp, ccb); -#else - xpt_done(ccb); -#endif - return; -} - -static void -ic_poll(struct cam_sim *sim) -{ - debug_called(4); - -} - -int -ic_getCamVals(isc_session_t *sp, iscsi_cam_t *cp) -{ - debug_called(8); - - if(sp && sp->cam_sim) { - cp->path_id = cam_sim_path(sp->cam_sim); - cp->target_id = 0; - cp->target_nluns = ISCSI_MAX_LUNS; // XXX: -1? - return 0; - } - return ENXIO; -} - -void -ic_destroy(isc_session_t *sp ) -{ - debug_called(8); - - if(sp->cam_path != NULL) { - sdebug(2, "name=%s unit=%d", - cam_sim_name(sp->cam_sim), cam_sim_unit(sp->cam_sim)); - CAM_LOCK(sp); -#if 0 - xpt_async(AC_LOST_DEVICE, sp->cam_path, NULL); -#else - xpt_async(XPT_RESET_BUS, sp->cam_path, NULL); -#endif - xpt_free_path(sp->cam_path); - xpt_bus_deregister(cam_sim_path(sp->cam_sim)); - cam_sim_free(sp->cam_sim, TRUE /*free_devq*/); - - CAM_UNLOCK(sp); - sdebug(2, "done"); - } -} - -int -ic_init(isc_session_t *sp) -{ - struct cam_sim *sim; - struct cam_devq *devq; - - debug_called(8); - - if((devq = cam_simq_alloc(256)) == NULL) - return ENOMEM; - -#if __FreeBSD_version >= 700000 - mtx_init(&sp->cam_mtx, "isc-cam", NULL, MTX_DEF); -#else - isp->cam_mtx = Giant; -#endif - sim = cam_sim_alloc(ic_action, - ic_poll, - "iscsi", - sp, - sp->sid, // unit -#if __FreeBSD_version >= 700000 - &sp->cam_mtx, -#endif - 1, // max_dev_transactions - 0, // max_tagged_dev_transactions - devq); - if(sim == NULL) { - cam_simq_free(devq); -#if __FreeBSD_version >= 700000 - mtx_destroy(&sp->cam_mtx); -#endif - return ENXIO; - } - - CAM_LOCK(sp); - if(xpt_bus_register(sim, -#if __FreeBSD_version >= 700000 - NULL, -#endif - 0/*bus_number*/) != CAM_SUCCESS) { - - cam_sim_free(sim, /*free_devq*/TRUE); - CAM_UNLOCK(sp); -#if __FreeBSD_version >= 700000 - mtx_destroy(&sp->cam_mtx); -#endif - return ENXIO; - } - sp->cam_sim = sim; - CAM_UNLOCK(sp); - - sdebug(1, "cam subsystem initialized"); - - ic_scan(sp); - - return 0; -} diff -urN p4/freebsd/src/sys/dev/iscsi/initiator/isc_sm.c p4/iscsi/sys/dev/iscsi/initiator/isc_sm.c --- p4/freebsd/src/sys/dev/iscsi/initiator/isc_sm.c 2013-04-20 20:09:46.000000000 +0200 +++ p4/iscsi/sys/dev/iscsi/initiator/isc_sm.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,786 +0,0 @@ -/*- - * Copyright (c) 2005-2010 Daniel Braniss - * 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. - * - * 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. - * - */ -/* - | iSCSI - Session Manager - | $Id: isc_sm.c 743 2009-08-08 10:54:53Z danny $ - */ - -#include -__FBSDID("$FreeBSD: head/sys/dev/iscsi/initiator/isc_sm.c 231378 2012-02-10 12:35:57Z ed $"); - -#include "opt_iscsi_initiator.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -static void -_async(isc_session_t *sp, pduq_t *pq) -{ - debug_called(8); - - iscsi_async(sp, pq); - - pdu_free(sp->isc, pq); -} - -static void -_reject(isc_session_t *sp, pduq_t *pq) -{ - pduq_t *opq; - pdu_t *pdu; - reject_t *reject; - int itt; - - debug_called(8); - pdu = mtod(pq->mp, pdu_t *); - itt = pdu->ipdu.bhs.itt; - reject = &pq->pdu.ipdu.reject; - sdebug(2, "itt=%x reason=0x%x", ntohl(itt), reject->reason); - opq = i_search_hld(sp, itt, 0); - if(opq != NULL) - iscsi_reject(sp, opq, pq); - else { - switch(pq->pdu.ipdu.bhs.opcode) { - case ISCSI_LOGOUT_CMD: // XXX: wasabi does this - can't figure out why - sdebug(2, "ISCSI_LOGOUT_CMD ..."); - break; - default: - xdebug("%d] we lost something itt=%x", - sp->sid, ntohl(pq->pdu.ipdu.bhs.itt)); - } - } - pdu_free(sp->isc, pq); -} - -static void -_r2t(isc_session_t *sp, pduq_t *pq) -{ - pduq_t *opq; - - debug_called(8); - opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1); - if(opq != NULL) { - iscsi_r2t(sp, opq, pq); - } - else { - r2t_t *r2t = &pq->pdu.ipdu.r2t; - - xdebug("%d] we lost something itt=%x r2tSN=%d bo=%x ddtl=%x", - sp->sid, ntohl(pq->pdu.ipdu.bhs.itt), - ntohl(r2t->r2tSN), ntohl(r2t->bo), ntohl(r2t->ddtl)); - } - pdu_free(sp->isc, pq); -} - -static void -_scsi_rsp(isc_session_t *sp, pduq_t *pq) -{ - pduq_t *opq; - - debug_called(8); - opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 0); - debug(5, "itt=%x pq=%p opq=%p", ntohl(pq->pdu.ipdu.bhs.itt), pq, opq); - if(opq != NULL) { - iscsi_done(sp, opq, pq); - i_acked_hld(sp, &pq->pdu); - } - else - xdebug("%d] we lost something itt=%x", - sp->sid, ntohl(pq->pdu.ipdu.bhs.itt)); - pdu_free(sp->isc, pq); -} - -static void -_read_data(isc_session_t *sp, pduq_t *pq) -{ - pduq_t *opq; - - debug_called(8); - opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1); - if(opq != NULL) { - if(scsi_decap(sp, opq, pq) != 1) { - i_remove_hld(sp, opq); // done - pdu_free(sp->isc, opq); - } - } - else - xdebug("%d] we lost something itt=%x", - sp->sid, ntohl(pq->pdu.ipdu.bhs.itt)); - pdu_free(sp->isc, pq); -} -/* - | this is a kludge, - | the jury is not back with a veredict, user or kernel - */ -static void -_nop_out(isc_session_t *sp) -{ - pduq_t *pq; - nop_out_t *nop_out; - - debug_called(8); - - sdebug(4, "cws=%d", sp->cws); - if(sp->cws == 0) { - /* - | only send a nop if window is closed. - */ - if((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL) - // I guess we ran out of resources - return; - nop_out = &pq->pdu.ipdu.nop_out; - nop_out->opcode = ISCSI_NOP_OUT; - nop_out->itt = htonl(sp->sn.itt); - nop_out->ttt = -1; - nop_out->I = 1; - nop_out->F = 1; - if(isc_qout(sp, pq) != 0) { - sdebug(1, "failed"); - pdu_free(sp->isc, pq); - } - } -} - -static void -_nop_in(isc_session_t *sp, pduq_t *pq) -{ - pdu_t *pp = &pq->pdu; - nop_in_t *nop_in = &pp->ipdu.nop_in; - bhs_t *bhs = &pp->ipdu.bhs; - - debug_called(8); - - sdebug(5, "itt=%x ttt=%x", htonl(nop_in->itt), htonl(nop_in->ttt)); - if(nop_in->itt == -1) { - if(pp->ds_len != 0) { - /* - | according to RFC 3720 this should be zero - | what to do if not? - */ - xdebug("%d] dslen not zero", sp->sid); - } - if(nop_in->ttt != -1) { - nop_out_t *nop_out; - /* - | target wants a nop_out - */ - bhs->opcode = ISCSI_NOP_OUT; - bhs->I = 1; - bhs->F = 1; - /* - | we are reusing the pdu, so bhs->ttt == nop_in->ttt; - | and need to zero out 'Reserved' - | small cludge here. - */ - nop_out = &pp->ipdu.nop_out; - nop_out->sn.maxcmd = 0; - memset(nop_out->mbz, 0, sizeof(nop_out->mbz)); - (void)isc_qout(sp, pq); //XXX: should check return? - return; - } - //else { - // just making noise? - // see 10.9.1: target does not want and answer. - //} - - } else - if(nop_in->ttt == -1) { - /* - | it is an answer to a nop_in from us - */ - if(nop_in->itt != -1) { -#ifdef ISC_WAIT4PING - // XXX: MUTEX please - if(sp->flags & ISC_WAIT4PING) { - i_nqueue_rsp(sp, pq); - wakeup(&sp->rsp); - return; - } -#endif - } - } - /* - | drop it - */ - pdu_free(sp->isc, pq); - return; -} - -int -i_prepPDU(isc_session_t *sp, pduq_t *pq) -{ - size_t len, n; - pdu_t *pp = &pq->pdu; - bhs_t *bhp = &pp->ipdu.bhs; - - len = sizeof(bhs_t); - if(pp->ahs_len) { - len += pp->ahs_len; - bhp->AHSLength = pp->ahs_len / 4; - } - if(ISOK2DIG(sp->hdrDigest, pp)) - len += 4; - if(pp->ds_len) { - n = pp->ds_len; - len += n; -#if BYTE_ORDER == LITTLE_ENDIAN - bhp->DSLength = ((n & 0x00ff0000) >> 16) - | (n & 0x0000ff00) - | ((n & 0x000000ff) << 16); -#else - bhp->DSLength = n; -#endif - if(len & 03) { - n = 4 - (len & 03); - len += n; - } - if(ISOK2DIG(sp->dataDigest, pp)) - len += 4; - } - - pq->len = len; - len -= sizeof(bhs_t); - if(sp->opt.maxBurstLength && (len > sp->opt.maxBurstLength)) { - xdebug("%d] pdu len=%zd > %d", - sp->sid, len, sp->opt.maxBurstLength); - // XXX: when this happens it used to hang ... - return E2BIG; - } - return 0; -} - -int -isc_qout(isc_session_t *sp, pduq_t *pq) -{ - int error = 0; - - debug_called(8); - - if(pq->len == 0 && (error = i_prepPDU(sp, pq))) - return error; - - if(pq->pdu.ipdu.bhs.I) - i_nqueue_isnd(sp, pq); - else - if(pq->pdu.ipdu.data_out.opcode == ISCSI_WRITE_DATA) - i_nqueue_wsnd(sp, pq); - else - i_nqueue_csnd(sp, pq); - - sdebug(5, "enqued: pq=%p", pq); - - mtx_lock(&sp->io_mtx); - sp->flags |= ISC_OQNOTEMPTY; - if(sp->flags & ISC_OWAITING) - wakeup(&sp->flags); - mtx_unlock(&sp->io_mtx); - - return error; -} -/* - | called when a fullPhase is restarted - */ -void -ism_restart(isc_session_t *sp) -{ - int lastcmd; - - sdebug(2, "restart ..."); - lastcmd = iscsi_requeue(sp); -#if 0 - if(lastcmd != sp->sn.cmd) { - sdebug(1, "resetting CmdSN to=%d (from %d)", lastcmd, sp->sn.cmd); - sp->sn.cmd = lastcmd; - } -#endif - mtx_lock(&sp->io_mtx); - if(sp->flags & ISC_OWAITING) { - wakeup(&sp->flags); - } - mtx_unlock(&sp->io_mtx); - - sdebug(2, "restarted sn.cmd=0x%x lastcmd=0x%x", sp->sn.cmd, lastcmd); -} - -void -ism_recv(isc_session_t *sp, pduq_t *pq) -{ - bhs_t *bhs; - int statSN; - - debug_called(8); - - bhs = &pq->pdu.ipdu.bhs; - statSN = ntohl(bhs->OpcodeSpecificFields[1]); - -#ifdef notyet - if(sp->sn.expCmd != sn->cmd) { - sdebug(1, "we lost something ... exp=0x%x cmd=0x%x", - sn->expCmd, sn->cmd); - } -#endif - sdebug(5, "opcode=0x%x itt=0x%x stat#0x%x maxcmd=0x%0x", - bhs->opcode, ntohl(bhs->itt), statSN, sp->sn.maxCmd); - - switch(bhs->opcode) { - case ISCSI_READ_DATA: { - data_in_t *cmd = &pq->pdu.ipdu.data_in; - - if(cmd->S == 0) - break; - } - - default: - if(statSN > (sp->sn.stat + 1)) { - sdebug(1, "we lost some rec=0x%x exp=0x%x", - statSN, sp->sn.stat); - // XXX: must do some error recovery here. - } - sp->sn.stat = statSN; - } - - switch(bhs->opcode) { - case ISCSI_LOGIN_RSP: - case ISCSI_TEXT_RSP: - case ISCSI_LOGOUT_RSP: - i_nqueue_rsp(sp, pq); - wakeup(&sp->rsp); - sdebug(3, "wakeup rsp"); - break; - - case ISCSI_NOP_IN: _nop_in(sp, pq); break; - case ISCSI_SCSI_RSP: _scsi_rsp(sp, pq); break; - case ISCSI_READ_DATA: _read_data(sp, pq); break; - case ISCSI_R2T: _r2t(sp, pq); break; - case ISCSI_REJECT: _reject(sp, pq); break; - case ISCSI_ASYNC: _async(sp, pq); break; - - case ISCSI_TASK_RSP: - default: - sdebug(1, "opcode=0x%x itt=0x%x not implemented yet", - bhs->opcode, ntohl(bhs->itt)); - break; - } -} - -/* - | go through the out queues looking for work - | if either nothing to do, or window is closed - | return. - */ -static int -proc_out(isc_session_t *sp) -{ - sn_t *sn = &sp->sn; - pduq_t *pq; - int error, which; - - debug_called(8); - error = 0; - - while(sp->flags & ISC_LINK_UP) { - pdu_t *pp; - bhs_t *bhs; - /* - | check if there is outstanding work in: - | 1- the Immediate queue - | 2- the R2T queue - | 3- the cmd queue, only if the command window allows it. - */ - which = BIT(0) | BIT(1); - if(SNA_GT(sn->cmd, sn->maxCmd) == 0) // if(sn->maxCmd - sn->smc + 1) > 0 - which |= BIT(2); - - sdebug(4, "which=%d sn->maxCmd=%d sn->cmd=%d", which, sn->maxCmd, sn->cmd); - - if((pq = i_dqueue_snd(sp, which)) == NULL) - break; - sdebug(4, "pq=%p", pq); - - pp = &pq->pdu; - bhs = &pp->ipdu.bhs; - switch(bhs->opcode) { - case ISCSI_SCSI_CMD: - sn->itt++; - bhs->itt = htonl(sn->itt); - - case ISCSI_LOGIN_CMD: - case ISCSI_TEXT_CMD: - case ISCSI_LOGOUT_CMD: - case ISCSI_SNACK: - case ISCSI_NOP_OUT: - case ISCSI_TASK_CMD: - bhs->CmdSN = htonl(sn->cmd); - if(bhs->I == 0) - sn->cmd++; - - case ISCSI_WRITE_DATA: - bhs->ExpStSN = htonl(sn->stat + 1); - break; - - default: - // XXX: can this happen? - xdebug("bad opcode=0x%x sn(cmd=0x%x expCmd=0x%x maxCmd=0x%x expStat=0x%x itt=0x%x)", - bhs->opcode, - sn->cmd, sn->expCmd, sn->maxCmd, sn->expStat, sn->itt); - // XXX: and now? - } - - sdebug(4, "opcode=0x%x sn(cmd=0x%x expCmd=0x%x maxCmd=0x%x expStat=0x%x itt=0x%x)", - bhs->opcode, - sn->cmd, sn->expCmd, sn->maxCmd, sn->expStat, sn->itt); - - if(bhs->opcode != ISCSI_NOP_OUT) - /* - | enqued till ack is received - | note: sosend(...) does not mean the packet left - | the host so that freeing resources has to wait - */ - i_nqueue_hld(sp, pq); - - error = isc_sendPDU(sp, pq); - if(bhs->opcode == ISCSI_NOP_OUT) - pdu_free(sp->isc, pq); - if(error) { - xdebug("error=%d opcode=0x%x ccb=%p itt=%x", - error, bhs->opcode, pq->ccb, ntohl(bhs->itt)); - i_remove_hld(sp, pq); - switch(error) { - case EPIPE: - sp->flags &= ~ISC_LINK_UP; - - case EAGAIN: - xdebug("requed"); - i_rqueue_pdu(sp, pq); - break; - - default: - if(pq->ccb) { - xdebug("back to cam"); - pq->ccb->ccb_h.status |= CAM_REQUEUE_REQ; // some better error? - XPT_DONE(sp, pq->ccb); - pdu_free(sp->isc, pq); - } - else - xdebug("we lost it!"); - } - } - } - return error; -} - -/* - | survives link breakdowns. - */ -static void -ism_out(void *vp) -{ - isc_session_t *sp = (isc_session_t *)vp; - int error; - - debug_called(8); - - sp->flags |= ISC_SM_RUNNING; - sdebug(3, "started sp->flags=%x", sp->flags); - do { - if((sp->flags & ISC_HOLD) == 0) { - error = proc_out(sp); - if(error) { - sdebug(3, "error=%d", error); - } - } - mtx_lock(&sp->io_mtx); - if((sp->flags & ISC_LINK_UP) == 0) { - sdebug(3, "ISC_LINK_UP==0, sp->flags=%x ", sp->flags); - if(sp->soc != NULL) - sdebug(3, "so_state=%x", sp->soc->so_state); - wakeup(&sp->soc); - } - - if(!(sp->flags & ISC_OQNOTEMPTY)) { - sp->flags |= ISC_OWAITING; - if(msleep(&sp->flags, &sp->io_mtx, PRIBIO, "isc_proc", hz*30) == EWOULDBLOCK) { - if(sp->flags & ISC_CON_RUNNING) - _nop_out(sp); - } - sp->flags &= ~ISC_OWAITING; - } - sp->flags &= ~ISC_OQNOTEMPTY; - mtx_unlock(&sp->io_mtx); - } while(sp->flags & ISC_SM_RUN); - - sp->flags &= ~ISC_SM_RUNNING; - sdebug(3, "dropped ISC_SM_RUNNING"); - - wakeup(&sp->soc); - wakeup(sp); // XXX: do we need this one? - -#if __FreeBSD_version >= 700000 - destroy_dev(sp->dev); -#endif - - debug(3, "terminated sp=%p sp->sid=%d", sp, sp->sid); - -#if __FreeBSD_version >= 800000 - kproc_exit(0); -#else - kthread_exit(0); -#endif -} - -#if 0 -static int -isc_dump_options(SYSCTL_HANDLER_ARGS) -{ - int error; - isc_session_t *sp; - char buf[1024], *bp; - - sp = (isc_session_t *)arg1; - bp = buf; - sprintf(bp, "targetname='%s'", sp->opt.targetName); - bp += strlen(bp); - sprintf(bp, " targetname='%s'", sp->opt.targetAddress); - error = SYSCTL_OUT(req, buf, strlen(buf)); - return error; -} -#endif - -static int -isc_dump_stats(SYSCTL_HANDLER_ARGS) -{ - isc_session_t *sp; - struct isc_softc *sc; - char buf[1024], *bp; - int error, n; - - sp = (isc_session_t *)arg1; - sc = sp->isc; - - bp = buf; - n = sizeof(buf); - snprintf(bp, n, "recv=%d sent=%d", sp->stats.nrecv, sp->stats.nsent); - bp += strlen(bp); - n -= strlen(bp); - snprintf(bp, n, " flags=0x%08x pdus-alloc=%d pdus-max=%d", - sp->flags, sc->npdu_alloc, sc->npdu_max); - bp += strlen(bp); - n -= strlen(bp); - snprintf(bp, n, " cws=%d cmd=%x exp=%x max=%x stat=%x itt=%x", - sp->cws, sp->sn.cmd, sp->sn.expCmd, sp->sn.maxCmd, sp->sn.stat, sp->sn.itt); - error = SYSCTL_OUT(req, buf, strlen(buf)); - return error; -} - -static int -isc_sysctl_targetName(SYSCTL_HANDLER_ARGS) -{ - char buf[128], **cp; - int error; - - cp = (char **)arg1; - snprintf(buf, sizeof(buf), "%s", *cp); - error = SYSCTL_OUT(req, buf, strlen(buf)); - return error; -} - -static int -isc_sysctl_targetAddress(SYSCTL_HANDLER_ARGS) -{ - char buf[128], **cp; - int error; - - cp = (char **)arg1; - snprintf(buf, sizeof(buf), "%s", *cp); - error = SYSCTL_OUT(req, buf, strlen(buf)); - return error; -} - -static void -isc_add_sysctls(isc_session_t *sp) -{ - debug_called(8); - sdebug(6, "sid=%d %s", sp->sid, devtoname(sp->dev)); - - sysctl_ctx_init(&sp->clist); - sp->oid = SYSCTL_ADD_NODE(&sp->clist, - SYSCTL_CHILDREN(sp->isc->oid), - OID_AUTO, - devtoname(sp->dev) + 5, // iscsi0 - CTLFLAG_RD, - 0, - "initiator"); - SYSCTL_ADD_PROC(&sp->clist, - SYSCTL_CHILDREN(sp->oid), - OID_AUTO, - "targetname", - CTLTYPE_STRING | CTLFLAG_RD, - (void *)&sp->opt.targetName, 0, - isc_sysctl_targetName, "A", "target name"); - - SYSCTL_ADD_PROC(&sp->clist, - SYSCTL_CHILDREN(sp->oid), - OID_AUTO, - "targeaddress", - CTLTYPE_STRING | CTLFLAG_RD, - (void *)&sp->opt.targetAddress, 0, - isc_sysctl_targetAddress, "A", "target address"); - - SYSCTL_ADD_PROC(&sp->clist, - SYSCTL_CHILDREN(sp->oid), - OID_AUTO, - "stats", - CTLTYPE_STRING | CTLFLAG_RD, - (void *)sp, 0, - isc_dump_stats, "A", "statistics"); - - SYSCTL_ADD_INT(&sp->clist, - SYSCTL_CHILDREN(sp->oid), - OID_AUTO, - "douio", - CTLFLAG_RW, - &sp->douio, 0, "enable uio on read"); -} - -void -ism_stop(isc_session_t *sp) -{ - struct isc_softc *sc = sp->isc; - int n; - - debug_called(8); - sdebug(2, "terminating"); - /* - | first stop the receiver - */ - isc_stop_receiver(sp); - /* - | now stop the xmitter - */ - n = 5; - sp->flags &= ~ISC_SM_RUN; - while(n-- && (sp->flags & ISC_SM_RUNNING)) { - sdebug(2, "n=%d", n); - wakeup(&sp->flags); - tsleep(sp, PRIBIO, "-", 5*hz); - } - sdebug(2, "final n=%d", n); - sp->flags &= ~ISC_FFPHASE; - - iscsi_cleanup(sp); - - (void)i_pdu_flush(sp); - - ic_destroy(sp); - - sx_xlock(&sc->unit_sx); - free_unr(sc->unit, sp->sid); - sx_xunlock(&sc->unit_sx); - - mtx_lock(&sc->isc_mtx); - TAILQ_REMOVE(&sc->isc_sess, sp, sp_link); - sc->nsess--; - mtx_unlock(&sc->isc_mtx); - -#if __FreeBSD_version < 700000 - destroy_dev(sp->dev); -#endif - - mtx_destroy(&sp->rsp_mtx); - mtx_destroy(&sp->rsv_mtx); - mtx_destroy(&sp->hld_mtx); - mtx_destroy(&sp->snd_mtx); - mtx_destroy(&sp->io_mtx); - - i_freeopt(&sp->opt); - - if(sysctl_ctx_free(&sp->clist)) - xdebug("sysctl_ctx_free failed"); - - free(sp, M_ISCSI); -} - -int -ism_start(isc_session_t *sp) -{ - debug_called(8); - /* - | now is a good time to do some initialization - */ - TAILQ_INIT(&sp->rsp); - TAILQ_INIT(&sp->rsv); - TAILQ_INIT(&sp->csnd); - TAILQ_INIT(&sp->isnd); - TAILQ_INIT(&sp->wsnd); - TAILQ_INIT(&sp->hld); - - mtx_init(&sp->rsv_mtx, "iscsi-rsv", NULL, MTX_DEF); - mtx_init(&sp->rsp_mtx, "iscsi-rsp", NULL, MTX_DEF); - mtx_init(&sp->snd_mtx, "iscsi-snd", NULL, MTX_DEF); - mtx_init(&sp->hld_mtx, "iscsi-hld", NULL, MTX_DEF); - mtx_init(&sp->io_mtx, "iscsi-io", NULL, MTX_DEF); - - isc_add_sysctls(sp); - - sp->flags |= ISC_SM_RUN; - - debug(4, "starting ism_proc: sp->sid=%d", sp->sid); - -#if __FreeBSD_version >= 800000 - return kproc_create(ism_out, sp, &sp->stp, 0, 0, "isc_out %d", sp->sid); -#else - return kthread_create(ism_out, sp, &sp->stp, 0, 0, "isc_out %d", sp->sid); -#endif -} diff -urN p4/freebsd/src/sys/dev/iscsi/initiator/isc_soc.c p4/iscsi/sys/dev/iscsi/initiator/isc_soc.c --- p4/freebsd/src/sys/dev/iscsi/initiator/isc_soc.c 2013-04-20 20:09:46.000000000 +0200 +++ p4/iscsi/sys/dev/iscsi/initiator/isc_soc.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,701 +0,0 @@ -/*- - * Copyright (c) 2005-2010 Daniel Braniss - * 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. - * - * 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. - * - */ -/* - | $Id: isc_soc.c 998 2009-12-20 10:32:45Z danny $ - */ -#include -__FBSDID("$FreeBSD: head/sys/dev/iscsi/initiator/isc_soc.c 248188 2013-03-12 08:59:51Z glebius $"); - -#include "opt_iscsi_initiator.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#ifndef NO_USE_MBUF -#define USE_MBUF -#endif - -#ifdef USE_MBUF -static int ou_refcnt = 0; -/* - | function for freeing external storage for mbuf - */ -static void -ext_free(void *a, void *b) -{ - pduq_t *pq = b; - - if(pq->buf != NULL) { - debug(3, "ou_refcnt=%d a=%p b=%p", ou_refcnt, a, pq->buf); - free(pq->buf, M_ISCSIBUF); - pq->buf = NULL; - } -} - -int -isc_sendPDU(isc_session_t *sp, pduq_t *pq) -{ - struct mbuf *mh, **mp; - pdu_t *pp = &pq->pdu; - int len, error; - - debug_called(8); - /* - | mbuf for the iSCSI header - */ - MGETHDR(mh, M_WAITOK, MT_DATA); - mh->m_pkthdr.rcvif = NULL; - mh->m_next = NULL; - mh->m_len = sizeof(union ipdu_u); - - if(ISOK2DIG(sp->hdrDigest, pp)) { - pp->hdr_dig = sp->hdrDigest(&pp->ipdu, sizeof(union ipdu_u), 0); - mh->m_len += sizeof(pp->hdr_dig); - if(pp->ahs_len) { - debug(2, "ahs_len=%d", pp->ahs_len); - pp->hdr_dig = sp->hdrDigest(&pp->ahs_addr, pp->ahs_len, pp->hdr_dig); - } - debug(3, "pp->hdr_dig=%04x", htonl(pp->hdr_dig)); - } - if(pp->ahs_len) { - /* - | Add any AHS to the iSCSI hdr mbuf - */ - if((mh->m_len + pp->ahs_len) < MHLEN) { - MH_ALIGN(mh, mh->m_len + pp->ahs_len); - bcopy(&pp->ipdu, mh->m_data, mh->m_len); - bcopy(pp->ahs_addr, mh->m_data + mh->m_len, pp->ahs_len); - mh->m_len += pp->ahs_len; - } - else - panic("len AHS=%d too big, not impleneted yet", pp->ahs_len); - } - else { - MH_ALIGN(mh, mh->m_len); - bcopy(&pp->ipdu, mh->m_data, mh->m_len); - } - mh->m_pkthdr.len = mh->m_len; - mp = &mh->m_next; - if(pp->ds_len && pq->pdu.ds_addr) { - struct mbuf *md; - int off = 0; - - len = pp->ds_len; - while(len > 0) { - int l; - - MGET(md, M_WAITOK, MT_DATA); - md->m_ext.ref_cnt = &ou_refcnt; - l = min(MCLBYTES, len); - debug(4, "setting ext_free(arg=%p len/l=%d/%d)", pq->buf, len, l); - MEXTADD(md, pp->ds_addr + off, l, ext_free, -#if __FreeBSD_version >= 800000 - pp->ds_addr + off, -#endif - pq, 0, EXT_EXTREF); - md->m_len = l; - md->m_next = NULL; - mh->m_pkthdr.len += l; - *mp = md; - mp = &md->m_next; - len -= l; - off += l; - } - if(((pp->ds_len & 03) != 0) || ISOK2DIG(sp->dataDigest, pp)) { - MGET(md, M_WAITOK, MT_DATA); - if(pp->ds_len & 03) - len = 4 - (pp->ds_len & 03); - else - len = 0; - md->m_len = len; - if(ISOK2DIG(sp->dataDigest, pp)) - md->m_len += sizeof(pp->ds_dig); - M_ALIGN(md, md->m_len); - if(ISOK2DIG(sp->dataDigest, pp)) { - pp->ds_dig = sp->dataDigest(pp->ds_addr, pp->ds_len, 0); - if(len) { - bzero(md->m_data, len); // RFC says SHOULD be 0 - pp->ds_dig = sp->dataDigest(md->m_data, len, pp->ds_dig); - } - bcopy(&pp->ds_dig, md->m_data+len, sizeof(pp->ds_dig)); - } - md->m_next = NULL; - mh->m_pkthdr.len += md->m_len; - *mp = md; - } - } - if((error = sosend(sp->soc, NULL, NULL, mh, 0, 0, sp->td)) != 0) { - sdebug(2, "error=%d", error); - return error; - } - sp->stats.nsent++; - getbintime(&sp->stats.t_sent); - return 0; -} -#else /* NO_USE_MBUF */ -int -isc_sendPDU(isc_session_t *sp, pduq_t *pq) -{ - struct uio *uio = &pq->uio; - struct iovec *iv; - pdu_t *pp = &pq->pdu; - int len, error; - - debug_called(8); - - bzero(uio, sizeof(struct uio)); - uio->uio_rw = UIO_WRITE; - uio->uio_segflg = UIO_SYSSPACE; - uio->uio_td = sp->td; - uio->uio_iov = iv = pq->iov; - - iv->iov_base = &pp->ipdu; - iv->iov_len = sizeof(union ipdu_u); - uio->uio_resid = iv->iov_len; - iv++; - if(ISOK2DIG(sp->hdrDigest, pp)) - pq->pdu.hdr_dig = sp->hdrDigest(&pp->ipdu, sizeof(union ipdu_u), 0); - if(pp->ahs_len) { - iv->iov_base = pp->ahs_addr; - iv->iov_len = pp->ahs_len; - uio->uio_resid += iv->iov_len; - iv++; - if(ISOK2DIG(sp->hdrDigest, pp)) - pp->hdr_dig = sp->hdrDigest(&pp->ahs_addr, pp->ahs_len, pp->hdr_dig); - } - if(ISOK2DIG(sp->hdrDigest, pp)) { - debug(3, "hdr_dig=%04x", htonl(pp->hdr_dig)); - iv->iov_base = &pp->hdr_dig; - iv->iov_len = sizeof(int); - uio->uio_resid += iv->iov_len ; - iv++; - } - if(pq->pdu.ds_addr && pp->ds_len) { - iv->iov_base = pp->ds_addr; - iv->iov_len = pp->ds_len; - while(iv->iov_len & 03) // the specs say it must be int alligned - iv->iov_len++; - uio->uio_resid += iv->iov_len ; - iv++; - if(ISOK2DIG(sp->dataDigest, pp)) { - pp->ds_dig = sp->dataDigest(pp->ds, pp->ds_len, 0); - iv->iov_base = &pp->ds_dig; - iv->iov_len = sizeof(pp->ds_dig); - uio->uio_resid += iv->iov_len ; - iv++; - } - } - uio->uio_iovcnt = iv - pq->iov; - sdebug(4, "pq->len=%d uio->uio_resid=%d uio->uio_iovcnt=%d", pq->len, - uio->uio_resid, - uio->uio_iovcnt); - - sdebug(4, "opcode=%x iovcnt=%d uio_resid=%d itt=%x", - pp->ipdu.bhs.opcode, uio->uio_iovcnt, uio->uio_resid, - ntohl(pp->ipdu.bhs.itt)); - sdebug(5, "sp=%p sp->soc=%p uio=%p sp->td=%p", - sp, sp->soc, uio, sp->td); - do { - len = uio->uio_resid; - error = sosend(sp->soc, NULL, uio, 0, 0, 0, sp->td); - if(uio->uio_resid == 0 || error || len == uio->uio_resid) { - if(uio->uio_resid) { - sdebug(2, "uio->uio_resid=%d uio->uio_iovcnt=%d error=%d len=%d", - uio->uio_resid, uio->uio_iovcnt, error, len); - if(error == 0) - error = EAGAIN; // 35 - } - break; - } - /* - | XXX: untested code - */ - sdebug(1, "uio->uio_resid=%d uio->uio_iovcnt=%d", - uio->uio_resid, uio->uio_iovcnt); - iv = uio->uio_iov; - len -= uio->uio_resid; - while(uio->uio_iovcnt > 0) { - if(iv->iov_len > len) { - caddr_t bp = (caddr_t)iv->iov_base; - - iv->iov_len -= len; - iv->iov_base = (void *)&bp[len]; - break; - } - len -= iv->iov_len; - uio->uio_iovcnt--; - uio->uio_iov++; - iv++; - } - } while(uio->uio_resid); - - if(error == 0) { - sp->stats.nsent++; - getbintime(&sp->stats.t_sent); - } - - return error; -} -#endif /* USE_MBUF */ - -/* - | wait till a PDU header is received - | from the socket. - */ -/* - The format of the BHS is: - - Byte/ 0 | 1 | 2 | 3 | - / | | | | - |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| - +---------------+---------------+---------------+---------------+ - 0|.|I| Opcode |F| Opcode-specific fields | - +---------------+---------------+---------------+---------------+ - 4|TotalAHSLength | DataSegmentLength | - +---------------+---------------+---------------+---------------+ - 8| LUN or Opcode-specific fields | - + + - 12| | - +---------------+---------------+---------------+---------------+ - 16| Initiator Task Tag | - +---------------+---------------+---------------+---------------+ - 20/ Opcode-specific fields / - +/ / - +---------------+---------------+---------------+---------------+ - 48 - */ -static __inline int -so_getbhs(isc_session_t *sp) -{ - bhs_t *bhs = &sp->bhs; - struct uio *uio = &sp->uio; - struct iovec *iov = &sp->iov; - int error, flags; - - debug_called(8); - - iov->iov_base = bhs; - iov->iov_len = sizeof(bhs_t); - - uio->uio_iov = iov; - uio->uio_iovcnt = 1; - uio->uio_rw = UIO_READ; - uio->uio_segflg = UIO_SYSSPACE; - uio->uio_td = curthread; // why ... - uio->uio_resid = sizeof(bhs_t); - - flags = MSG_WAITALL; - error = soreceive(sp->soc, NULL, uio, 0, 0, &flags); - - if(error) - debug(2, -#if __FreeBSD_version > 800000 - "error=%d so_error=%d uio->uio_resid=%zd iov.iov_len=%zd", -#else - "error=%d so_error=%d uio->uio_resid=%d iov.iov_len=%zd", -#endif - error, - sp->soc->so_error, uio->uio_resid, iov->iov_len); - if(!error && (uio->uio_resid > 0)) { - error = EPIPE; // was EAGAIN - debug(2, -#if __FreeBSD_version > 800000 - "error=%d so_error=%d uio->uio_resid=%zd iov.iov_len=%zd so_state=%x", -#else - "error=%d so_error=%d uio->uio_resid=%d iov.iov_len=%zd so_state=%x", -#endif - error, - sp->soc->so_error, uio->uio_resid, iov->iov_len, sp->soc->so_state); - } - return error; -} - -/* - | so_recv gets called when - | an iSCSI header has been received. - | Note: the designers had no intentions - | in making programmer's life easy. - */ -static int -so_recv(isc_session_t *sp, pduq_t *pq) -{ - sn_t *sn = &sp->sn; - struct uio *uio = &pq->uio; - pdu_t *pp = &pq->pdu; - bhs_t *bhs = &pp->ipdu.bhs; - struct iovec *iov = pq->iov; - int error; - u_int len; - u_int max, exp; - int flags = MSG_WAITALL; - - debug_called(8); - /* - | now calculate how much data should be in the buffer - */ - uio->uio_iov = iov; - uio->uio_iovcnt = 0; - len = 0; - if(bhs->AHSLength) { - debug(2, "bhs->AHSLength=%d", bhs->AHSLength); - pp->ahs_len = bhs->AHSLength * 4; - len += pp->ahs_len; - pp->ahs_addr = malloc(pp->ahs_len, M_TEMP, M_WAITOK); // XXX: could get stuck here - iov->iov_base = pp->ahs_addr; - iov->iov_len = pp->ahs_len; - uio->uio_iovcnt++; - iov++; - } - if(ISOK2DIG(sp->hdrDigest, pp)) { - len += sizeof(pp->hdr_dig); - iov->iov_base = &pp->hdr_dig; - iov->iov_len = sizeof(pp->hdr_dig); - uio->uio_iovcnt++; - } - if(len) { - uio->uio_rw = UIO_READ; - uio->uio_segflg = UIO_SYSSPACE; - uio->uio_resid = len; - uio->uio_td = sp->td; // why ... - error = soreceive(sp->soc, NULL, uio, NULL, NULL, &flags); - //if(error == EAGAIN) - // XXX: this needs work! it hangs iscontrol - if(error || uio->uio_resid) { - debug(2, -#if __FreeBSD_version > 800000 - "len=%d error=%d uio->uio_resid=%zd", -#else - "len=%d error=%d uio->uio_resid=%d", -#endif - len, error, uio->uio_resid); - goto out; - } - if(ISOK2DIG(sp->hdrDigest, pp)) { - bhs_t *bhs; - u_int digest; - - bhs = (bhs_t *)&pp->ipdu; - digest = sp->hdrDigest(bhs, sizeof(bhs_t), 0); - if(pp->ahs_len) - digest = sp->hdrDigest(pp->ahs_addr, pp->ahs_len, digest); - if(pp->hdr_dig != digest) { - debug(2, "bad header digest: received=%x calculated=%x", pp->hdr_dig, digest); - // XXX: now what? - error = EIO; - goto out; - } - } - if(pp->ahs_len) { - debug(2, "ahs len=%x type=%x spec=%x", - pp->ahs_addr->len, pp->ahs_addr->type, pp->ahs_addr->spec); - // XXX: till I figure out what to do with this - free(pp->ahs_addr, M_TEMP); - } - pq->len += len; // XXX: who needs this? - bzero(uio, sizeof(struct uio)); - len = 0; - } - - if(bhs->DSLength) { - len = bhs->DSLength; -#if BYTE_ORDER == LITTLE_ENDIAN - len = ((len & 0x00ff0000) >> 16) - | (len & 0x0000ff00) - | ((len & 0x000000ff) << 16); -#endif - pp->ds_len = len; - if((sp->opt.maxRecvDataSegmentLength > 0) && (len > sp->opt.maxRecvDataSegmentLength)) { - xdebug("impossible PDU length(%d) opt.maxRecvDataSegmentLength=%d", - len, sp->opt.maxRecvDataSegmentLength); - log(LOG_ERR, - "so_recv: impossible PDU length(%d) from iSCSI %s/%s\n", - len, sp->opt.targetAddress, sp->opt.targetName); - /* - | XXX: this will really screwup the stream. - | should clear up the buffer till a valid header - | is found, or just close connection ... - | should read the RFC. - */ - error = E2BIG; - goto out; - } - while(len & 03) - len++; - if(ISOK2DIG(sp->dataDigest, pp)) - len += 4; - uio->uio_resid = len; - uio->uio_td = sp->td; // why ... - pq->len += len; // XXX: do we need this? - error = soreceive(sp->soc, NULL, uio, &pq->mp, NULL, &flags); - //if(error == EAGAIN) - // XXX: this needs work! it hangs iscontrol - if(error || uio->uio_resid) - goto out; - if(ISOK2DIG(sp->dataDigest, pp)) { - struct mbuf *m; - u_int digest, ds_len, cnt; - - // get the received digest - m_copydata(pq->mp, - len - sizeof(pp->ds_dig), - sizeof(pp->ds_dig), - (caddr_t)&pp->ds_dig); - // calculate all mbufs - digest = 0; - ds_len = len - sizeof(pp->ds_dig); - for(m = pq->mp; m != NULL; m = m->m_next) { - cnt = MIN(ds_len, m->m_len); - digest = sp->dataDigest(mtod(m, char *), cnt, digest); - ds_len -= cnt; - if(ds_len == 0) - break; - } - if(digest != pp->ds_dig) { - sdebug(1, "bad data digest: received=%x calculated=%x", pp->ds_dig, digest); - error = EIO; // XXX: find a better error - goto out; - } - KASSERT(ds_len == 0, ("ds_len not zero")); - } - } - sdebug(6, "len=%d] opcode=0x%x ahs_len=0x%x ds_len=0x%x", - pq->len, bhs->opcode, pp->ahs_len, pp->ds_len); - - max = ntohl(bhs->MaxCmdSN); - exp = ntohl(bhs->ExpStSN); - if(max < exp - 1 && - max > exp - _MAXINCR) { - sdebug(2, "bad cmd window size"); - error = EIO; // XXX: for now; - goto out; // error - } - if(SNA_GT(max, sn->maxCmd)) - sn->maxCmd = max; - if(SNA_GT(exp, sn->expCmd)) - sn->expCmd = exp; - /* - | remove from the holding queue packets - | that have been acked and don't need - | further processing. - */ - i_acked_hld(sp, NULL); - - sp->cws = sn->maxCmd - sn->expCmd + 1; - - return 0; - - out: - // XXX: need some work here - if(pp->ahs_len) { - // XXX: till I figure out what to do with this - free(pp->ahs_addr, M_TEMP); - } - xdebug("have a problem, error=%d", error); - pdu_free(sp->isc, pq); - if(!error && uio->uio_resid > 0) - error = EPIPE; - return error; -} - -/* - | wait for something to arrive. - | and if the pdu is without errors, process it. - */ -static int -so_input(isc_session_t *sp) -{ - pduq_t *pq; - int error; - - debug_called(8); - /* - | first read in the iSCSI header - */ - error = so_getbhs(sp); - if(error == 0) { - /* - | now read the rest. - */ - pq = pdu_alloc(sp->isc, M_NOWAIT); - if(pq == NULL) { // XXX: might cause a deadlock ... - debug(2, "out of pdus, wait"); - pq = pdu_alloc(sp->isc, M_WAITOK); // OK to WAIT - } - pq->pdu.ipdu.bhs = sp->bhs; - pq->len = sizeof(bhs_t); // so far only the header was read - error = so_recv(sp, pq); - if(error != 0) { - error += 0x800; // XXX: just to see the error. - // terminal error - // XXX: close connection and exit - } - else { - sp->stats.nrecv++; - getbintime(&sp->stats.t_recv); - ism_recv(sp, pq); - } - } - return error; -} - -/* - | one per active (connected) session. - | this thread is responsible for reading - | in packets from the target. - */ -static void -isc_in(void *vp) -{ - isc_session_t *sp = (isc_session_t *)vp; - struct socket *so = sp->soc; - int error; - - debug_called(8); - - sp->flags |= ISC_CON_RUNNING; - error = 0; - while((sp->flags & (ISC_CON_RUN | ISC_LINK_UP)) == (ISC_CON_RUN | ISC_LINK_UP)) { - // XXX: hunting ... - if(sp->soc == NULL || !(so->so_state & SS_ISCONNECTED)) { - debug(2, "sp->soc=%p", sp->soc); - break; - } - error = so_input(sp); - if(error == 0) { - mtx_lock(&sp->io_mtx); - if(sp->flags & ISC_OWAITING) { - wakeup(&sp->flags); - } - mtx_unlock(&sp->io_mtx); - } else if(error == EPIPE) { - break; - } - else if(error == EAGAIN) { - if(so->so_state & SS_ISCONNECTED) - // there seems to be a problem in 6.0 ... - tsleep(sp, PRIBIO, "isc_soc", 2*hz); - } - } - sdebug(2, "terminated, flags=%x so_count=%d so_state=%x error=%d proc=%p", - sp->flags, so->so_count, so->so_state, error, sp->proc); - if((sp->proc != NULL) && sp->signal) { - PROC_LOCK(sp->proc); - kern_psignal(sp->proc, sp->signal); - PROC_UNLOCK(sp->proc); - sp->flags |= ISC_SIGNALED; - sdebug(2, "pid=%d signaled(%d)", sp->proc->p_pid, sp->signal); - } - else { - // we have to do something ourselves - // like closing this session ... - } - /* - | we've been terminated - */ - // do we need this mutex ...? - mtx_lock(&sp->io_mtx); - sp->flags &= ~(ISC_CON_RUNNING | ISC_LINK_UP); - wakeup(&sp->soc); - mtx_unlock(&sp->io_mtx); - - sdebug(2, "dropped ISC_CON_RUNNING"); -#if __FreeBSD_version >= 800000 - kproc_exit(0); -#else - kthread_exit(0); -#endif -} - -void -isc_stop_receiver(isc_session_t *sp) -{ - int n; - - debug_called(8); - sdebug(3, "sp=%p sp->soc=%p", sp, sp? sp->soc: 0); - mtx_lock(&sp->io_mtx); - sp->flags &= ~ISC_LINK_UP; - msleep(&sp->soc, &sp->io_mtx, PRIBIO|PDROP, "isc_stpc", 5*hz); - - soshutdown(sp->soc, SHUT_RD); - - mtx_lock(&sp->io_mtx); - sdebug(3, "soshutdown"); - sp->flags &= ~ISC_CON_RUN; - n = 2; - while(n-- && (sp->flags & ISC_CON_RUNNING)) { - sdebug(3, "waiting n=%d... flags=%x", n, sp->flags); - msleep(&sp->soc, &sp->io_mtx, PRIBIO, "isc_stpc", 5*hz); - } - mtx_unlock(&sp->io_mtx); - - if(sp->fp != NULL) - fdrop(sp->fp, sp->td); - fputsock(sp->soc); - sp->soc = NULL; - sp->fp = NULL; - - sdebug(3, "done"); -} - -void -isc_start_receiver(isc_session_t *sp) -{ - debug_called(8); - - sp->flags |= ISC_CON_RUN | ISC_LINK_UP; -#if __FreeBSD_version >= 800000 - kproc_create -#else - kthread_create -#endif - (isc_in, sp, &sp->soc_proc, 0, 0, "isc_in %d", sp->sid); -} diff -urN p4/freebsd/src/sys/dev/iscsi/initiator/isc_subr.c p4/iscsi/sys/dev/iscsi/initiator/isc_subr.c --- p4/freebsd/src/sys/dev/iscsi/initiator/isc_subr.c 2013-04-20 20:09:46.000000000 +0200 +++ p4/iscsi/sys/dev/iscsi/initiator/isc_subr.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,269 +0,0 @@ -/*- - * Copyright (c) 2005-2011 Daniel Braniss - * 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. - * - * 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. - * - */ -/* - | iSCSI - | $Id: isc_subr.c 560 2009-05-07 07:37:49Z danny $ - */ - -#include -__FBSDID("$FreeBSD: head/sys/dev/iscsi/initiator/isc_subr.c 234233 2012-04-13 18:21:56Z jpaetzel $"); - -#include "opt_iscsi_initiator.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -static MALLOC_DEFINE(M_ISC, "iSC", "iSCSI driver options"); - -static char * -i_strdupin(char *s, size_t maxlen) -{ - size_t len; - char *p, *q; - - p = malloc(maxlen, M_ISC, M_WAITOK); - if(copyinstr(s, p, maxlen, &len)) { - free(p, M_ISC); - return NULL; - } - q = malloc(len, M_ISC, M_WAITOK); - bcopy(p, q, len); - free(p, M_ISC); - - return q; -} -#if __FreeBSD_version < 800000 -/*****************************************************************/ -/* */ -/* CRC LOOKUP TABLE */ -/* ================ */ -/* The following CRC lookup table was generated automagically */ -/* by the Rocksoft^tm Model CRC Algorithm Table Generation */ -/* Program V1.0 using the following model parameters: */ -/* */ -/* Width : 4 bytes. */ -/* Poly : 0x1EDC6F41L */ -/* Reverse : TRUE. */ -/* */ -/* For more information on the Rocksoft^tm Model CRC Algorithm, */ -/* see the document titled "A Painless Guide to CRC Error */ -/* Detection Algorithms" by Ross Williams */ -/* (ross@guest.adelaide.edu.au.). This document is likely to be */ -/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */ -/* */ -/*****************************************************************/ - -static uint32_t crc32Table[256] = { - 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, - 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, - 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, - 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, - 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, - 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, - 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, - 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, - 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, - 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, - 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, - 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, - 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, - 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, - 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, - 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, - 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, - 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, - 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, - 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, - 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, - 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, - 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, - 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, - 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, - 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, - 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, - 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, - 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, - 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, - 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, - 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, - 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, - 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, - 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, - 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, - 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, - 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, - 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, - 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, - 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, - 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, - 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, - 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, - 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, - 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, - 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, - 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, - 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, - 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, - 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, - 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, - 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, - 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, - 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, - 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, - 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, - 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, - 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, - 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, - 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, - 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, - 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, - 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L -}; - -static __inline int -calculate_crc32c(uint32_t crc, const void *buf, size_t size) -{ - const uint8_t *p = buf; - - while (size--) - crc = crc32Table[(crc ^ *p++) & 0xff] ^ (crc >> 8); - return crc; -} -#endif - -static uint32_t -i_crc32c(const void *buf, size_t size, uint32_t crc) -{ - crc = crc ^ 0xffffffff; - crc = calculate_crc32c(crc, buf, size); - crc = crc ^ 0xffffffff; - return crc; -} - -/* - | XXX: not finished coding - */ -int -i_setopt(isc_session_t *sp, isc_opt_t *opt) -{ - if(opt->maxRecvDataSegmentLength > 0) { - sp->opt.maxRecvDataSegmentLength = opt->maxRecvDataSegmentLength; - sdebug(2, "maxRecvDataSegmentLength=%d", sp->opt.maxRecvDataSegmentLength); - } - if(opt->maxXmitDataSegmentLength > 0) { - // danny's RFC - sp->opt.maxXmitDataSegmentLength = opt->maxXmitDataSegmentLength; - sdebug(2, "opt.maXmitDataSegmentLength=%d", sp->opt.maxXmitDataSegmentLength); - } - if(opt->maxBurstLength != 0) { - sp->opt.maxBurstLength = opt->maxBurstLength; - sdebug(2, "opt.maxBurstLength=%d", sp->opt.maxBurstLength); - } - - if(opt->targetAddress != NULL) { - if(sp->opt.targetAddress != NULL) - free(sp->opt.targetAddress, M_ISC); - sp->opt.targetAddress = i_strdupin(opt->targetAddress, 128); - sdebug(2, "opt.targetAddress='%s'", sp->opt.targetAddress); - } - if(opt->targetName != NULL) { - if(sp->opt.targetName != NULL) - free(sp->opt.targetName, M_ISC); - sp->opt.targetName = i_strdupin(opt->targetName, 128); - sdebug(2, "opt.targetName='%s'", sp->opt.targetName); - } - if(opt->initiatorName != NULL) { - if(sp->opt.initiatorName != NULL) - free(sp->opt.initiatorName, M_ISC); - sp->opt.initiatorName = i_strdupin(opt->initiatorName, 128); - sdebug(2, "opt.initiatorName='%s'", sp->opt.initiatorName); - } - - if(opt->maxluns > 0) { - if(opt->maxluns > ISCSI_MAX_LUNS) - sp->opt.maxluns = ISCSI_MAX_LUNS; // silently chop it down ... - sp->opt.maxluns = opt->maxluns; - sdebug(2, "opt.maxluns=%d", sp->opt.maxluns); - } - - if(opt->headerDigest != NULL) { - sdebug(2, "opt.headerDigest='%s'", opt->headerDigest); - if(strcmp(opt->headerDigest, "CRC32C") == 0) { - sp->hdrDigest = (digest_t *)i_crc32c; - sdebug(2, "opt.headerDigest set"); - } - } - if(opt->dataDigest != NULL) { - sdebug(2, "opt.dataDigest='%s'", opt->headerDigest); - if(strcmp(opt->dataDigest, "CRC32C") == 0) { - sp->dataDigest = (digest_t *)i_crc32c; - sdebug(2, "opt.dataDigest set"); - } - } - - return 0; -} - -void -i_freeopt(isc_opt_t *opt) -{ - debug_called(8); - - if(opt->targetAddress != NULL) { - free(opt->targetAddress, M_ISC); - opt->targetAddress = NULL; - } - if(opt->targetName != NULL) { - free(opt->targetName, M_ISC); - opt->targetName = NULL; - } - if(opt->initiatorName != NULL) { - free(opt->initiatorName, M_ISC); - opt->initiatorName = NULL; - } -} diff -urN p4/freebsd/src/sys/dev/iscsi/initiator/iscsi.c p4/iscsi/sys/dev/iscsi/initiator/iscsi.c --- p4/freebsd/src/sys/dev/iscsi/initiator/iscsi.c 2013-04-20 20:09:46.000000000 +0200 +++ p4/iscsi/sys/dev/iscsi/initiator/iscsi.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,879 +0,0 @@ -/*- - * Copyright (c) 2005-2011 Daniel Braniss - * 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. - * - * 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. - * - */ -/* - | $Id: iscsi.c 752 2009-08-20 11:23:28Z danny $ - */ - -#include -__FBSDID("$FreeBSD: head/sys/dev/iscsi/initiator/iscsi.c 247602 2013-03-02 00:53:12Z pjd $"); - -#include "opt_iscsi_initiator.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -static char *iscsi_driver_version = "2.3.1"; - -static struct isc_softc *isc; - -MALLOC_DEFINE(M_ISCSI, "iSCSI", "iSCSI driver"); -MALLOC_DEFINE(M_ISCSIBUF, "iSCbuf", "iSCSI buffers"); -static MALLOC_DEFINE(M_TMP, "iSCtmp", "iSCSI tmp"); - -#ifdef ISCSI_INITIATOR_DEBUG -int iscsi_debug = ISCSI_INITIATOR_DEBUG; -SYSCTL_INT(_debug, OID_AUTO, iscsi_initiator, CTLFLAG_RW, &iscsi_debug, 0, - "iSCSI driver debug flag"); - -struct mtx iscsi_dbg_mtx; -#endif - -static int max_sessions = MAX_SESSIONS; -SYSCTL_INT(_net, OID_AUTO, iscsi_initiator_max_sessions, CTLFLAG_RDTUN, &max_sessions, MAX_SESSIONS, - "Max sessions allowed"); -static int max_pdus = MAX_PDUS; -SYSCTL_INT(_net, OID_AUTO, iscsi_initiator_max_pdus, CTLFLAG_RDTUN, &max_pdus, MAX_PDUS, - "Max pdu pool"); - -static char isid[6+1] = { - 0x80, - 'D', - 'I', - 'B', - '0', - '0', - 0 -}; - -static int i_create_session(struct cdev *dev, int *ndev); - -static int i_ping(struct cdev *dev); -static int i_send(struct cdev *dev, caddr_t arg, struct thread *td); -static int i_recv(struct cdev *dev, caddr_t arg, struct thread *td); -static int i_setsoc(isc_session_t *sp, int fd, struct thread *td); -static int i_fullfeature(struct cdev *dev, int flag); - -static d_open_t iscsi_open; -static d_close_t iscsi_close; -static d_ioctl_t iscsi_ioctl; -#ifdef ISCSI_INITIATOR_DEBUG -static d_read_t iscsi_read; -#endif - -static struct cdevsw iscsi_cdevsw = { - .d_version = D_VERSION, - .d_open = iscsi_open, - .d_close = iscsi_close, - .d_ioctl = iscsi_ioctl, -#ifdef ISCSI_INITIATOR_DEBUG - .d_read = iscsi_read, -#endif - .d_name = "iSCSI", -}; - -static int -iscsi_open(struct cdev *dev, int flags, int otype, struct thread *td) -{ - debug_called(8); - - debug(7, "dev=%d", dev2unit(dev)); - - if(dev2unit(dev) > max_sessions) { - // should not happen - return ENODEV; - } - return 0; -} - -static int -iscsi_close(struct cdev *dev, int flag, int otyp, struct thread *td) -{ - isc_session_t *sp; - - debug_called(8); - - debug(3, "session=%d flag=%x", dev2unit(dev), flag); - - if(dev2unit(dev) == max_sessions) { - return 0; - } - sp = dev->si_drv2; - if(sp != NULL) { - sdebug(3, "sp->flags=%x", sp->flags ); - /* - | if still in full phase, this probably means - | that something went realy bad. - | it could be a result from 'shutdown', in which case - | we will ignore it (so buffers can be flushed). - | the problem is that there is no way of differentiating - | between a shutdown procedure and 'iscontrol' dying. - */ - if(sp->flags & ISC_FFPHASE) - // delay in case this is a shutdown. - tsleep(sp, PRIBIO, "isc-cls", 60*hz); - ism_stop(sp); - } - debug(2, "done"); - return 0; -} - -static int -iscsi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode, struct thread *td) -{ - struct isc_softc *sc; - isc_session_t *sp; - isc_opt_t *opt; - int error; - - debug_called(8); - - error = 0; - if(dev2unit(dev) == max_sessions) { - /* - | non Session commands - */ - sc = dev->si_drv1; - if(sc == NULL) - return ENXIO; - - switch(cmd) { - case ISCSISETSES: - error = i_create_session(dev, (int *)arg); - if(error == 0) - break; - - default: - error = ENXIO; - } - return error; - } - /* - | session commands - */ - sp = dev->si_drv2; - if(sp == NULL) - return ENXIO; - - sdebug(6, "dev=%d cmd=%d", dev2unit(dev), (int)(cmd & 0xff)); - - switch(cmd) { - case ISCSISETSOC: - error = i_setsoc(sp, *(u_int *)arg, td); - break; - - case ISCSISETOPT: - opt = (isc_opt_t *)arg; - error = i_setopt(sp, opt); - break; - - case ISCSISEND: - error = i_send(dev, arg, td); - break; - - case ISCSIRECV: - error = i_recv(dev, arg, td); - break; - - case ISCSIPING: - error = i_ping(dev); - break; - - case ISCSISTART: - error = sp->soc == NULL? ENOTCONN: i_fullfeature(dev, 1); - if(error == 0) { - sp->proc = td->td_proc; - SYSCTL_ADD_INT(&sp->clist, SYSCTL_CHILDREN(sp->oid), - OID_AUTO, "pid", CTLFLAG_RD, - &sp->proc->p_pid, sizeof(pid_t), "control process id"); - } - break; - - case ISCSIRESTART: - error = sp->soc == NULL? ENOTCONN: i_fullfeature(dev, 2); - break; - - case ISCSISTOP: - error = i_fullfeature(dev, 0); - break; - - case ISCSISIGNAL: { - int sig = *(int *)arg; - - if(sig < 0 || sig > _SIG_MAXSIG) - error = EINVAL; - else - sp->signal = sig; - break; - } - - case ISCSIGETCAM: { - iscsi_cam_t *cp = (iscsi_cam_t *)arg; - - error = ic_getCamVals(sp, cp); - break; - } - - default: - error = ENOIOCTL; - } - - return error; -} - -static int -iscsi_read(struct cdev *dev, struct uio *uio, int ioflag) -{ -#ifdef ISCSI_INITIATOR_DEBUG - struct isc_softc *sc; - isc_session_t *sp; - pduq_t *pq; - char buf[1024]; - - sc = dev->si_drv1; - sp = dev->si_drv2; - if(dev2unit(dev) == max_sessions) { - sprintf(buf, "/----- Session ------/\n"); - uiomove(buf, strlen(buf), uio); - int i = 0; - - TAILQ_FOREACH(sp, &sc->isc_sess, sp_link) { - if(uio->uio_resid == 0) - return 0; - sprintf(buf, "%03d] '%s' '%s'\n", i++, sp->opt.targetAddress, sp->opt.targetName); - uiomove(buf, strlen(buf), uio); - } - sprintf(buf, "free npdu_alloc=%d, npdu_max=%d\n", sc->npdu_alloc, sc->npdu_max); - uiomove(buf, strlen(buf), uio); - } - else { - int i = 0; - struct socket *so = sp->soc; -#define pukeit(i, pq) do {\ - sprintf(buf, "%03d] %06x %02x %06x %06x %jd\n",\ - i, ntohl(pq->pdu.ipdu.bhs.CmdSN),\ - pq->pdu.ipdu.bhs.opcode, ntohl(pq->pdu.ipdu.bhs.itt),\ - ntohl(pq->pdu.ipdu.bhs.ExpStSN),\ - (intmax_t)pq->ts.sec);\ - } while(0) - - sprintf(buf, "%d/%d /---- hld -----/\n", sp->stats.nhld, sp->stats.max_hld); - uiomove(buf, strlen(buf), uio); - TAILQ_FOREACH(pq, &sp->hld, pq_link) { - if(uio->uio_resid == 0) - return 0; - pukeit(i, pq); i++; - uiomove(buf, strlen(buf), uio); - } - sprintf(buf, "%d/%d /---- rsp -----/\n", sp->stats.nrsp, sp->stats.max_rsp); - uiomove(buf, strlen(buf), uio); - i = 0; - TAILQ_FOREACH(pq, &sp->rsp, pq_link) { - if(uio->uio_resid == 0) - return 0; - pukeit(i, pq); i++; - uiomove(buf, strlen(buf), uio); - } - sprintf(buf, "%d/%d /---- csnd -----/\n", sp->stats.ncsnd, sp->stats.max_csnd); - i = 0; - uiomove(buf, strlen(buf), uio); - TAILQ_FOREACH(pq, &sp->csnd, pq_link) { - if(uio->uio_resid == 0) - return 0; - pukeit(i, pq); i++; - uiomove(buf, strlen(buf), uio); - } - sprintf(buf, "%d/%d /---- wsnd -----/\n", sp->stats.nwsnd, sp->stats.max_wsnd); - i = 0; - uiomove(buf, strlen(buf), uio); - TAILQ_FOREACH(pq, &sp->wsnd, pq_link) { - if(uio->uio_resid == 0) - return 0; - pukeit(i, pq); i++; - uiomove(buf, strlen(buf), uio); - } - sprintf(buf, "%d/%d /---- isnd -----/\n", sp->stats.nisnd, sp->stats.max_isnd); - i = 0; - uiomove(buf, strlen(buf), uio); - TAILQ_FOREACH(pq, &sp->isnd, pq_link) { - if(uio->uio_resid == 0) - return 0; - pukeit(i, pq); i++; - uiomove(buf, strlen(buf), uio); - } - - sprintf(buf, "/---- Stats ---/\n"); - uiomove(buf, strlen(buf), uio); - - sprintf(buf, "recv=%d sent=%d\n", sp->stats.nrecv, sp->stats.nsent); - uiomove(buf, strlen(buf), uio); - - sprintf(buf, "flags=%x pdus: alloc=%d max=%d\n", - sp->flags, sc->npdu_alloc, sc->npdu_max); - uiomove(buf, strlen(buf), uio); - - sprintf(buf, "cws=%d last cmd=%x exp=%x max=%x stat=%x itt=%x\n", - sp->cws, sp->sn.cmd, sp->sn.expCmd, sp->sn.maxCmd, sp->sn.stat, sp->sn.itt); - uiomove(buf, strlen(buf), uio); - - sprintf(buf, "/---- socket -----/\nso_count=%d so_state=%x\n", so->so_count, so->so_state); - uiomove(buf, strlen(buf), uio); - - } -#endif - return 0; -} - -static int -i_ping(struct cdev *dev) -{ - return 0; -} -/* - | low level I/O - */ -static int -i_setsoc(isc_session_t *sp, int fd, struct thread *td) -{ - int error = 0; - - if(sp->soc != NULL) - isc_stop_receiver(sp); - - error = fget(td, fd, CAP_SOCK_CLIENT, &sp->fp); - if(error) - return error; - - if((error = fgetsock(td, fd, CAP_SOCK_CLIENT, &sp->soc, 0)) == 0) { - sp->td = td; - isc_start_receiver(sp); - } - else { - fdrop(sp->fp, td); - sp->fp = NULL; - } - - return error; -} - -static int -i_send(struct cdev *dev, caddr_t arg, struct thread *td) -{ - isc_session_t *sp = dev->si_drv2; - caddr_t bp; - pduq_t *pq; - pdu_t *pp; - int n, error; - - debug_called(8); - - if(sp->soc == NULL) - return ENOTCONN; - - if((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL) - return EAGAIN; - pp = &pq->pdu; - pq->pdu = *(pdu_t *)arg; - if((error = i_prepPDU(sp, pq)) != 0) - goto out; - - bp = NULL; - if((pq->len - sizeof(union ipdu_u)) > 0) { - pq->buf = bp = malloc(pq->len - sizeof(union ipdu_u), M_ISCSIBUF, M_NOWAIT); - if(pq->buf == NULL) { - error = EAGAIN; - goto out; - } - } - else - pq->buf = NULL; // just in case? - - sdebug(2, "len=%d ahs_len=%d ds_len=%d buf=%zu@%p", - pq->len, pp->ahs_len, pp->ds_len, pq->len - sizeof(union ipdu_u), bp); - - if(pp->ahs_len) { - // XXX: never tested, looks suspicious - n = pp->ahs_len; - error = copyin(pp->ahs_addr, bp, n); - if(error != 0) { - sdebug(3, "copyin ahs: error=%d", error); - goto out; - } - pp->ahs_addr = (ahs_t *)bp; - bp += n; - } - if(pp->ds_len) { - n = pp->ds_len; - error = copyin(pp->ds_addr, bp, n); - if(error != 0) { - sdebug(3, "copyin ds: error=%d", error); - goto out; - } - pp->ds_addr = bp; - bp += n; - while(n & 03) { - n++; - *bp++ = 0; - } - } - - error = isc_qout(sp, pq); - if(error == 0) - wakeup(&sp->flags); // XXX: to 'push' proc_out ... -out: - if(error) - pdu_free(sp->isc, pq); - - return error; -} - -static int -i_recv(struct cdev *dev, caddr_t arg, struct thread *td) -{ - isc_session_t *sp = dev->si_drv2; - pduq_t *pq; - pdu_t *pp, *up; - caddr_t bp; - int error, mustfree, cnt; - size_t need, have, n; - - debug_called(8); - - if(sp == NULL) - return EIO; - - if(sp->soc == NULL) - return ENOTCONN; - cnt = 6; // XXX: maybe the user can request a time out? - mtx_lock(&sp->rsp_mtx); - while((pq = TAILQ_FIRST(&sp->rsp)) == NULL) { - msleep(&sp->rsp, &sp->rsp_mtx, PRIBIO, "isc_rsp", hz*10); - if(cnt-- == 0) break; // XXX: for now, needs work - } - if(pq != NULL) { - sp->stats.nrsp--; - TAILQ_REMOVE(&sp->rsp, pq, pq_link); - } - mtx_unlock(&sp->rsp_mtx); - - sdebug(6, "cnt=%d", cnt); - - if(pq == NULL) { - error = ENOTCONN; - sdebug(3, "error=%d sp->flags=%x ", error, sp->flags); - return error; - } - up = (pdu_t *)arg; - pp = &pq->pdu; - up->ipdu = pp->ipdu; - n = 0; - up->ds_len = 0; - up->ahs_len = 0; - error = 0; - - if(pq->mp) { - u_int len; - - // Grr... - len = 0; - if(pp->ahs_len) { - len += pp->ahs_len; - } - if(pp->ds_len) { - len += pp->ds_len; - } - - mustfree = 0; - if(len > pq->mp->m_len) { - mustfree++; - bp = malloc(len, M_TMP, M_WAITOK); - sdebug(4, "need mbufcopy: %d", len); - i_mbufcopy(pq->mp, bp, len); - } - else - bp = mtod(pq->mp, caddr_t); - - if(pp->ahs_len) { - need = pp->ahs_len; - n = MIN(up->ahs_size, need); - error = copyout(bp, (caddr_t)up->ahs_addr, n); - up->ahs_len = n; - bp += need; - } - if(!error && pp->ds_len) { - need = pp->ds_len; - if((have = up->ds_size) == 0) { - have = up->ahs_size - n; - up->ds_addr = (caddr_t)up->ahs_addr + n; - } - n = MIN(have, need); - error = copyout(bp, (caddr_t)up->ds_addr, n); - up->ds_len = n; - } - - if(mustfree) - free(bp, M_TMP); - } - - sdebug(6, "len=%d ahs_len=%d ds_len=%d", pq->len, pp->ahs_len, pp->ds_len); - - pdu_free(sp->isc, pq); - - return error; -} - -static int -i_fullfeature(struct cdev *dev, int flag) -{ - isc_session_t *sp = dev->si_drv2; - int error; - - sdebug(2, "flag=%d", flag); - - error = 0; - switch(flag) { - case 0: // stop - sp->flags &= ~ISC_FFPHASE; - break; - case 1: // start - sp->flags |= ISC_FFPHASE; - error = ic_init(sp); - break; - case 2: // restart - sp->flags |= ISC_FFPHASE; - ism_restart(sp); - break; - } - return error; -} - -static int -i_create_session(struct cdev *dev, int *ndev) -{ - struct isc_softc *sc = dev->si_drv1; - isc_session_t *sp; - int error, n; - - debug_called(8); - - sp = malloc(sizeof(isc_session_t), M_ISCSI, M_WAITOK | M_ZERO); - if(sp == NULL) - return ENOMEM; - - sx_xlock(&sc->unit_sx); - if((n = alloc_unr(sc->unit)) < 0) { - sx_unlock(&sc->unit_sx); - free(sp, M_ISCSI); - xdebug("too many sessions!"); - return EPERM; - } - sx_unlock(&sc->unit_sx); - - mtx_lock(&sc->isc_mtx); - TAILQ_INSERT_TAIL(&sc->isc_sess, sp, sp_link); - isc->nsess++; - mtx_unlock(&sc->isc_mtx); - - sp->dev = make_dev(&iscsi_cdevsw, n, UID_ROOT, GID_WHEEL, 0600, "iscsi%d", n); - *ndev = sp->sid = n; - sp->isc = sc; - sp->dev->si_drv1 = sc; - sp->dev->si_drv2 = sp; - - sp->opt.maxRecvDataSegmentLength = 8192; - sp->opt.maxXmitDataSegmentLength = 8192; - sp->opt.maxBurstLength = 65536; // 64k - sp->opt.maxluns = ISCSI_MAX_LUNS; - - error = ism_start(sp); - - return error; -} - -#ifdef notused -static void -iscsi_counters(isc_session_t *sp) -{ - int h, r, s; - pduq_t *pq; - -#define _puke(i, pq) do {\ - debug(2, "%03d] %06x %02x %x %ld %jd %x\n",\ - i, ntohl( pq->pdu.ipdu.bhs.CmdSN), \ - pq->pdu.ipdu.bhs.opcode, ntohl(pq->pdu.ipdu.bhs.itt),\ - (long)pq->ts.sec, pq->ts.frac, pq->flags);\ - } while(0) - - h = r = s = 0; - TAILQ_FOREACH(pq, &sp->hld, pq_link) { - _puke(h, pq); - h++; - } - TAILQ_FOREACH(pq, &sp->rsp, pq_link) r++; - TAILQ_FOREACH(pq, &sp->csnd, pq_link) s++; - TAILQ_FOREACH(pq, &sp->wsnd, pq_link) s++; - TAILQ_FOREACH(pq, &sp->isnd, pq_link) s++; - debug(2, "hld=%d rsp=%d snd=%d", h, r, s); -} -#endif - -static void -iscsi_shutdown(void *v) -{ - struct isc_softc *sc = v; - isc_session_t *sp; - int n; - - debug_called(8); - if(sc == NULL) { - xdebug("sc is NULL!"); - return; - } -#ifdef DO_EVENTHANDLER - if(sc->eh == NULL) - debug(2, "sc->eh is NULL"); - else { - EVENTHANDLER_DEREGISTER(shutdown_pre_sync, sc->eh); - debug(2, "done n=%d", sc->nsess); - } -#endif - n = 0; - TAILQ_FOREACH(sp, &sc->isc_sess, sp_link) { - debug(2, "%2d] sp->flags=0x%08x", n, sp->flags); - n++; - } - debug(2, "done"); -} - -static void -free_pdus(struct isc_softc *sc) -{ - debug_called(8); - - if(sc->pdu_zone != NULL) { - uma_zdestroy(sc->pdu_zone); - sc->pdu_zone = NULL; - } -} - -static void -iscsi_start(void) -{ - debug_called(8); - - TUNABLE_INT_FETCH("net.iscsi_initiator.max_sessions", &max_sessions); - TUNABLE_INT_FETCH("net.iscsi_initiator.max_pdus", &max_pdus); - - isc = malloc(sizeof(struct isc_softc), M_ISCSI, M_ZERO|M_WAITOK); - isc->dev = make_dev(&iscsi_cdevsw, max_sessions, UID_ROOT, GID_WHEEL, 0600, "iscsi"); - isc->dev->si_drv1 = isc; - mtx_init(&isc->isc_mtx, "iscsi", NULL, MTX_DEF); - - TAILQ_INIT(&isc->isc_sess); - /* - | now init the free pdu list - */ - isc->pdu_zone = uma_zcreate("pdu", sizeof(pduq_t), - NULL, NULL, NULL, NULL, - 0, 0); - if(isc->pdu_zone == NULL) { - xdebug("iscsi_initiator: uma_zcreate failed"); - // XXX: should fail... - } - uma_zone_set_max(isc->pdu_zone, max_pdus); - isc->unit = new_unrhdr(0, max_sessions-1, NULL); - sx_init(&isc->unit_sx, "iscsi sx"); - -#ifdef DO_EVENTHANDLER - if((isc->eh = EVENTHANDLER_REGISTER(shutdown_pre_sync, iscsi_shutdown, - sc, SHUTDOWN_PRI_DEFAULT-1)) == NULL) - xdebug("shutdown event registration failed\n"); -#endif - /* - | sysctl stuff - */ - sysctl_ctx_init(&isc->clist); - isc->oid = SYSCTL_ADD_NODE(&isc->clist, - SYSCTL_STATIC_CHILDREN(_net), - OID_AUTO, - "iscsi_initiator", - CTLFLAG_RD, - 0, - "iSCSI Subsystem"); - - SYSCTL_ADD_STRING(&isc->clist, - SYSCTL_CHILDREN(isc->oid), - OID_AUTO, - "driver_version", - CTLFLAG_RD, - iscsi_driver_version, - 0, - "iscsi driver version"); - - SYSCTL_ADD_STRING(&isc->clist, - SYSCTL_CHILDREN(isc->oid), - OID_AUTO, - "isid", - CTLFLAG_RW, - isid, - 6+1, - "initiator part of the Session Identifier"); - - SYSCTL_ADD_INT(&isc->clist, - SYSCTL_CHILDREN(isc->oid), - OID_AUTO, - "sessions", - CTLFLAG_RD, - &isc->nsess, - sizeof(isc->nsess), - "number of active session"); - -#ifdef ISCSI_INITIATOR_DEBUG - mtx_init(&iscsi_dbg_mtx, "iscsi_dbg", NULL, MTX_DEF); -#endif - - printf("iscsi: version %s\n", iscsi_driver_version); -} - -/* - | Notes: - | unload SHOULD fail if there is activity - | activity: there is/are active session/s - */ -static void -iscsi_stop(void) -{ - isc_session_t *sp, *sp_tmp; - - debug_called(8); - - /* - | go through all the sessions - | Note: close should have done this ... - */ - TAILQ_FOREACH_SAFE(sp, &isc->isc_sess, sp_link, sp_tmp) { - //XXX: check for activity ... - ism_stop(sp); - if(sp->cam_sim != NULL) - ic_destroy(sp); - } - mtx_destroy(&isc->isc_mtx); - sx_destroy(&isc->unit_sx); - - free_pdus(isc); - - if(isc->dev) - destroy_dev(isc->dev); - - if(sysctl_ctx_free(&isc->clist)) - xdebug("sysctl_ctx_free failed"); - - iscsi_shutdown(isc); // XXX: check EVENTHANDLER_ ... - -#ifdef ISCSI_INITIATOR_DEBUG - mtx_destroy(&iscsi_dbg_mtx); -#endif - - free(isc, M_ISCSI); -} - -static int -iscsi_modevent(module_t mod, int what, void *arg) -{ - debug_called(8); - - switch(what) { - case MOD_LOAD: - iscsi_start(); - break; - - case MOD_QUIESCE: - if(isc->nsess) { - xdebug("iscsi module busy(nsess=%d), cannot unload", isc->nsess); - log(LOG_ERR, "iscsi module busy, cannot unload"); - } - return isc->nsess; - - case MOD_SHUTDOWN: - break; - - case MOD_UNLOAD: - iscsi_stop(); - break; - - default: - break; - } - return 0; -} - -moduledata_t iscsi_mod = { - "iscsi", - (modeventhand_t) iscsi_modevent, - 0 -}; - -#ifdef ISCSI_ROOT -static void -iscsi_rootconf(void) -{ -#if 0 - nfs_setup_diskless(); - if (nfs_diskless_valid) - rootdevnames[0] = "nfs:"; -#endif - printf("** iscsi_rootconf **\n"); -} - -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); diff -urN p4/freebsd/src/sys/dev/iscsi/initiator/iscsi.h p4/iscsi/sys/dev/iscsi/initiator/iscsi.h --- p4/freebsd/src/sys/dev/iscsi/initiator/iscsi.h 2013-04-20 20:09:46.000000000 +0200 +++ p4/iscsi/sys/dev/iscsi/initiator/iscsi.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,500 +0,0 @@ -/*- - * Copyright (c) 2005-2010 Daniel Braniss - * 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. - * - * 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: head/sys/dev/iscsi/initiator/iscsi.h 211095 2010-08-09 12:36:36Z des $ - */ -/* - | $Id: iscsi.h 743 2009-08-08 10:54:53Z danny $ - */ -#define TRUE 1 -#define FALSE 0 -#ifndef _KERNEL -typedef int boolean_t; -#endif - -#include - -#define ISCSIDEV "iscsi" -#define ISCSI_MAX_TARGETS 64 -/* - | iSCSI commands - */ - -/* - | Initiator Opcodes: - */ -#define ISCSI_NOP_OUT 0x00 -#define ISCSI_SCSI_CMD 0x01 -#define ISCSI_TASK_CMD 0x02 -#define ISCSI_LOGIN_CMD 0x03 -#define ISCSI_TEXT_CMD 0x04 -#define ISCSI_WRITE_DATA 0x05 -#define ISCSI_LOGOUT_CMD 0x06 -#define ISCSI_SNACK 0x10 -/* - | Target Opcodes: - */ -#define ISCSI_NOP_IN 0x20 -#define ISCSI_SCSI_RSP 0x21 -#define ISCSI_TASK_RSP 0x22 -#define ISCSI_LOGIN_RSP 0x23 -#define ISCSI_TEXT_RSP 0x24 -#define ISCSI_READ_DATA 0x25 -#define ISCSI_LOGOUT_RSP 0x26 -#define ISCSI_R2T 0x31 -#define ISCSI_ASYNC 0x32 -#define ISCSI_REJECT 0x3f -/* - | PDU stuff - */ -/* - | BHS Basic Header Segment - */ -typedef struct bhs { - // the order is network byte order! - u_char opcode:6; - u_char I:1; - u_char _:1; - u_char __:7; - u_char F:1; // Final bit - u_char ___[2]; - - u_int AHSLength:8; // in 4byte words - u_int DSLength:24; // in bytes - - u_int LUN[2]; // or Opcode-specific fields - u_int itt; - u_int OpcodeSpecificFields[7]; -#define CmdSN OpcodeSpecificFields[1] -#define ExpStSN OpcodeSpecificFields[2] -#define MaxCmdSN OpcodeSpecificFields[3] -} bhs_t; - -typedef struct ahs { - u_int len:16; - u_int type:8; - u_int spec:8; - char data[0]; -} ahs_t; - -typedef struct { - // Sequence Numbers - // (computers were invented to count, right?) - int cmd; - int expcmd; - int maxcmd; -} req_sn_t; - -typedef struct { - // Sequence Numbers - // (computers were invented to count, right?) - int stat; - int expcmd; - int maxcmd; -} rsp_sn_t; - -typedef struct scsi_req { - u_char opcode:6; // 0x01 - u_char I:1; - u_char _:1; - - u_char attr:3; - u_char _0:2; - u_char W:1; - u_char R:1; - u_char F:1; -#define iSCSI_TASK_UNTAGGED 0 -#define iSCSI_TASK_SIMPLE 1 -#define iSCSI_TASK_ORDER 2 -#define iSCSI_TASK_HOFQ 3 -#define iSCSI_TASK_ACA 4 - char _1[2]; - int len; - int lun[2]; - int itt; - int edtlen; // expectect data transfere length - int cmdSN; - int extStatSN; - int cdb[4]; -} scsi_req_t; - -typedef struct scsi_rsp { - char opcode; // 0x21 - u_char flag; - u_char response; - u_char status; - - int len; - int _[2]; - int itt; - int stag; - rsp_sn_t sn; - int expdatasn; - int bdrcnt; // bidirectional residual count - int rcnt; // residual count -} scsi_rsp_t; - -typedef struct nop_out { - // the order is network byte order! - u_char opcode:6; - u_char I:1; - u_char _:1; - u_char __:7; - u_char F:1; // Final bit - u_char ___[2]; - - u_int len; - u_int lun[2]; - u_int itt; - u_int ttt; - req_sn_t sn; - u_int mbz[3]; -} nop_out_t; - -typedef struct nop_in { - // the order is network byte order! - u_char opcode:6; - u_char I:1; - u_char _:1; - u_char __:7; - u_char F:1; // Final bit - u_char ___[2]; - - u_int len; - u_int lun[2]; - u_int itt; - u_int ttt; - rsp_sn_t sn; - u_int ____[2]; - -} nop_in_t; - -typedef struct r2t { - u_char opcode:6; - u_char I:1; - u_char _:1; - u_char __:7; - u_char F:1; // Final bit - u_char ___[2]; - - u_int len; - u_int lun[2]; - u_int itt; - u_int ttt; - rsp_sn_t sn; - u_int r2tSN; - u_int bo; - u_int ddtl; -} r2t_t; - -typedef struct data_out { - u_char opcode:6; - u_char I:1; - u_char _:1; - u_char __:7; - u_char F:1; // Final bit - u_char ___[2]; - - u_int len; - u_int lun[2]; - u_int itt; - u_int ttt; - rsp_sn_t sn; - u_int dsn; // data seq. number - u_int bo; - u_int ____; -} data_out_t; - -typedef struct data_in { - u_char opcode:6; - u_char I:1; - u_char _:1; - - u_char S:1; - u_char U:1; - u_char O:1; - u_char __:3; - u_char A:1; - u_char F:1; // Final bit - u_char ___[1]; - u_char status; - - u_int len; - u_int lun[2]; - u_int itt; - u_int ttt; - rsp_sn_t sn; - u_int dataSN; - u_int bo; - u_int ____; -} data_in_t; - -typedef struct reject { - u_char opcode:6; - u_char _:2; - u_char F:1; - u_char __:7; - u_char reason; - u_char ___; - - u_int len; - u_int ____[2]; - u_int tt[2]; // must be -1 - rsp_sn_t sn; - u_int dataSN; // or R2TSN or reserved - u_int _____[2]; -} reject_t; - -typedef struct async { - u_char opcode:6; - u_char _:2; - u_char F:1; - u_char __:7; - u_char ___[2]; - - u_int len; - u_int lun[2]; - u_int itt; // must be -1 - u_int ____; - rsp_sn_t sn; - - u_char asyncEvent; - u_char asyncVCode; - u_char param1[2]; - u_char param2[2]; - u_char param3[2]; - - u_int _____; - -} async_t; - -typedef struct login_req { - char cmd; // 0x03 - - u_char NSG:2; - u_char CSG:2; - u_char _:2; - u_char C:1; - u_char T:1; - - char v_max; - char v_min; - - int len; // remapped via standard bhs - char isid[6]; - short tsih; - int itt; // Initiator Task Tag; - - int CID:16; - int rsv:16; - - int cmdSN; - int expStatSN; - int unused[4]; -} login_req_t; - -typedef struct login_rsp { - char cmd; // 0x23 - u_char NSG:2; - u_char CSG:2; - u_char _1:2; - u_char C:1; - u_char T:1; - - char v_max; - char v_act; - - int len; // remapped via standard bhs - char isid[6]; - short tsih; - int itt; // Initiator Task Tag; - int _2; - rsp_sn_t sn; - int status:16; - int _3:16; - int _4[2]; -} login_rsp_t; - -typedef struct text_req { - char cmd; // 0x04 - - u_char _1:6; - u_char C:1; // Continuation - u_char F:1; // Final - char _2[2]; - - int len; - int itt; // Initiator Task Tag - int LUN[2]; - int ttt; // Target Transfer Tag - int cmdSN; - int expStatSN; - int unused[4]; -} text_req_t; - -typedef struct logout_req { - char cmd; // 0x06 - char reason; // 0 - close session - // 1 - close connection - // 2 - remove the connection for recovery - char _2[2]; - - int len; - int _r[2]; - int itt; // Initiator Task Tag; - - u_int CID:16; - u_int rsv:16; - - int cmdSN; - int expStatSN; - int unused[4]; -} logout_req_t; - -typedef struct logout_rsp { - char cmd; // 0x26 - char cbits; - char _1[2]; - int len; - int _2[2]; - int itt; - int _3; - rsp_sn_t sn; - short time2wait; - short time2retain; - int _4; -} logout_rsp_t; - -union ipdu_u { - bhs_t bhs; - scsi_req_t scsi_req; - scsi_rsp_t scsi_rsp; - nop_out_t nop_out; - nop_in_t nop_in; - r2t_t r2t; - data_out_t data_out; - data_in_t data_in; - reject_t reject; - async_t async; -}; - -/* - | Sequence Numbers - */ -typedef struct { - u_int itt; - u_int cmd; - u_int expCmd; - u_int maxCmd; - u_int stat; - u_int expStat; - u_int data; -} sn_t; - -/* - | in-core version of a Protocol Data Unit - */ -typedef struct { - union ipdu_u ipdu; - u_int hdr_dig; // header digest - - ahs_t *ahs_addr; - u_int ahs_len; - u_int ahs_size; // the allocated size - - u_char *ds_addr; - u_int ds_len; - u_int ds_size; // the allocated size - u_int ds_dig; // data digest -} pdu_t; - -typedef struct opvals { - int port; - int tags; - int maxluns; - int sockbufsize; - - int maxConnections; - int maxRecvDataSegmentLength; - int maxXmitDataSegmentLength; // pseudo ... - int maxBurstLength; - int firstBurstLength; - int defaultTime2Wait; - int defaultTime2Retain; - int maxOutstandingR2T; - int errorRecoveryLevel; - int targetPortalGroupTag; - - boolean_t initialR2T; - boolean_t immediateData; - boolean_t dataPDUInOrder; - boolean_t dataSequenceInOrder; - char *headerDigest; - char *dataDigest; - char *sessionType; - char *sendTargets; - char *targetAddress; - char *targetAlias; - char *targetName; - char *initiatorName; - char *initiatorAlias; - char *authMethod; - char *chapSecret; - char *chapIName; - char *chapDigest; - char *tgtChapName; - char *tgtChapSecret; - int tgtChallengeLen; - u_char tgtChapID; - char *tgtChapDigest; - char *iqn; - char *pidfile; -} isc_opt_t; - -/* - | ioctl - */ -#define ISCSISETSES _IOR('i', 1, int) -#define ISCSISETSOC _IOW('i', 2, int) -#define ISCSISETOPT _IOW('i', 5, isc_opt_t) -#define ISCSIGETOPT _IOR('i', 6, isc_opt_t) - -#define ISCSISEND _IOW('i', 10, pdu_t) -#define ISCSIRECV _IOWR('i', 11, pdu_t) - -#define ISCSIPING _IO('i', 20) -#define ISCSISIGNAL _IOW('i', 21, int *) - -#define ISCSISTART _IO('i', 30) -#define ISCSIRESTART _IO('i', 31) -#define ISCSISTOP _IO('i', 32) - -typedef struct iscsi_cam { - path_id_t path_id; - target_id_t target_id; - int target_nluns; -} iscsi_cam_t; - -#define ISCSIGETCAM _IOR('i', 33, iscsi_cam_t) diff -urN p4/freebsd/src/sys/dev/iscsi/initiator/iscsi_subr.c p4/iscsi/sys/dev/iscsi/initiator/iscsi_subr.c --- p4/freebsd/src/sys/dev/iscsi/initiator/iscsi_subr.c 2013-04-20 20:09:46.000000000 +0200 +++ p4/iscsi/sys/dev/iscsi/initiator/iscsi_subr.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,608 +0,0 @@ -/*- - * Copyright (c) 2005-2010 Daniel Braniss - * 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. - * - * 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. - * - */ -/* - | $Id: iscsi_subr.c 743 2009-08-08 10:54:53Z danny $ - */ - -#include -__FBSDID("$FreeBSD: head/sys/dev/iscsi/initiator/iscsi_subr.c 234233 2012-04-13 18:21:56Z jpaetzel $"); - -#include "opt_iscsi_initiator.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -/* - | Interface to the SCSI layer - */ -void -iscsi_r2t(isc_session_t *sp, pduq_t *opq, pduq_t *pq) -{ - union ccb *ccb = opq->ccb; - struct ccb_scsiio *csio = &ccb->csio; - pdu_t *opp = &opq->pdu; - bhs_t *bhp = &opp->ipdu.bhs; - r2t_t *r2t = &pq->pdu.ipdu.r2t; - pduq_t *wpq; - int error; - - debug_called(8); - sdebug(4, "itt=%x r2tSN=%d bo=%x ddtl=%x W=%d", ntohl(r2t->itt), - ntohl(r2t->r2tSN), ntohl(r2t->bo), ntohl(r2t->ddtl), opp->ipdu.scsi_req.W); - - switch(bhp->opcode) { - case ISCSI_SCSI_CMD: - if(opp->ipdu.scsi_req.W) { - data_out_t *cmd; - u_int ddtl = ntohl(r2t->ddtl); - u_int edtl = ntohl(opp->ipdu.scsi_req.edtlen); - u_int bleft, bs, dsn, bo; - caddr_t bp = csio->data_ptr; - - bo = ntohl(r2t->bo); - bp += MIN(bo, edtl - ddtl); - bleft = ddtl; - - if(sp->opt.maxXmitDataSegmentLength > 0) // danny's RFC - bs = MIN(sp->opt.maxXmitDataSegmentLength, ddtl); - else - bs = ddtl; - dsn = 0; - sdebug(4, "edtl=%x ddtl=%x bo=%x dsn=%x bs=%x maxX=%x", - edtl, ddtl, bo, dsn, bs, sp->opt.maxXmitDataSegmentLength); - while(bleft > 0) { - wpq = pdu_alloc(sp->isc, M_NOWAIT); // testing ... - if(wpq == NULL) { - sdebug(3, "itt=%x r2tSN=%d bo=%x ddtl=%x W=%d", ntohl(r2t->itt), - ntohl(r2t->r2tSN), ntohl(r2t->bo), ntohl(r2t->ddtl), opp->ipdu.scsi_req.W); - sdebug(1, "npdu_max=%d npdu_alloc=%d", sp->isc->npdu_max, sp->isc->npdu_alloc); - - while((wpq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL) { - sdebug(2, "waiting..."); -#if __FreeBSD_version >= 700000 - pause("isc_r2t", 5*hz); -#else - tsleep(sp->isc, 0, "isc_r2t", 5*hz); -#endif - } - } - cmd = &wpq->pdu.ipdu.data_out; - cmd->opcode = ISCSI_WRITE_DATA; - cmd->lun[0] = r2t->lun[0]; - cmd->lun[1] = r2t->lun[1]; - cmd->ttt = r2t->ttt; - cmd->itt = r2t->itt; - - cmd->dsn = htonl(dsn); - cmd->bo = htonl(bo); - - cmd->F = (bs < bleft)? 0: 1; // is this the last one? - bs = MIN(bs, bleft); - - wpq->pdu.ds_len = bs; - wpq->pdu.ds_addr = bp; - - error = isc_qout(sp, wpq); - sdebug(6, "bs=%x bo=%x bp=%p dsn=%x error=%d", bs, bo, bp, dsn, error); - if(error) - break; - bo += bs; - bp += bs; - bleft -= bs; - dsn++; - } - } - break; - - default: - // XXX: should not happen ... - xdebug("huh? opcode=0x%x", bhp->opcode); - } -} - -static int -getSenseData(u_int status, union ccb *ccb, pduq_t *pq) -{ - pdu_t *pp = &pq->pdu; - struct ccb_scsiio *scsi = (struct ccb_scsiio *)ccb; - struct scsi_sense_data *sense = &scsi->sense_data; - struct mbuf *m = pq->mp; - scsi_rsp_t *cmd = &pp->ipdu.scsi_rsp; - caddr_t bp; - int sense_len, mustfree = 0; - int error_code, sense_key, asc, ascq; - - bp = mtod(pq->mp, caddr_t); - if((sense_len = scsi_2btoul(bp)) == 0) - return 0; - debug(4, "sense_len=%d", sense_len); - /* - | according to the specs, the sense data cannot - | be larger than 252 ... - */ - if(sense_len > m->m_len) { - bp = malloc(sense_len, M_ISCSI, M_WAITOK); - debug(3, "calling i_mbufcopy(len=%d)", sense_len); - i_mbufcopy(pq->mp, bp, sense_len); - mustfree++; - } - scsi->scsi_status = status; - - bcopy(bp+2, sense, min(sense_len, scsi->sense_len)); - scsi->sense_resid = 0; - if(cmd->flag & (BIT(1)|BIT(2))) - scsi->sense_resid = ntohl(pp->ipdu.scsi_rsp.rcnt); - scsi_extract_sense_len(sense, scsi->sense_len - scsi->sense_resid, - &error_code, &sense_key, &asc, &ascq, /*show_errors*/ 1); - - debug(3, "sense_len=%d rcnt=%d sense_resid=%d dsl=%d error_code=%x flags=%x", - sense_len, - ntohl(pp->ipdu.scsi_rsp.rcnt), scsi->sense_resid, - pp->ds_len, error_code, sense_key); - - if(mustfree) - free(bp, M_ISCSI); - - return 1; -} - -/* - | Some information is from SAM draft. - */ -static void -_scsi_done(isc_session_t *sp, u_int response, u_int status, union ccb *ccb, pduq_t *pq) -{ - struct ccb_hdr *ccb_h = &ccb->ccb_h; - - debug_called(8); - - if(status || response) { - sdebug(3, "response=%x status=%x ccb=%p pq=%p", response, status, ccb, pq); - if(pq != NULL) - sdebug(3, "mp=%p buf=%p len=%d", pq->mp, pq->buf, pq->len); - } - ccb_h->status = 0; - switch(response) { - case 0: // Command Completed at Target - switch(status) { - case 0: // Good, all is ok - ccb_h->status = CAM_REQ_CMP; - break; - - case 0x02: // Check Condition - if((pq != NULL) && (pq->mp != NULL) && getSenseData(status, ccb, pq)) - ccb_h->status |= CAM_AUTOSNS_VALID; - - case 0x14: // Intermediate-Condition Met - case 0x10: // Intermediate - case 0x04: // Condition Met - ccb_h->status |= CAM_SCSI_STATUS_ERROR; - break; - - case 0x08: - ccb_h->status = CAM_BUSY; - break; - - case 0x18: // Reservation Conflict - case 0x28: // Task Set Full - ccb_h->status = CAM_REQUEUE_REQ; - break; - default: - //case 0x22: // Command Terminated - //case 0x30: // ACA Active - //case 0x40: // Task Aborted - ccb_h->status = CAM_REQ_CMP_ERR; //CAM_REQ_ABORTED; - } - break; - - default: - if((response >= 0x80) && (response <= 0xFF)) { - // Vendor specific ... - } - case 1: // target failure - ccb_h->status = CAM_REQ_CMP_ERR; //CAM_REQ_ABORTED; - break; - } - sdebug(5, "ccb_h->status=%x", ccb_h->status); - - XPT_DONE(sp, ccb); -} - -/* - | returns the lowest cmdseq that was not acked - */ -int -iscsi_requeue(isc_session_t *sp) -{ - pduq_t *pq; - u_int i, n, last; - - debug_called(8); - i = last = 0; - sp->flags |= ISC_HOLD; - while((pq = i_dqueue_hld(sp)) != NULL) { - i++; - if(pq->ccb != NULL) { - _scsi_done(sp, 0, 0x28, pq->ccb, NULL); - n = ntohl(pq->pdu.ipdu.bhs.CmdSN); - if(last==0 || (last > n)) - last = n; - sdebug(2, "last=%x n=%x", last, n); - } - pdu_free(sp->isc, pq); - } - sp->flags &= ~ISC_HOLD; - return i? last: sp->sn.cmd; -} - -int -i_pdu_flush(isc_session_t *sp) -{ - int n = 0; - pduq_t *pq; - - debug_called(8); - while((pq = i_dqueue_rsp(sp)) != NULL) { - pdu_free(sp->isc, pq); - n++; - } - while((pq = i_dqueue_rsv(sp)) != NULL) { - pdu_free(sp->isc, pq); - n++; - } - while((pq = i_dqueue_snd(sp, -1)) != NULL) { - pdu_free(sp->isc, pq); - n++; - } - while((pq = i_dqueue_hld(sp)) != NULL) { - pdu_free(sp->isc, pq); - n++; - } - while((pq = i_dqueue_wsnd(sp)) != NULL) { - pdu_free(sp->isc, pq); - n++; - } - if(n != 0) - xdebug("%d pdus recovered, should have been ZERO!", n); - return n; -} -/* - | called from ism_destroy. - */ -void -iscsi_cleanup(isc_session_t *sp) -{ - pduq_t *pq, *pqtmp; - - debug_called(8); - - TAILQ_FOREACH_SAFE(pq, &sp->hld, pq_link, pqtmp) { - sdebug(3, "hld pq=%p", pq); - if(pq->ccb) - _scsi_done(sp, 1, 0x40, pq->ccb, NULL); - TAILQ_REMOVE(&sp->hld, pq, pq_link); - if(pq->buf) { - free(pq->buf, M_ISCSIBUF); - pq->buf = NULL; - } - pdu_free(sp->isc, pq); - } - while((pq = i_dqueue_snd(sp, BIT(0)|BIT(1)|BIT(2))) != NULL) { - sdebug(3, "pq=%p", pq); - if(pq->ccb) - _scsi_done(sp, 1, 0x40, pq->ccb, NULL); - if(pq->buf) { - free(pq->buf, M_ISCSIBUF); - pq->buf = NULL; - } - pdu_free(sp->isc, pq); - } - - wakeup(&sp->rsp); -} - -void -iscsi_done(isc_session_t *sp, pduq_t *opq, pduq_t *pq) -{ - pdu_t *pp = &pq->pdu; - scsi_rsp_t *cmd = &pp->ipdu.scsi_rsp; - - debug_called(8); - - _scsi_done(sp, cmd->response, cmd->status, opq->ccb, pq); - - pdu_free(sp->isc, opq); -} - -// see RFC 3720, 10.9.1 page 146 -/* - | NOTE: - | the call to isc_stop_receiver is a kludge, - | instead, it should be handled by the userland controller, - | but that means that there should be a better way, other than - | sending a signal. Somehow, this packet should be supplied to - | the userland via read. - */ -void -iscsi_async(isc_session_t *sp, pduq_t *pq) -{ - pdu_t *pp = &pq->pdu; - async_t *cmd = &pp->ipdu.async; - - debug_called(8); - - sdebug(3, "asyncevent=0x%x asyncVCode=0x%0x", cmd->asyncEvent, cmd->asyncVCode); - switch(cmd->asyncEvent) { - case 0: // check status ... - break; - - case 1: // target request logout - isc_stop_receiver(sp); // XXX: temporary solution - break; - - case 2: // target indicates it wants to drop connection - isc_stop_receiver(sp); // XXX: temporary solution - break; - - case 3: // target indicates it will drop all connections. - isc_stop_receiver(sp); // XXX: temporary solution - break; - - case 4: // target request parameter negotiation - break; - - default: - break; - } -} - -void -iscsi_reject(isc_session_t *sp, pduq_t *opq, pduq_t *pq) -{ - union ccb *ccb = opq->ccb; - //reject_t *reject = &pq->pdu.ipdu.reject; - - debug_called(8); - //XXX: check RFC 10.17.1 (page 176) - ccb->ccb_h.status = CAM_REQ_ABORTED; - XPT_DONE(sp, ccb); - - pdu_free(sp->isc, opq); -} - -/* - | deal with lun - */ -static int -dwl(isc_session_t *sp, int lun, u_char *lp) -{ - debug_called(8); - sdebug(4, "lun=%d", lun); - /* - | mapping LUN to iSCSI LUN - | check the SAM-2 specs - | hint: maxLUNS is a small number, cam's LUN is 32bits - | iSCSI is 64bits, scsi is ? - */ - // XXX: check if this will pass the endian test - if(lun < 256) { - lp[0] = 0; - lp[1] = lun; - } else - if(lun < 16384) { - lp[0] = (1 << 5) | ((lun >> 8) & 0x3f); - lp[1] = lun & 0xff; - } - else { - xdebug("lun %d: is unsupported!", lun); - return -1; - } - - return 0; -} - -/* - | encapsulate the scsi command and - */ -int -scsi_encap(struct cam_sim *sim, union ccb *ccb) -{ - isc_session_t *sp = cam_sim_softc(sim); - struct ccb_scsiio *csio = &ccb->csio; - struct ccb_hdr *ccb_h = &ccb->ccb_h; - pduq_t *pq; - scsi_req_t *cmd; - - debug_called(8); - - debug(4, "ccb->sp=%p", ccb_h->spriv_ptr0); - sp = ccb_h->spriv_ptr0; - - if((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL) { - debug(2, "ccb->sp=%p", ccb_h->spriv_ptr0); - sdebug(1, "pdu_alloc failed sc->npdu_max=%d npdu_alloc=%d", - sp->isc->npdu_max, sp->isc->npdu_alloc); - while((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL) { - sdebug(2, "waiting..."); -#if __FreeBSD_version >= 700000 - pause("isc_encap", 5*hz); -#else - tsleep(sp->isc, 0, "isc_encap", 5*hz); -#endif - } - } - cmd = &pq->pdu.ipdu.scsi_req; - cmd->opcode = ISCSI_SCSI_CMD; - cmd->F = 1; -#if 0 -// this breaks at least Isilon's iscsi target. - /* - | map tag option, default is UNTAGGED - */ - switch(csio->tag_action) { - case MSG_SIMPLE_Q_TAG: cmd->attr = iSCSI_TASK_SIMPLE; break; - case MSG_HEAD_OF_Q_TAG: cmd->attr = iSCSI_TASK_HOFQ; break; - case MSG_ORDERED_Q_TAG: cmd->attr = iSCSI_TASK_ORDER; break; - case MSG_ACA_TASK: cmd->attr = iSCSI_TASK_ACA; break; - } -#else - cmd->attr = iSCSI_TASK_SIMPLE; -#endif - - dwl(sp, ccb_h->target_lun, (u_char *)&cmd->lun); - - if((ccb_h->flags & CAM_CDB_POINTER) != 0) { - if((ccb_h->flags & CAM_CDB_PHYS) == 0) { - if(csio->cdb_len > 16) { - sdebug(3, "oversize cdb %d > 16", csio->cdb_len); - goto invalid; - } - } - else { - sdebug(3, "not phys"); - goto invalid; - } - } - - if(csio->cdb_len > sizeof(cmd->cdb)) - xdebug("guevalt! %d > %ld", csio->cdb_len, (long)sizeof(cmd->cdb)); - - memcpy(cmd->cdb, - ccb_h->flags & CAM_CDB_POINTER? csio->cdb_io.cdb_ptr: csio->cdb_io.cdb_bytes, - csio->cdb_len); - - cmd->W = (ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT; - cmd->R = (ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN; - cmd->edtlen = htonl(csio->dxfer_len); - - pq->ccb = ccb; - /* - | place it in the out queue - */ - if(isc_qout(sp, pq) == 0) - return 1; - invalid: - ccb->ccb_h.status = CAM_REQ_INVALID; - pdu_free(sp->isc, pq); - - return 0; -} - -int -scsi_decap(isc_session_t *sp, pduq_t *opq, pduq_t *pq) -{ - union ccb *ccb = opq->ccb; - struct ccb_scsiio *csio = &ccb->csio; - pdu_t *opp = &opq->pdu; - bhs_t *bhp = &opp->ipdu.bhs; - - debug_called(8); - sdebug(6, "pq=%p opq=%p bhp->opcode=0x%x len=%d", - pq, opq, bhp->opcode, pq->pdu.ds_len); - if(ccb == NULL) { - sdebug(1, "itt=0x%x pq=%p opq=%p bhp->opcode=0x%x len=%d", - ntohl(pq->pdu.ipdu.bhs.itt), - pq, opq, bhp->opcode, pq->pdu.ds_len); - xdebug("%d] ccb == NULL!", sp->sid); - return 0; - } - if(pq->pdu.ds_len != 0) { - switch(bhp->opcode) { - case ISCSI_SCSI_CMD: { - scsi_req_t *cmd = &opp->ipdu.scsi_req; - sdebug(5, "itt=0x%x opcode=%x R=%d", - ntohl(pq->pdu.ipdu.bhs.itt), - pq->pdu.ipdu.bhs.opcode, cmd->R); - - switch(pq->pdu.ipdu.bhs.opcode) { - case ISCSI_READ_DATA: // SCSI Data in - { - caddr_t bp = NULL; // = mtod(pq->mp, caddr_t); - data_in_t *rcmd = &pq->pdu.ipdu.data_in; - - if(cmd->R) { - sdebug(5, "copy to=%p from=%p l1=%d l2=%d mp@%p", - csio->data_ptr, bp? mtod(pq->mp, caddr_t): 0, - ntohl(cmd->edtlen), pq->pdu.ds_len, pq->mp); - if(ntohl(cmd->edtlen) >= pq->pdu.ds_len) { - int offset, len = pq->pdu.ds_len; - - if(pq->mp != NULL) { - caddr_t dp; - - offset = ntohl(rcmd->bo); - dp = csio->data_ptr + offset; - i_mbufcopy(pq->mp, dp, len); - } - } - else { - xdebug("edtlen=%d < ds_len=%d", - ntohl(cmd->edtlen), pq->pdu.ds_len); - } - } - if(rcmd->S) { - /* - | contains also the SCSI Status - */ - _scsi_done(sp, 0, rcmd->status, opq->ccb, NULL); - return 0; - } else - return 1; - } - break; - } - } - default: - sdebug(3, "opcode=%02x", bhp->opcode); - break; - } - } - /* - | XXX: error ... - */ - return 1; -} diff -urN p4/freebsd/src/sys/dev/iscsi/initiator/iscsivar.h p4/iscsi/sys/dev/iscsi/initiator/iscsivar.h --- p4/freebsd/src/sys/dev/iscsi/initiator/iscsivar.h 2013-04-20 20:09:46.000000000 +0200 +++ p4/iscsi/sys/dev/iscsi/initiator/iscsivar.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,599 +0,0 @@ -/*- - * Copyright (c) 2005-2011 Daniel Braniss - * 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. - * - * 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: head/sys/dev/iscsi/initiator/iscsivar.h 234233 2012-04-13 18:21:56Z jpaetzel $ - */ - -/* - | $Id: iscsivar.h 743 2009-08-08 10:54:53Z danny $ - */ -#define ISCSI_MAX_LUNS 128 // don't touch this -#if ISCSI_MAX_LUNS > 8 -/* - | for this to work - | sysctl kern.cam.cam_srch_hi=1 - */ -#endif - -#ifndef ISCSI_INITIATOR_DEBUG -#define ISCSI_INITIATOR_DEBUG 1 -#endif - -#ifdef ISCSI_INITIATOR_DEBUG -extern int iscsi_debug; -#define debug(level, fmt, args...) do {if(level <= iscsi_debug)\ - printf("%s: " fmt "\n", __func__ , ##args);} while(0) -#define sdebug(level, fmt, args...) do {if(level <= iscsi_debug)\ - printf("%d] %s: " fmt "\n", sp->sid, __func__ , ##args);} while(0) -#define debug_called(level) do {if(level <= iscsi_debug)\ - printf("%s: called\n", __func__);} while(0) -#else -#define debug(level, fmt, args...) -#define debug_called(level) -#define sdebug(level, fmt, args...) -#endif /* ISCSI_INITIATOR_DEBUG */ - -#define xdebug(fmt, args...) printf(">>> %s: " fmt "\n", __func__ , ##args) - -#define MAX_SESSIONS ISCSI_MAX_TARGETS -#define MAX_PDUS (MAX_SESSIONS*256) // XXX: at the moment this is arbitrary - -typedef uint32_t digest_t(const void *, int len, uint32_t ocrc); - -MALLOC_DECLARE(M_ISCSI); -MALLOC_DECLARE(M_ISCSIBUF); - -#define ISOK2DIG(dig, pp) ((dig != NULL) && ((pp->ipdu.bhs.opcode & 0x1f) != ISCSI_LOGIN_CMD)) - -#ifndef BIT -#define BIT(n) (1 <<(n)) -#endif - -#define ISC_SM_RUN BIT(0) -#define ISC_SM_RUNNING BIT(1) - -#define ISC_LINK_UP BIT(2) -#define ISC_CON_RUN BIT(3) -#define ISC_CON_RUNNING BIT(4) -#define ISC_KILL BIT(5) -#define ISC_OQNOTEMPTY BIT(6) -#define ISC_OWAITING BIT(7) -#define ISC_FFPHASE BIT(8) - -#define ISC_CAMDEVS BIT(9) -#define ISC_SCANWAIT BIT(10) - -#define ISC_MEMWAIT BIT(11) -#define ISC_SIGNALED BIT(12) - -#define ISC_HOLD BIT(15) -#define ISC_HOLDED BIT(16) - -#define ISC_SHUTDOWN BIT(31) - -/* - | some stats - */ -struct i_stats { - int npdu; // number of pdus malloc'ed. - int nrecv; // unprocessed received pdus - int nsent; // sent pdus - - int nrsp, max_rsp; - int nrsv, max_rsv; - int ncsnd, max_csnd; - int nisnd, max_isnd; - int nwsnd, max_wsnd; - int nhld, max_hld; - - struct bintime t_sent; - struct bintime t_recv; -}; - -/* - | one per 'session' - */ - -typedef TAILQ_HEAD(, pduq) queue_t; - -typedef struct isc_session { - TAILQ_ENTRY(isc_session) sp_link; - int flags; - struct cdev *dev; - struct socket *soc; - struct file *fp; - struct thread *td; - - struct proc *proc; // the userland process - int signal; - struct proc *soc_proc; - struct proc *stp; // the sm thread - - struct isc_softc *isc; - - digest_t *hdrDigest; // the digest alg. if any - digest_t *dataDigest; // the digest alg. if any - - int sid; // Session ID - sn_t sn; // sequence number stuff; - int cws; // current window size - - int target_nluns; // this and target_lun are - // hopefully temporal till I - // figure out a better way. - int target_lun[ISCSI_MAX_LUNS/(sizeof(int)*8) + 1]; - - struct mtx rsp_mtx; - struct mtx rsv_mtx; - struct mtx snd_mtx; - struct mtx hld_mtx; - struct mtx io_mtx; - queue_t rsp; - queue_t rsv; - queue_t csnd; - queue_t isnd; - queue_t wsnd; - queue_t hld; - - isc_opt_t opt; // negotiable values - - struct i_stats stats; - bhs_t bhs; - struct uio uio; - struct iovec iov; - /* - | cam stuff - */ - struct cam_sim *cam_sim; - struct cam_path *cam_path; - struct mtx cam_mtx; - /* - | sysctl stuff - */ - struct sysctl_ctx_list clist; - struct sysctl_oid *oid; - int douio; //XXX: turn on/off uio on read -} isc_session_t; - -typedef struct pduq { - TAILQ_ENTRY(pduq) pq_link; - - caddr_t buf; - u_int len; // the total length of the pdu - pdu_t pdu; - union ccb *ccb; - - struct uio uio; - struct iovec iov[5]; // XXX: careful ... - struct mbuf *mp; - struct bintime ts; - queue_t *pduq; -} pduq_t; -/* - */ -struct isc_softc { - struct mtx isc_mtx; - TAILQ_HEAD(,isc_session) isc_sess; - int nsess; - struct cdev *dev; - char isid[6]; // Initiator Session ID (48 bits) - struct unrhdr *unit; - struct sx unit_sx; - - uma_zone_t pdu_zone; // pool of free pdu's - TAILQ_HEAD(,pduq) freepdu; - -#ifdef ISCSI_INITIATOR_DEBUG - int npdu_alloc, npdu_max; // for instrumentation -#endif -#ifdef DO_EVENTHANDLER - eventhandler_tag eh; -#endif - /* - | sysctl stuff - */ - struct sysctl_ctx_list clist; - struct sysctl_oid *oid; -}; - -#ifdef ISCSI_INITIATOR_DEBUG -extern struct mtx iscsi_dbg_mtx; -#endif - -void isc_start_receiver(isc_session_t *sp); -void isc_stop_receiver(isc_session_t *sp); - -int isc_sendPDU(isc_session_t *sp, pduq_t *pq); -int isc_qout(isc_session_t *sp, pduq_t *pq); -int i_prepPDU(isc_session_t *sp, pduq_t *pq); - -int ism_fullfeature(struct cdev *dev, int flag); - -int i_pdu_flush(isc_session_t *sc); -int i_setopt(isc_session_t *sp, isc_opt_t *opt); -void i_freeopt(isc_opt_t *opt); - -int ic_init(isc_session_t *sp); -void ic_destroy(isc_session_t *sp); -void ic_lost_target(isc_session_t *sp, int target); -int ic_getCamVals(isc_session_t *sp, iscsi_cam_t *cp); - -void ism_recv(isc_session_t *sp, pduq_t *pq); -int ism_start(isc_session_t *sp); -void ism_restart(isc_session_t *sp); -void ism_stop(isc_session_t *sp); - -int scsi_encap(struct cam_sim *sim, union ccb *ccb); -int scsi_decap(isc_session_t *sp, pduq_t *opq, pduq_t *pq); -void iscsi_r2t(isc_session_t *sp, pduq_t *opq, pduq_t *pq); -void iscsi_done(isc_session_t *sp, pduq_t *opq, pduq_t *pq); -void iscsi_reject(isc_session_t *sp, pduq_t *opq, pduq_t *pq); -void iscsi_async(isc_session_t *sp, pduq_t *pq); -void iscsi_cleanup(isc_session_t *sp); -int iscsi_requeue(isc_session_t *sp); - -// Serial Number Arithmetic -#define _MAXINCR 0x7FFFFFFF // 2 ^ 31 - 1 -#define SNA_GT(i1, i2) ((i1 != i2) && (\ - (i1 < i2 && i2 - i1 > _MAXINCR) ||\ - (i1 > i2 && i1 - i2 < _MAXINCR))?1: 0) - -/* - | inlines - */ -#ifdef _CAM_CAM_XPT_SIM_H - -#if __FreeBSD_version < 600000 -#define CAM_LOCK(arg) -#define CAM_ULOCK(arg) - -static __inline void -XPT_DONE(isc_session_t *sp, union ccb *ccb) -{ - mtx_lock(&Giant); - xpt_done(ccb); - mtx_unlock(&Giant); -} -#elif __FreeBSD_version >= 700000 -#define CAM_LOCK(arg) mtx_lock(&arg->cam_mtx) -#define CAM_UNLOCK(arg) mtx_unlock(&arg->cam_mtx) - -static __inline void -XPT_DONE(isc_session_t *sp, union ccb *ccb) -{ - CAM_LOCK(sp); - xpt_done(ccb); - CAM_UNLOCK(sp); -} -#else -//__FreeBSD_version >= 600000 -#define CAM_LOCK(arg) -#define CAM_UNLOCK(arg) -#define XPT_DONE(ignore, arg) xpt_done(arg) -#endif - -#endif /* _CAM_CAM_XPT_SIM_H */ - -static __inline pduq_t * -pdu_alloc(struct isc_softc *isc, int wait) -{ - pduq_t *pq; - - pq = (pduq_t *)uma_zalloc(isc->pdu_zone, wait /* M_WAITOK or M_NOWAIT*/); - if(pq == NULL) { - debug(7, "out of mem"); - return NULL; - } -#ifdef ISCSI_INITIATOR_DEBUG - mtx_lock(&iscsi_dbg_mtx); - isc->npdu_alloc++; - if(isc->npdu_alloc > isc->npdu_max) - isc->npdu_max = isc->npdu_alloc; - mtx_unlock(&iscsi_dbg_mtx); -#endif - memset(pq, 0, sizeof(pduq_t)); - - return pq; -} - -static __inline void -pdu_free(struct isc_softc *isc, pduq_t *pq) -{ - if(pq->mp) - m_freem(pq->mp); -#ifdef NO_USE_MBUF - if(pq->buf != NULL) - free(pq->buf, M_ISCSIBUF); -#endif - uma_zfree(isc->pdu_zone, pq); -#ifdef ISCSI_INITIATOR_DEBUG - mtx_lock(&iscsi_dbg_mtx); - isc->npdu_alloc--; - mtx_unlock(&iscsi_dbg_mtx); -#endif -} - -static __inline void -i_nqueue_rsp(isc_session_t *sp, pduq_t *pq) -{ - mtx_lock(&sp->rsp_mtx); - if(++sp->stats.nrsp > sp->stats.max_rsp) - sp->stats.max_rsp = sp->stats.nrsp; - TAILQ_INSERT_TAIL(&sp->rsp, pq, pq_link); - mtx_unlock(&sp->rsp_mtx); -} - -static __inline pduq_t * -i_dqueue_rsp(isc_session_t *sp) -{ - pduq_t *pq; - - mtx_lock(&sp->rsp_mtx); - if((pq = TAILQ_FIRST(&sp->rsp)) != NULL) { - sp->stats.nrsp--; - TAILQ_REMOVE(&sp->rsp, pq, pq_link); - } - mtx_unlock(&sp->rsp_mtx); - - return pq; -} - -static __inline void -i_nqueue_rsv(isc_session_t *sp, pduq_t *pq) -{ - mtx_lock(&sp->rsv_mtx); - if(++sp->stats.nrsv > sp->stats.max_rsv) - sp->stats.max_rsv = sp->stats.nrsv; - TAILQ_INSERT_TAIL(&sp->rsv, pq, pq_link); - mtx_unlock(&sp->rsv_mtx); -} - -static __inline pduq_t * -i_dqueue_rsv(isc_session_t *sp) -{ - pduq_t *pq; - - mtx_lock(&sp->rsv_mtx); - if((pq = TAILQ_FIRST(&sp->rsv)) != NULL) { - sp->stats.nrsv--; - TAILQ_REMOVE(&sp->rsv, pq, pq_link); - } - mtx_unlock(&sp->rsv_mtx); - - return pq; -} - -static __inline void -i_nqueue_csnd(isc_session_t *sp, pduq_t *pq) -{ - mtx_lock(&sp->snd_mtx); - if(++sp->stats.ncsnd > sp->stats.max_csnd) - sp->stats.max_csnd = sp->stats.ncsnd; - TAILQ_INSERT_TAIL(&sp->csnd, pq, pq_link); - mtx_unlock(&sp->snd_mtx); -} - -static __inline pduq_t * -i_dqueue_csnd(isc_session_t *sp) -{ - pduq_t *pq; - - mtx_lock(&sp->snd_mtx); - if((pq = TAILQ_FIRST(&sp->csnd)) != NULL) { - sp->stats.ncsnd--; - TAILQ_REMOVE(&sp->csnd, pq, pq_link); - } - mtx_unlock(&sp->snd_mtx); - - return pq; -} - -static __inline void -i_nqueue_isnd(isc_session_t *sp, pduq_t *pq) -{ - mtx_lock(&sp->snd_mtx); - if(++sp->stats.nisnd > sp->stats.max_isnd) - sp->stats.max_isnd = sp->stats.nisnd; - TAILQ_INSERT_TAIL(&sp->isnd, pq, pq_link); - mtx_unlock(&sp->snd_mtx); -} - -static __inline pduq_t * -i_dqueue_isnd(isc_session_t *sp) -{ - pduq_t *pq; - - mtx_lock(&sp->snd_mtx); - if((pq = TAILQ_FIRST(&sp->isnd)) != NULL) { - sp->stats.nisnd--; - TAILQ_REMOVE(&sp->isnd, pq, pq_link); - } - mtx_unlock(&sp->snd_mtx); - - return pq; -} - -static __inline void -i_nqueue_wsnd(isc_session_t *sp, pduq_t *pq) -{ - mtx_lock(&sp->snd_mtx); - if(++sp->stats.nwsnd > sp->stats.max_wsnd) - sp->stats.max_wsnd = sp->stats.nwsnd; - TAILQ_INSERT_TAIL(&sp->wsnd, pq, pq_link); - mtx_unlock(&sp->snd_mtx); -} - -static __inline pduq_t * -i_dqueue_wsnd(isc_session_t *sp) -{ - pduq_t *pq; - - mtx_lock(&sp->snd_mtx); - if((pq = TAILQ_FIRST(&sp->wsnd)) != NULL) { - sp->stats.nwsnd--; - TAILQ_REMOVE(&sp->wsnd, pq, pq_link); - } - mtx_unlock(&sp->snd_mtx); - - return pq; -} - -static __inline pduq_t * -i_dqueue_snd(isc_session_t *sp, int which) -{ - pduq_t *pq; - - pq = NULL; - mtx_lock(&sp->snd_mtx); - if((which & BIT(0)) && (pq = TAILQ_FIRST(&sp->isnd)) != NULL) { - sp->stats.nisnd--; - TAILQ_REMOVE(&sp->isnd, pq, pq_link); - pq->pduq = &sp->isnd; // remember where you came from - } else - if((which & BIT(1)) && (pq = TAILQ_FIRST(&sp->wsnd)) != NULL) { - sp->stats.nwsnd--; - TAILQ_REMOVE(&sp->wsnd, pq, pq_link); - pq->pduq = &sp->wsnd; // remember where you came from - } else - if((which & BIT(2)) && (pq = TAILQ_FIRST(&sp->csnd)) != NULL) { - sp->stats.ncsnd--; - TAILQ_REMOVE(&sp->csnd, pq, pq_link); - pq->pduq = &sp->csnd; // remember where you came from - } - mtx_unlock(&sp->snd_mtx); - - return pq; -} - -static __inline void -i_rqueue_pdu(isc_session_t *sp, pduq_t *pq) -{ - mtx_lock(&sp->snd_mtx); - KASSERT(pq->pduq != NULL, ("pq->pduq is NULL")); - TAILQ_INSERT_TAIL(pq->pduq, pq, pq_link); - mtx_unlock(&sp->snd_mtx); -} - -/* - | Waiting for ACK (or something :-) - */ -static __inline void -i_nqueue_hld(isc_session_t *sp, pduq_t *pq) -{ - getbintime(&pq->ts); - mtx_lock(&sp->hld_mtx); - if(++sp->stats.nhld > sp->stats.max_hld) - sp->stats.max_hld = sp->stats.nhld; - TAILQ_INSERT_TAIL(&sp->hld, pq, pq_link); - mtx_unlock(&sp->hld_mtx); - return; -} - -static __inline void -i_remove_hld(isc_session_t *sp, pduq_t *pq) -{ - mtx_lock(&sp->hld_mtx); - sp->stats.nhld--; - TAILQ_REMOVE(&sp->hld, pq, pq_link); - mtx_unlock(&sp->hld_mtx); -} - -static __inline pduq_t * -i_dqueue_hld(isc_session_t *sp) -{ - pduq_t *pq; - - mtx_lock(&sp->hld_mtx); - if((pq = TAILQ_FIRST(&sp->hld)) != NULL) { - sp->stats.nhld--; - TAILQ_REMOVE(&sp->hld, pq, pq_link); - } - mtx_unlock(&sp->hld_mtx); - - return pq; -} - -static __inline pduq_t * -i_search_hld(isc_session_t *sp, int itt, int keep) -{ - pduq_t *pq, *tmp; - - pq = NULL; - - mtx_lock(&sp->hld_mtx); - TAILQ_FOREACH_SAFE(pq, &sp->hld, pq_link, tmp) { - if(pq->pdu.ipdu.bhs.itt == itt) { - if(!keep) { - sp->stats.nhld--; - TAILQ_REMOVE(&sp->hld, pq, pq_link); - } - break; - } - } - mtx_unlock(&sp->hld_mtx); - - return pq; -} - -static __inline void -i_acked_hld(isc_session_t *sp, pdu_t *op) -{ - pduq_t *pq, *tmp; - u_int exp = sp->sn.expCmd; - - pq = NULL; - mtx_lock(&sp->hld_mtx); - TAILQ_FOREACH_SAFE(pq, &sp->hld, pq_link, tmp) { - if((op && op->ipdu.bhs.itt == pq->pdu.ipdu.bhs.itt) - || (pq->ccb == NULL - && (pq->pdu.ipdu.bhs.opcode != ISCSI_WRITE_DATA) - && SNA_GT(exp, ntohl(pq->pdu.ipdu.bhs.ExpStSN)))) { - sp->stats.nhld--; - TAILQ_REMOVE(&sp->hld, pq, pq_link); - pdu_free(sp->isc, pq); - } - } - mtx_unlock(&sp->hld_mtx); -} - -static __inline void -i_mbufcopy(struct mbuf *mp, caddr_t dp, int len) -{ - struct mbuf *m; - caddr_t bp; - - for(m = mp; m != NULL; m = m->m_next) { - bp = mtod(m, caddr_t); - /* - | the pdu is word (4 octed) aligned - | so len <= packet - */ - memcpy(dp, bp, MIN(len, m->m_len)); - dp += m->m_len; - len -= m->m_len; - if(len <= 0) - break; - } -} diff -urN p4/freebsd/src/sys/dev/iscsi/iscsi.c p4/iscsi/sys/dev/iscsi/iscsi.c --- p4/freebsd/src/sys/dev/iscsi/iscsi.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/sys/dev/iscsi/iscsi.c 2013-08-18 13:07:26.000000000 +0200 @@ -0,0 +1,1949 @@ +/*- + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iscsi_ioctl.h" +#include "iscsi.h" +#include "icl.h" + +#ifdef ICL_KERNEL_PROXY +#include +#endif + +/* + * XXX: This is global so the iscsi_unload() can access it. + * Think about how to do this properly. + */ +static struct iscsi_softc *sc; + +SYSCTL_NODE(_kern, OID_AUTO, iscsi, CTLFLAG_RD, 0, "iSCSI initiator"); +static int debug = 2; +TUNABLE_INT("kern.iscsi.debug", &debug); +SYSCTL_INT(_kern_iscsi, OID_AUTO, debug, CTLFLAG_RW, + &debug, 2, "Enable debug messages"); +static int ping_timeout = 5; +TUNABLE_INT("kern.iscsi.ping_timeout", &ping_timeout); +SYSCTL_INT(_kern_iscsi, OID_AUTO, ping_timeout, CTLFLAG_RW, &ping_timeout, + 5, "Timeout for ping (NOP-Out) requests, in seconds"); +static int iscsid_timeout = 60; +TUNABLE_INT("kern.iscsi.iscsid_timeout", &iscsid_timeout); +SYSCTL_INT(_kern_iscsi, OID_AUTO, iscsid_timeout, CTLFLAG_RW, &iscsid_timeout, + 60, "Time to wait for iscsid(8) to handle reconnection, in seconds"); +static int login_timeout = 60; +TUNABLE_INT("kern.iscsi.login_timeout", &login_timeout); +SYSCTL_INT(_kern_iscsi, OID_AUTO, login_timeout, CTLFLAG_RW, &login_timeout, + 60, "Time to wait for iscsid(8) to finish Login Phase, in seconds"); +static int maxtags = 255; +TUNABLE_INT("kern.iscsi.maxtags", &maxtags); +SYSCTL_INT(_kern_iscsi, OID_AUTO, maxtags, CTLFLAG_RW, &maxtags, + 255, "Max number of IO requests queued"); + +MALLOC_DEFINE(M_ISCSI, "iSCSI", "iSCSI initiator"); +static uma_zone_t iscsi_outstanding_zone; + +#define CONN_SESSION(X) ((struct iscsi_session *)X->ic_prv0) +#define PDU_SESSION(X) (CONN_SESSION(X->ip_conn)) + +#define ISCSI_DEBUG(X, ...) \ + if (debug > 1) { \ + printf("%s: " X "\n", __func__, ## __VA_ARGS__);\ + } while (0) + +#define ISCSI_WARN(X, ...) \ + if (debug > 0) { \ + printf("WARNING: %s: " X "\n", \ + __func__, ## __VA_ARGS__); \ + } while (0) + +#define ISCSI_SESSION_DEBUG(S, X, ...) \ + if (debug > 1) { \ + printf("%s: %s (%s): " X "\n", \ + __func__, S->is_desc.isd_target_addr, \ + S->is_desc.isd_target, ## __VA_ARGS__); \ + } while (0) + +#define ISCSI_SESSION_WARN(S, X, ...) \ + if (debug > 0) { \ + printf("WARNING: %s (%s): " X "\n", \ + S->is_desc.isd_target_addr, \ + S->is_desc.isd_target, ## __VA_ARGS__); \ + } while (0) + +#define ISCSI_SESSION_LOCK(X) mtx_lock(&X->is_lock) +#define ISCSI_SESSION_UNLOCK(X) mtx_unlock(&X->is_lock) +#define ISCSI_SESSION_LOCK_ASSERT(X) mtx_assert(&X->is_lock, MA_OWNED) + +static int iscsi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, + int mode, struct thread *td); + +static struct cdevsw iscsi_cdevsw = { + .d_version = D_VERSION, + .d_ioctl = iscsi_ioctl, + .d_name = "iscsi", +}; + +static void iscsi_pdu_queue_locked(struct icl_pdu *request); +static void iscsi_pdu_queue(struct icl_pdu *request); +static void iscsi_pdu_update_statsn(const struct icl_pdu *response); +static void iscsi_pdu_handle_nop_in(struct icl_pdu *response); +static void iscsi_pdu_handle_scsi_response(struct icl_pdu *response); +static void iscsi_pdu_handle_data_in(struct icl_pdu *response); +static void iscsi_pdu_handle_logout_response(struct icl_pdu *response); +static void iscsi_pdu_handle_r2t(struct icl_pdu *response); +static void iscsi_pdu_handle_async_message(struct icl_pdu *response); +static void iscsi_pdu_handle_reject(struct icl_pdu *response); +static void iscsi_session_reconnect(struct iscsi_session *is); +static void iscsi_session_terminate(struct iscsi_session *is); +static void iscsi_action(struct cam_sim *sim, union ccb *ccb); +static void iscsi_poll(struct cam_sim *sim); +static struct iscsi_outstanding *iscsi_outstanding_find(struct iscsi_session *is, + uint32_t initiator_task_tag); +static int iscsi_outstanding_add(struct iscsi_session *is, + uint32_t initiator_task_tag, union ccb *ccb); +static void iscsi_outstanding_remove(struct iscsi_session *is, + struct iscsi_outstanding *io); + +static bool +iscsi_pdu_prepare(struct icl_pdu *request) +{ + struct iscsi_session *is; + struct iscsi_bhs_scsi_command *bhssc; + + is = PDU_SESSION(request); + + ISCSI_SESSION_LOCK_ASSERT(is); + + /* + * We're only using fields common for all the request + * (initiator -> target) PDUs. + */ + bhssc = (struct iscsi_bhs_scsi_command *)request->ip_bhs; + + /* + * Data-Out PDU does not contain CmdSN. + */ + if (bhssc->bhssc_opcode != ISCSI_BHS_OPCODE_SCSI_DATA_OUT) { + if (is->is_cmdsn > is->is_maxcmdsn && + (bhssc->bhssc_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0) { + /* + * Current MaxCmdSN prevents us from sending any more + * SCSI Command PDUs to the target; postpone the PDU. + * It will get resent by either iscsi_pdu_queue(), + * or by maintenance thread. + */ +#if 0 + ISCSI_SESSION_DEBUG(is, "postponing send, CmdSN %d, ExpCmdSN %d, MaxCmdSN %d, opcode 0x%x", + is->is_cmdsn, is->is_expcmdsn, is->is_maxcmdsn, bhssc->bhssc_opcode); +#endif + return (true); + } + bhssc->bhssc_cmdsn = htonl(is->is_cmdsn); + if ((bhssc->bhssc_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0) + is->is_cmdsn++; + } + bhssc->bhssc_expstatsn = htonl(is->is_statsn + 1); + + return (false); +} + +static void +iscsi_session_send_postponed(struct iscsi_session *is) +{ + struct icl_pdu *request; + bool postpone; + + ISCSI_SESSION_LOCK_ASSERT(is); + + while (!TAILQ_EMPTY(&is->is_postponed)) { + request = TAILQ_FIRST(&is->is_postponed); + postpone = iscsi_pdu_prepare(request); + if (postpone) + break; + TAILQ_REMOVE(&is->is_postponed, request, ip_next); + icl_pdu_queue(request); + } +} + +static void +iscsi_pdu_queue_locked(struct icl_pdu *request) +{ + struct iscsi_session *is; + bool postpone; + + is = PDU_SESSION(request); + ISCSI_SESSION_LOCK_ASSERT(is); + iscsi_session_send_postponed(is); + postpone = iscsi_pdu_prepare(request); + if (postpone) { + TAILQ_INSERT_TAIL(&is->is_postponed, request, ip_next); + return; + } + icl_pdu_queue(request); +} + +static void +iscsi_pdu_queue(struct icl_pdu *request) +{ + struct iscsi_session *is; + + is = PDU_SESSION(request); + ISCSI_SESSION_LOCK(is); + iscsi_pdu_queue_locked(request); + ISCSI_SESSION_UNLOCK(is); +} + +static void +iscsi_session_logout(struct iscsi_session *is) +{ + struct icl_pdu *request; + struct iscsi_bhs_logout_request *bhslr; + + request = icl_pdu_new_bhs(is->is_conn, M_NOWAIT); + if (request == NULL) + return; + + bhslr = (struct iscsi_bhs_logout_request *)request->ip_bhs; + bhslr->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_REQUEST; + bhslr->bhslr_reason = BHSLR_REASON_CLOSE_SESSION; + iscsi_pdu_queue_locked(request); +} + +static void +iscsi_session_terminate_tasks(struct iscsi_session *is, bool requeue) +{ + struct iscsi_outstanding *io, *tmp; + + ISCSI_SESSION_LOCK_ASSERT(is); + + TAILQ_FOREACH_SAFE(io, &is->is_outstanding, io_next, tmp) { + if (requeue) { + io->io_ccb->ccb_h.status &= ~CAM_SIM_QUEUED; + io->io_ccb->ccb_h.status |= CAM_REQUEUE_REQ; + } else { + io->io_ccb->ccb_h.status = CAM_REQ_ABORTED; + } + xpt_done(io->io_ccb); + iscsi_outstanding_remove(is, io); + } +} + +static void +iscsi_maintenance_thread_reconnect(struct iscsi_session *is) +{ + struct icl_pdu *pdu; + + icl_conn_shutdown(is->is_conn); + icl_conn_close(is->is_conn); + + ISCSI_SESSION_LOCK(is); + +#ifdef ICL_KERNEL_PROXY + if (is->is_login_pdu != NULL) { + icl_pdu_free(is->is_login_pdu); + is->is_login_pdu = NULL; + } + cv_signal(&is->is_login_cv); +#endif + + /* + * Don't queue any new PDUs. + */ + if (is->is_sim != NULL && is->is_simq_frozen == false) { + ISCSI_SESSION_DEBUG(is, "freezing"); + xpt_freeze_simq(is->is_sim, 1); + is->is_simq_frozen = true; + } + + /* + * Remove postponed PDUs. + */ + while (!TAILQ_EMPTY(&is->is_postponed)) { + pdu = TAILQ_FIRST(&is->is_postponed); + TAILQ_REMOVE(&is->is_postponed, pdu, ip_next); + icl_pdu_free(pdu); + } + + /* + * Terminate SCSI tasks, asking CAM to requeue them. + */ + //ISCSI_SESSION_DEBUG(is, "terminating tasks"); + iscsi_session_terminate_tasks(is, true); + + KASSERT(TAILQ_EMPTY(&is->is_outstanding), + ("destroying session with active tasks")); + KASSERT(TAILQ_EMPTY(&is->is_postponed), + ("destroying session with postponed PDUs")); + + /* + * Request immediate reconnection from iscsid(8). + */ + //ISCSI_SESSION_DEBUG(is, "waking up iscsid(8)"); + is->is_desc.isd_connected = 0; + is->is_reconnecting = false; + is->is_login_phase = false; + is->is_waiting_for_iscsid = true; + is->is_timeout = 0; + ISCSI_SESSION_UNLOCK(is); + cv_signal(&is->is_softc->sc_cv); +} + +static void +iscsi_maintenance_thread_terminate(struct iscsi_session *is) +{ + struct iscsi_softc *sc; + struct icl_pdu *pdu; + + sc = is->is_softc; + sx_xlock(&sc->sc_lock); + TAILQ_REMOVE(&sc->sc_sessions, is, is_next); + sx_xunlock(&sc->sc_lock); + + icl_conn_close(is->is_conn); + + ISCSI_SESSION_LOCK(is); + + KASSERT(is->is_terminating, ("is_terminating == false")); + +#ifdef ICL_KERNEL_PROXY + if (is->is_login_pdu != NULL) { + icl_pdu_free(is->is_login_pdu); + is->is_login_pdu = NULL; + } + cv_signal(&is->is_login_cv); +#endif + + /* + * Don't queue any new PDUs. + */ + callout_drain(&is->is_callout); + if (is->is_sim != NULL && is->is_simq_frozen == false) { + ISCSI_SESSION_DEBUG(is, "freezing"); + xpt_freeze_simq(is->is_sim, 1); + is->is_simq_frozen = true; + } + + /* + * Remove postponed PDUs. + */ + while (!TAILQ_EMPTY(&is->is_postponed)) { + pdu = TAILQ_FIRST(&is->is_postponed); + TAILQ_REMOVE(&is->is_postponed, pdu, ip_next); + icl_pdu_free(pdu); + } + + /* + * Forcibly terminate SCSI tasks. + */ + ISCSI_SESSION_DEBUG(is, "terminating tasks"); + iscsi_session_terminate_tasks(is, false); + + /* + * Deregister CAM. + */ + if (is->is_sim != NULL) { + ISCSI_SESSION_DEBUG(is, "deregistering SIM"); + xpt_async(AC_LOST_DEVICE, is->is_path, NULL); + + if (is->is_simq_frozen) { + xpt_release_simq(is->is_sim, 1); + is->is_simq_frozen = false; + } + + xpt_free_path(is->is_path); + xpt_bus_deregister(cam_sim_path(is->is_sim)); + cam_sim_free(is->is_sim, TRUE /*free_devq*/); + is->is_sim = NULL; + } + + KASSERT(TAILQ_EMPTY(&is->is_outstanding), + ("destroying session with active tasks")); + KASSERT(TAILQ_EMPTY(&is->is_postponed), + ("destroying session with postponed PDUs")); + + ISCSI_SESSION_UNLOCK(is); + + icl_conn_free(is->is_conn); + mtx_destroy(&is->is_lock); + cv_destroy(&is->is_maintenance_cv); +#ifdef ICL_KERNEL_PROXY + cv_destroy(&is->is_login_cv); +#endif + ISCSI_SESSION_DEBUG(is, "terminated"); + free(is, M_ISCSI); + + /* + * The iscsi_unload() routine might be waiting. + */ + cv_signal(&sc->sc_cv); +} + +static void +iscsi_maintenance_thread(void *arg) +{ + struct iscsi_session *is; + + is = arg; + + for (;;) { + ISCSI_SESSION_LOCK(is); + if (is->is_reconnecting == false && + is->is_terminating == false && + TAILQ_EMPTY(&is->is_postponed)) + cv_wait(&is->is_maintenance_cv, &is->is_lock); + ISCSI_SESSION_UNLOCK(is); + + if (is->is_reconnecting) { + iscsi_maintenance_thread_reconnect(is); + continue; + } + + if (is->is_terminating) { + iscsi_maintenance_thread_terminate(is); + kthread_exit(); + return; + } + + ISCSI_SESSION_LOCK(is); + iscsi_session_send_postponed(is); + ISCSI_SESSION_UNLOCK(is); + } +} + +static void +iscsi_session_reconnect(struct iscsi_session *is) +{ + + /* + * XXX: We can't use locking here, because + * it's being called from various contexts. + * Hope it doesn't break anything. + */ + if (is->is_reconnecting) + return; + + is->is_reconnecting = true; + cv_signal(&is->is_maintenance_cv); +} + +static void +iscsi_session_terminate(struct iscsi_session *is) +{ + if (is->is_terminating) + return; + + is->is_terminating = true; + +#if 0 + iscsi_session_logout(is); +#endif + cv_signal(&is->is_maintenance_cv); +} + +static void +iscsi_callout(void *context) +{ + struct icl_pdu *request; + struct iscsi_bhs_nop_out *bhsno; + struct iscsi_session *is; + bool reconnect_needed = false; + + is = context; + + if (is->is_terminating) + return; + + callout_schedule(&is->is_callout, 1 * hz); + + ISCSI_SESSION_LOCK(is); + is->is_timeout++; + + if (is->is_waiting_for_iscsid) { + if (is->is_timeout > iscsid_timeout) { + ISCSI_SESSION_WARN(is, "timed out waiting for iscsid(8) " + "for %d seconds; reconnecting", + is->is_timeout); + reconnect_needed = true; + } + goto out; + } + + if (is->is_login_phase) { + if (is->is_timeout > login_timeout) { + ISCSI_SESSION_WARN(is, "login timed out after %d seconds; " + "reconnecting", is->is_timeout); + reconnect_needed = true; + } + goto out; + } + + if (is->is_timeout >= ping_timeout) { + ISCSI_SESSION_WARN(is, "no ping reply (NOP-In) after %d seconds; " + "reconnecting", ping_timeout); + reconnect_needed = true; + goto out; + } + + ISCSI_SESSION_UNLOCK(is); + + /* + * If the ping was reset less than one second ago - which means + * that we've received some PDU during the last second - assume + * the traffic flows correctly and don't bother sending a NOP-Out. + * + * (It's 2 - one for one second, and one for incrementing is_timeout + * earlier in this routine.) + */ + if (is->is_timeout < 2) + return; + + request = icl_pdu_new_bhs(is->is_conn, M_WAITOK); + bhsno = (struct iscsi_bhs_nop_out *)request->ip_bhs; + bhsno->bhsno_opcode = ISCSI_BHS_OPCODE_NOP_OUT | + ISCSI_BHS_OPCODE_IMMEDIATE; + bhsno->bhsno_flags = 0x80; + bhsno->bhsno_target_transfer_tag = 0xffffffff; + iscsi_pdu_queue(request); + return; + +out: + ISCSI_SESSION_UNLOCK(is); + + if (reconnect_needed) + iscsi_session_reconnect(is); +} + +static void +iscsi_pdu_update_statsn(const struct icl_pdu *response) +{ + const struct iscsi_bhs_data_in *bhsdi; + struct iscsi_session *is; + uint32_t expcmdsn, maxcmdsn; + + is = PDU_SESSION(response); + + ISCSI_SESSION_LOCK_ASSERT(is); + + /* + * We're only using fields common for all the response + * (target -> initiator) PDUs. + */ + bhsdi = (const struct iscsi_bhs_data_in *)response->ip_bhs; + /* + * Ok, I lied. In case of Data-In, "The fields StatSN, Status, + * and Residual Count only have meaningful content if the S bit + * is set to 1", so we also need to check the bit specific for + * Data-In PDU. + */ + if (bhsdi->bhsdi_opcode != ISCSI_BHS_OPCODE_SCSI_DATA_IN || + (bhsdi->bhsdi_flags & BHSDI_FLAGS_S) != 0) { + if (ntohl(bhsdi->bhsdi_statsn) < is->is_statsn) { + ISCSI_SESSION_WARN(is, + "PDU StatSN %d >= session StatSN %d, opcode 0x%x", + is->is_statsn, ntohl(bhsdi->bhsdi_statsn), + bhsdi->bhsdi_opcode); + } + is->is_statsn = ntohl(bhsdi->bhsdi_statsn); + } + + expcmdsn = ntohl(bhsdi->bhsdi_expcmdsn); + maxcmdsn = ntohl(bhsdi->bhsdi_maxcmdsn); + + /* + * XXX: Compare using Serial Arithmetic Sense. + */ + if (maxcmdsn + 1 < expcmdsn) { + ISCSI_SESSION_DEBUG(is, "PDU MaxCmdSN %d + 1 < PDU ExpCmdSN %d; ignoring", + maxcmdsn, expcmdsn); + } else { + if (maxcmdsn > is->is_maxcmdsn) { + is->is_maxcmdsn = maxcmdsn; + + /* + * Command window increased; kick the maintanance thread + * to send out postponed commands. + */ + cv_signal(&is->is_maintenance_cv); + } else if (maxcmdsn < is->is_maxcmdsn) { + ISCSI_SESSION_DEBUG(is, "PDU MaxCmdSN %d < session MaxCmdSN %d; ignoring", + maxcmdsn, is->is_maxcmdsn); + } + + if (expcmdsn > is->is_expcmdsn) { + is->is_expcmdsn = expcmdsn; + } else if (expcmdsn < is->is_expcmdsn) { + ISCSI_SESSION_DEBUG(is, "PDU ExpCmdSN %d < session ExpCmdSN %d; ignoring", + expcmdsn, is->is_expcmdsn); + } + } + + /* + * Every incoming PDU - not just NOP-In - resets the ping timer. + * The purpose of the timeout is to reset the connection when it stalls; + * we don't want this to happen when NOP-In or NOP-Out ends up delayed + * in some queue. + */ + is->is_timeout = 0; +} + +static void +iscsi_receive_callback(struct icl_pdu *response) +{ + struct iscsi_session *is; + + is = PDU_SESSION(response); + + ISCSI_SESSION_LOCK(is); + +#ifdef ICL_KERNEL_PROXY + if (is->is_login_phase) { + if (is->is_login_pdu == NULL) + is->is_login_pdu = response; + else + icl_pdu_free(response); + ISCSI_SESSION_UNLOCK(is); + cv_signal(&is->is_login_cv); + return; + } +#endif + + iscsi_pdu_update_statsn(response); + + /* + * The handling routine is responsible for freeing the PDU + * when it's no longer needed. + */ + switch (response->ip_bhs->bhs_opcode) { + case ISCSI_BHS_OPCODE_NOP_IN: + iscsi_pdu_handle_nop_in(response); + break; + case ISCSI_BHS_OPCODE_SCSI_RESPONSE: + iscsi_pdu_handle_scsi_response(response); + break; + case ISCSI_BHS_OPCODE_SCSI_DATA_IN: + iscsi_pdu_handle_data_in(response); + break; + case ISCSI_BHS_OPCODE_LOGOUT_RESPONSE: + iscsi_pdu_handle_logout_response(response); + break; + case ISCSI_BHS_OPCODE_R2T: + iscsi_pdu_handle_r2t(response); + break; + case ISCSI_BHS_OPCODE_ASYNC_MESSAGE: + iscsi_pdu_handle_async_message(response); + break; + case ISCSI_BHS_OPCODE_REJECT: + iscsi_pdu_handle_reject(response); + break; + default: + ISCSI_SESSION_WARN(is, "received PDU with unsupported " + "opcode 0x%x; reconnecting", + response->ip_bhs->bhs_opcode); + iscsi_session_reconnect(is); + icl_pdu_free(response); + } + + ISCSI_SESSION_UNLOCK(is); +} + +static void +iscsi_error_callback(struct icl_conn *ic) +{ + struct iscsi_session *is; + + is = CONN_SESSION(ic); + + ISCSI_SESSION_WARN(is, "connection error; reconnecting"); + iscsi_session_reconnect(is); +} + +static void +iscsi_pdu_handle_nop_in(struct icl_pdu *response) +{ + struct iscsi_bhs_nop_out *bhsno; + struct iscsi_bhs_nop_in *bhsni; + struct icl_pdu *request; + + bhsni = (struct iscsi_bhs_nop_in *)response->ip_bhs; + + if (bhsni->bhsni_target_transfer_tag == 0xffffffff) { + /* + * Nothing to do; iscsi_pdu_update_statsn() already + * zeroed the timeout. + */ + icl_pdu_free(response); + return; + } + + request = icl_pdu_new_bhs(response->ip_conn, M_NOWAIT); + if (request == NULL) { + icl_pdu_free(response); + return; + } + bhsno = (struct iscsi_bhs_nop_out *)request->ip_bhs; + bhsno->bhsno_opcode = ISCSI_BHS_OPCODE_NOP_OUT | + ISCSI_BHS_OPCODE_IMMEDIATE; + bhsno->bhsno_flags = 0x80; + bhsno->bhsno_initiator_task_tag = 0xffffffff; /* XXX */ + bhsno->bhsno_target_transfer_tag = bhsni->bhsni_target_transfer_tag; + + request->ip_data_len = response->ip_data_len; + request->ip_data_mbuf = response->ip_data_mbuf; + response->ip_data_len = 0; + response->ip_data_mbuf = NULL; + + icl_pdu_free(response); + iscsi_pdu_queue_locked(request); +} + +static void +iscsi_pdu_handle_scsi_response(struct icl_pdu *response) +{ + struct iscsi_bhs_scsi_response *bhssr; + struct iscsi_outstanding *io; + struct iscsi_session *is; + uint16_t sense_len; + size_t data_segment_len; + + is = PDU_SESSION(response); + + bhssr = (struct iscsi_bhs_scsi_response *)response->ip_bhs; + io = iscsi_outstanding_find(is, bhssr->bhssr_initiator_task_tag); + if (io == NULL) { + ISCSI_SESSION_WARN(is, "bad itt 0x%x", bhssr->bhssr_initiator_task_tag); + icl_pdu_free(response); + iscsi_session_reconnect(is); + return; + } + + if (bhssr->bhssr_response != BHSSR_RESPONSE_COMMAND_COMPLETED) { + ISCSI_SESSION_WARN(is, "bad response 0x%x", bhssr->bhssr_response); + io->io_ccb->ccb_h.status = CAM_REQ_CMP_ERR; + } else if (bhssr->bhssr_status == 0) { + io->io_ccb->ccb_h.status = CAM_REQ_CMP; + } else { + io->io_ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; + io->io_ccb->csio.scsi_status = bhssr->bhssr_status; + } + + data_segment_len = icl_pdu_data_segment_length(response); + if (data_segment_len > 0) { + if (data_segment_len < 2) { + ISCSI_SESSION_WARN(is, "truncated data segment (%zd bytes)", + data_segment_len); + io->io_ccb->ccb_h.status = CAM_REQ_CMP_ERR; + goto out; + } + icl_pdu_get_data(response, 0, &sense_len, sizeof(sense_len)); + sense_len = ntohs(sense_len); +#if 0 + ISCSI_SESSION_DEBUG(is, "sense_len %d, data len %zd", + sense_len, data_segment_len); +#endif + if (sizeof(sense_len) + sense_len > data_segment_len) { + ISCSI_SESSION_WARN(is, "truncated data segment " + "(%zd bytes, should be %ld)", + data_segment_len, sizeof(sense_len) + sense_len); + io->io_ccb->ccb_h.status = CAM_REQ_CMP_ERR; + goto out; + } else if (sizeof(sense_len) + sense_len > data_segment_len) + ISCSI_SESSION_WARN(is, "oversize data segment " + "(%ld bytes, should be %zd)", + data_segment_len, sizeof(sense_len) + sense_len); + if (sense_len > io->io_ccb->csio.sense_len) { + ISCSI_SESSION_DEBUG(is, "truncating sense from %d to %d", + sense_len, io->io_ccb->csio.sense_len); + sense_len = io->io_ccb->csio.sense_len; + } + icl_pdu_get_data(response, sizeof(sense_len), &io->io_ccb->csio.sense_data, sense_len); + io->io_ccb->ccb_h.status |= CAM_AUTOSNS_VALID; + } + +out: + xpt_done(io->io_ccb); + iscsi_outstanding_remove(is, io); + icl_pdu_free(response); +} + +static void +iscsi_pdu_handle_data_in(struct icl_pdu *response) +{ + struct iscsi_bhs_data_in *bhsdi; + struct iscsi_outstanding *io; + struct iscsi_session *is; + struct ccb_scsiio *csio; + size_t data_segment_len; + + is = PDU_SESSION(response); + bhsdi = (struct iscsi_bhs_data_in *)response->ip_bhs; + io = iscsi_outstanding_find(is, bhsdi->bhsdi_initiator_task_tag); + if (io == NULL) { + ISCSI_SESSION_WARN(is, "bad itt 0x%x", bhsdi->bhsdi_initiator_task_tag); + icl_pdu_free(response); + iscsi_session_reconnect(is); + return; + } + + data_segment_len = icl_pdu_data_segment_length(response); + if (data_segment_len == 0) { + /* + * "The sending of 0 length data segments should be avoided, + * but initiators and targets MUST be able to properly receive + * 0 length data segments." + */ + icl_pdu_free(response); + return; + } + + csio = &io->io_ccb->csio; + + if (ntohl(bhsdi->bhsdi_buffer_offset) + data_segment_len > + csio->dxfer_len) { + ISCSI_SESSION_WARN(is, "oversize data segment (%zd bytes " + "at offset %d, buffer is %d)", + data_segment_len, ntohl(bhsdi->bhsdi_buffer_offset), + csio->dxfer_len); + icl_pdu_free(response); + iscsi_session_reconnect(is); + } + + icl_pdu_get_data(response, 0, csio->data_ptr + ntohl(bhsdi->bhsdi_buffer_offset), data_segment_len); + + /* + * XXX: Check DataSN. + * XXX: Check F. + * XXX: How to pass overflow/underflow information to CAM? + */ + if (bhsdi->bhsdi_flags & BHSDI_FLAGS_S) { + //ISCSI_SESSION_DEBUG(is, "got S flag; status 0x%x", bhsdi->bhsdi_status); + if (bhsdi->bhsdi_status == 0) { + io->io_ccb->ccb_h.status = CAM_REQ_CMP; + } else { + io->io_ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; + csio->scsi_status = bhsdi->bhsdi_status; + } + xpt_done(io->io_ccb); + iscsi_outstanding_remove(is, io); + } + + icl_pdu_free(response); +} + +static void +iscsi_pdu_handle_logout_response(struct icl_pdu *response) +{ + + ISCSI_SESSION_DEBUG(PDU_SESSION(response), "logout response"); + icl_pdu_free(response); +} + +static void +iscsi_pdu_handle_r2t(struct icl_pdu *response) +{ + struct icl_pdu *request; + struct iscsi_session *is; + struct iscsi_bhs_r2t *bhsr2t; + struct iscsi_bhs_data_out *bhsdo; + struct iscsi_outstanding *io; + struct ccb_scsiio *csio; + size_t off, len, total_len; + + is = PDU_SESSION(response); + + bhsr2t = (struct iscsi_bhs_r2t *)response->ip_bhs; + io = iscsi_outstanding_find(is, bhsr2t->bhsr2t_initiator_task_tag); + if (io == NULL) { + ISCSI_SESSION_WARN(is, "bad itt 0x%x; reconnecting", + bhsr2t->bhsr2t_initiator_task_tag); + icl_pdu_free(response); + iscsi_session_reconnect(is); + return; + } + + csio = &io->io_ccb->csio; + + if ((csio->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_OUT) { + ISCSI_SESSION_WARN(is, "received R2T for read command; reconnecting"); + icl_pdu_free(response); + iscsi_session_reconnect(is); + return; + } + + /* + * XXX: Verify R2TSN. + */ + + io->io_datasn = 0; + off = ntohl(bhsr2t->bhsr2t_buffer_offset); + total_len = ntohl(bhsr2t->bhsr2t_desired_data_transfer_length); + + //ISCSI_SESSION_DEBUG(is, "r2t; off %zd, len %zd", off, total_len); + + for (;;) { + len = total_len; + + if (len > is->is_desc.isd_max_data_segment_length) + len = is->is_desc.isd_max_data_segment_length; + + if (off + len > csio->dxfer_len) { + ISCSI_SESSION_WARN(is, "bad off %zd, len %d", + off + len, csio->dxfer_len); + icl_pdu_free(response); + iscsi_session_reconnect(is); + } + + request = icl_pdu_new_bhs(response->ip_conn, M_NOWAIT); + if (request == NULL) { + icl_pdu_free(response); + iscsi_session_reconnect(is); + return; + } + + bhsdo = (struct iscsi_bhs_data_out *)request->ip_bhs; + bhsdo->bhsdo_opcode = ISCSI_BHS_OPCODE_SCSI_DATA_OUT; + bhsdo->bhsdo_lun = bhsr2t->bhsr2t_lun; + bhsdo->bhsdo_initiator_task_tag = + bhsr2t->bhsr2t_initiator_task_tag; + bhsdo->bhsdo_target_transfer_tag = + bhsr2t->bhsr2t_target_transfer_tag; + bhsdo->bhsdo_datasn = htonl(io->io_datasn++); + bhsdo->bhsdo_buffer_offset = htonl(off); + /* + * XXX: Ugly. + */ + ISCSI_SESSION_UNLOCK(is); + icl_pdu_append_data(request, csio->data_ptr + off, len); + ISCSI_SESSION_LOCK(is); + + off += len; + total_len -= len; + + if (total_len == 0) { + bhsdo->bhsdo_flags |= BHSDO_FLAGS_F; + //ISCSI_SESSION_DEBUG(is, "setting F, off %zd", off); + } else { + //ISCSI_SESSION_DEBUG(is, "not finished, off %zd", off); + } + + iscsi_pdu_queue_locked(request); + + if (total_len == 0) + break; + } + + icl_pdu_free(response); +} + +static void +iscsi_pdu_handle_async_message(struct icl_pdu *response) +{ + struct iscsi_bhs_asynchronous_message *bhsam; + struct iscsi_session *is; + + is = PDU_SESSION(response); + bhsam = (struct iscsi_bhs_asynchronous_message *)response->ip_bhs; + switch (bhsam->bhsam_async_event) { + case BHSAM_EVENT_TARGET_REQUESTS_LOGOUT: + ISCSI_SESSION_WARN(is, "target requests logout; removing session"); + iscsi_session_logout(is); + iscsi_session_terminate(is); + break; + case BHSAM_EVENT_TARGET_TERMINATES_CONNECTION: + ISCSI_SESSION_DEBUG(is, "target indicates it will drop drop the connection"); + break; + case BHSAM_EVENT_TARGET_TERMINATES_SESSION: + ISCSI_SESSION_DEBUG(is, "target indicates it will drop drop the session"); + break; + default: + /* + * XXX: Technically, we're obligated to also handle + * parameter renegotiation. + */ + ISCSI_SESSION_WARN(is, "ignoring AsyncEvent %d", bhsam->bhsam_async_event); + break; + } + + icl_pdu_free(response); +} + +static void +iscsi_pdu_handle_reject(struct icl_pdu *response) +{ + struct iscsi_bhs_reject *bhsr; + struct iscsi_session *is; + + is = PDU_SESSION(response); + bhsr = (struct iscsi_bhs_reject *)response->ip_bhs; + ISCSI_SESSION_WARN(is, "received Reject PDU, reason 0x%x; protocol error?", + bhsr->bhsr_reason); + + icl_pdu_free(response); +} + +static int +iscsi_ioctl_daemon_wait(struct iscsi_softc *sc, + struct iscsi_daemon_request *request) +{ + struct iscsi_session *is; + int error; + + sx_slock(&sc->sc_lock); + for (;;) { + TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { + if (is->is_waiting_for_iscsid) + break; + } + + if (is == NULL) { + /* + * No session requires attention from iscsid(8); wait. + */ + error = cv_wait_sig(&sc->sc_cv, &sc->sc_lock); + if (error != 0) { + sx_sunlock(&sc->sc_lock); + return (error); + } + continue; + } + + ISCSI_SESSION_LOCK(is); + is->is_waiting_for_iscsid = false; + is->is_login_phase = true; + ISCSI_SESSION_UNLOCK(is); + + request->idr_cmd = ISCSI_DAEMON_CMD_LOGIN; + memcpy(&request->idr_desc, &is->is_desc, + sizeof(request->idr_desc)); + + sx_sunlock(&sc->sc_lock); + return (0); + } +} + +static int +iscsi_ioctl_daemon_handoff(struct iscsi_softc *sc, + struct iscsi_daemon_handoff *handoff) +{ + struct iscsi_session *is; + int error; + + sx_slock(&sc->sc_lock); + + /* + * Find the session to hand off socket to. + */ + TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { + if (is->is_desc.isd_id == handoff->idh_session_id) + break; + } + if (is == NULL) { + sx_sunlock(&sc->sc_lock); + return (ESRCH); + } + ISCSI_SESSION_LOCK(is); + if (is->is_desc.isd_discovery || is->is_terminating) { + ISCSI_SESSION_UNLOCK(is); + sx_sunlock(&sc->sc_lock); + return (EINVAL); + } + + strlcpy(is->is_desc.isd_target_alias, handoff->idh_target_alias, + sizeof(is->is_desc.isd_target_alias)); + /* + * XXX: This is ugly. Find a better way to pass those to iscsictl(8), + * and put them into struct iscsi_session. + */ + is->is_desc.isd_immediate_data = handoff->idh_immediate_data; + is->is_desc.isd_max_data_segment_length = handoff->idh_max_data_segment_length; + is->is_desc.isd_connected = 1; + + is->is_cmdsn = 0; + is->is_expcmdsn = 1; + is->is_maxcmdsn = 1; + is->is_statsn = handoff->idh_statsn; + is->is_initial_r2t = handoff->idh_initial_r2t; + is->is_max_burst_length = handoff->idh_max_burst_length; + is->is_first_burst_length = handoff->idh_first_burst_length; + is->is_waiting_for_iscsid = false; + is->is_login_phase = false; + is->is_timeout = 0; + + memcpy(is->is_isid, handoff->idh_isid, sizeof(is->is_isid)); + + ISCSI_SESSION_UNLOCK(is); + + if (handoff->idh_header_digest == ISCSI_DIGEST_CRC32C) + is->is_conn->ic_header_crc32c = true; + if (handoff->idh_data_digest == ISCSI_DIGEST_CRC32C) + is->is_conn->ic_data_crc32c = true; + +#ifndef ICL_KERNEL_PROXY + error = icl_conn_handoff(is->is_conn, handoff->idh_socket); + if (error != 0) { + sx_sunlock(&sc->sc_lock); + iscsi_session_terminate(is); + return (error); + } +#endif + + sx_sunlock(&sc->sc_lock); + + if (is->is_sim != NULL) { + /* + * When reconnecting, there already is SIM allocated for the session. + */ + KASSERT(is->is_simq_frozen, ("reconnect without frozen simq")); + ISCSI_SESSION_LOCK(is); + ISCSI_SESSION_DEBUG(is, "releasing"); + xpt_release_simq(is->is_sim, 1); + is->is_simq_frozen = false; + ISCSI_SESSION_UNLOCK(is); + + } else { + ISCSI_SESSION_LOCK(is); + is->is_devq = cam_simq_alloc(maxtags); + if (is->is_devq == NULL) { + ISCSI_SESSION_WARN(is, "failed to allocate simq"); + iscsi_session_terminate(is); + return (ENOMEM); + } + + is->is_sim = cam_sim_alloc(iscsi_action, iscsi_poll, "iscsi", + is, is->is_desc.isd_id /* unit */, &is->is_lock, + maxtags, maxtags, is->is_devq); + if (is->is_sim == NULL) { + ISCSI_SESSION_UNLOCK(is); + ISCSI_SESSION_WARN(is, "failed to allocate SIM"); + cam_simq_free(is->is_devq); + iscsi_session_terminate(is); + return (ENOMEM); + } + + error = xpt_bus_register(is->is_sim, NULL, 0); + if (error != 0) { + ISCSI_SESSION_UNLOCK(is); + ISCSI_SESSION_WARN(is, "failed to register bus"); + iscsi_session_terminate(is); + return (ENOMEM); + } + + error = xpt_create_path(&is->is_path, /*periph*/NULL, + cam_sim_path(is->is_sim), CAM_TARGET_WILDCARD, + CAM_LUN_WILDCARD); + if (error != CAM_REQ_CMP) { + ISCSI_SESSION_UNLOCK(is); + ISCSI_SESSION_WARN(is, "failed to create path"); + iscsi_session_terminate(is); + return (ENOMEM); + } + ISCSI_SESSION_UNLOCK(is); + } + + return (0); +} + +#ifdef ICL_KERNEL_PROXY +static int +iscsi_ioctl_daemon_connect(struct iscsi_softc *sc, + struct iscsi_daemon_connect *idc) +{ + struct iscsi_session *is; + struct sockaddr *from_sa, *to_sa; + int error; + + sx_slock(&sc->sc_lock); + TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { + if (is->is_desc.isd_id == idc->idc_session_id) + break; + } + if (is == NULL) { + sx_sunlock(&sc->sc_lock); + return (ESRCH); + } + sx_sunlock(&sc->sc_lock); + + if (idc->idc_from_addrlen > 0) { + error = getsockaddr(&from_sa, (void *)idc->idc_from_addr, idc->idc_from_addrlen); + if (error != 0) + return (error); + } else { + from_sa = NULL; + } + error = getsockaddr(&to_sa, (void *)idc->idc_to_addr, idc->idc_to_addrlen); + if (error != 0) { + free(from_sa, M_SONAME); + return (error); + } + + ISCSI_SESSION_LOCK(is); + is->is_waiting_for_iscsid = false; + is->is_login_phase = true; + is->is_timeout = 0; + ISCSI_SESSION_UNLOCK(is); + + error = icl_conn_connect(is->is_conn, idc->idc_iser, idc->idc_domain, + idc->idc_socktype, idc->idc_protocol, from_sa, to_sa); + free(from_sa, M_SONAME); + free(to_sa, M_SONAME); + + /* + * Digests are always disabled during login phase. + */ + is->is_conn->ic_header_crc32c = false; + is->is_conn->ic_data_crc32c = false; + + return (error); +} + +static int +iscsi_ioctl_daemon_send(struct iscsi_softc *sc, + struct iscsi_daemon_send *ids) +{ + struct iscsi_session *is; + struct icl_pdu *ip; + size_t datalen; + void *data; + int error; + + sx_slock(&sc->sc_lock); + TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { + if (is->is_desc.isd_id == ids->ids_session_id) + break; + } + if (is == NULL) { + sx_sunlock(&sc->sc_lock); + return (ESRCH); + } + sx_sunlock(&sc->sc_lock); + + if (is->is_login_phase == false) + return (EBUSY); + + if (is->is_terminating || is->is_reconnecting) + return (EIO); + + datalen = ids->ids_data_segment_len; + if (datalen > ISCSI_MAX_DATA_SEGMENT_LENGTH) + return (EINVAL); + if (datalen > 0) { + data = malloc(datalen, M_ISCSI, M_WAITOK); + error = copyin(ids->ids_data_segment, data, datalen); + if (error != 0) { + free(data, M_ISCSI); + return (error); + } + } + + ip = icl_pdu_new_bhs(is->is_conn, M_WAITOK); + memcpy(ip->ip_bhs, ids->ids_bhs, sizeof(*ip->ip_bhs)); + if (datalen > 0) { + icl_pdu_append_data(ip, data, datalen); + free(data, M_ISCSI); + } + icl_pdu_queue(ip); + + return (0); +} + +static int +iscsi_ioctl_daemon_receive(struct iscsi_softc *sc, + struct iscsi_daemon_receive *idr) +{ + struct iscsi_session *is; + struct icl_pdu *ip; + void *data; + + sx_slock(&sc->sc_lock); + TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { + if (is->is_desc.isd_id == idr->idr_session_id) + break; + } + if (is == NULL) { + sx_sunlock(&sc->sc_lock); + return (ESRCH); + } + sx_sunlock(&sc->sc_lock); + + if (is->is_login_phase == false) + return (EBUSY); + + ISCSI_SESSION_LOCK(is); + while (is->is_login_pdu == NULL && + is->is_terminating == false && + is->is_reconnecting == false) + cv_wait(&is->is_login_cv, &is->is_lock); + if (is->is_terminating || is->is_reconnecting) { + ISCSI_SESSION_UNLOCK(is); + return (EIO); + } + ip = is->is_login_pdu; + is->is_login_pdu = NULL; + ISCSI_SESSION_UNLOCK(is); + + if (ip->ip_data_len > idr->idr_data_segment_len) { + icl_pdu_free(ip); + return (EMSGSIZE); + } + + copyout(ip->ip_bhs, idr->idr_bhs, sizeof(*ip->ip_bhs)); + if (ip->ip_data_len > 0) { + data = malloc(ip->ip_data_len, M_ISCSI, M_WAITOK); + icl_pdu_get_data(ip, 0, data, ip->ip_data_len); + copyout(data, idr->idr_data_segment, ip->ip_data_len); + free(data, M_ISCSI); + } + + icl_pdu_free(ip); + + return (0); +} + +static int +iscsi_ioctl_daemon_close(struct iscsi_softc *sc, + struct iscsi_daemon_close *idc) +{ + struct iscsi_session *is; + + sx_slock(&sc->sc_lock); + TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { + if (is->is_desc.isd_id == idc->idc_session_id) + break; + } + if (is == NULL) { + sx_sunlock(&sc->sc_lock); + return (ESRCH); + } + sx_sunlock(&sc->sc_lock); + + iscsi_session_reconnect(is); + + return (0); +} +#endif /* ICL_KERNEL_PROXY */ + +static void +iscsi_sanitize_session_desc(struct iscsi_session_desc *isd) +{ + /* + * Just make sure all the fields are null-terminated. + * + * XXX: This is not particularly secure. We should + * create our own desc and then copy in relevant + * fields. + */ + isd->isd_initiator[ISCSI_NAME_LEN - 1] = '\0'; + isd->isd_initiator_addr[ISCSI_ADDR_LEN - 1] = '\0'; + isd->isd_initiator_alias[ISCSI_ALIAS_LEN - 1] = '\0'; + isd->isd_target[ISCSI_NAME_LEN - 1] = '\0'; + isd->isd_target_addr[ISCSI_ADDR_LEN - 1] = '\0'; + isd->isd_target_alias[ISCSI_ALIAS_LEN - 1] = '\0'; + isd->isd_user[ISCSI_NAME_LEN - 1] = '\0'; + isd->isd_secret[ISCSI_SECRET_LEN - 1] = '\0'; + isd->isd_mutual_user[ISCSI_NAME_LEN - 1] = '\0'; + isd->isd_mutual_secret[ISCSI_SECRET_LEN - 1] = '\0'; + isd->isd_connected = 0; +} + +static int +iscsi_ioctl_session_add(struct iscsi_softc *sc, struct iscsi_session_add *isa) +{ + struct iscsi_session *is; + const struct iscsi_session *is2; + int error; + + iscsi_sanitize_session_desc(&isa->isa_desc); + + is = malloc(sizeof(*is), M_ISCSI, M_ZERO | M_WAITOK); + memcpy(&is->is_desc, &isa->isa_desc, sizeof(is->is_desc)); + + if (is->is_desc.isd_initiator[0] == '\0' || + is->is_desc.isd_target == '\0' || + is->is_desc.isd_target_addr == '\0') { + free(is, M_ISCSI); + return (EINVAL); + } + + sx_xlock(&sc->sc_lock); + + /* + * Prevent duplicates. + */ + TAILQ_FOREACH(is2, &sc->sc_sessions, is_next) { + if (strcmp(is2->is_desc.isd_target, + is->is_desc.isd_target) == 0) { + sx_xunlock(&sc->sc_lock); + return (EBUSY); + } + } + + is->is_conn = icl_conn_new(); + is->is_conn->ic_receive = iscsi_receive_callback; + is->is_conn->ic_error = iscsi_error_callback; + is->is_conn->ic_prv0 = is; + TAILQ_INIT(&is->is_outstanding); + TAILQ_INIT(&is->is_postponed); + mtx_init(&is->is_lock, "iscsi_lock", NULL, MTX_DEF); + cv_init(&is->is_maintenance_cv, "iscsi_mt"); +#ifdef ICL_KERNEL_PROXY + cv_init(&is->is_login_cv, "iscsi_login"); +#endif + + is->is_softc = sc; + sc->sc_last_session_id++; + is->is_desc.isd_id = sc->sc_last_session_id; + callout_init(&is->is_callout, 1); + callout_reset(&is->is_callout, 1 * hz, iscsi_callout, is); + TAILQ_INSERT_TAIL(&sc->sc_sessions, is, is_next); + + error = kthread_add(iscsi_maintenance_thread, is, NULL, NULL, 0, 0, "iscsimt"); + if (error != 0) { + ISCSI_SESSION_WARN(is, "kthread_add(9) failed with error %d", error); + return (error); + } + + /* + * Trigger immediate reconnection. + */ + is->is_waiting_for_iscsid = true; + cv_signal(&sc->sc_cv); + + sx_xunlock(&sc->sc_lock); + + return (0); +} + +static bool +iscsi_session_desc_matches(const struct iscsi_session_desc *d1, + const struct iscsi_session_desc *d2) +{ + if (d2->isd_id == 0 && d2->isd_target[0] == '\0' && + d2->isd_target_addr[0] == '\0') + return (true); + if (d2->isd_id != 0 && d2->isd_id == d1->isd_id) + return (true); + if (d2->isd_target[0] != '\0' && + strcmp(d1->isd_target, d2->isd_target) == 0) + return (true); + if (d2->isd_target_addr[0] != '\0' && + strcmp(d1->isd_target_addr, d2->isd_target_addr) == 0) + return (true); + return (false); +} + +static int +iscsi_ioctl_session_remove(struct iscsi_softc *sc, + struct iscsi_session_remove *isr) +{ + int found = 0; + struct iscsi_session *is, *tmp; + + iscsi_sanitize_session_desc(&isr->isr_desc); + + sx_xlock(&sc->sc_lock); + TAILQ_FOREACH_SAFE(is, &sc->sc_sessions, is_next, tmp) { + ISCSI_SESSION_LOCK(is); + if (iscsi_session_desc_matches(&is->is_desc, &isr->isr_desc)) { + found++; + iscsi_session_logout(is); + iscsi_session_terminate(is); + } + ISCSI_SESSION_UNLOCK(is); + } + sx_xunlock(&sc->sc_lock); + + if (found == 0) + return (ESRCH); + + return (0); +} + +static int +iscsi_ioctl_session_list(struct iscsi_softc *sc, struct iscsi_session_list *isl) +{ + int error; + unsigned int i = 0; + struct iscsi_session *is; + + sx_slock(&sc->sc_lock); + TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { + if (i >= isl->isl_nentries) { + sx_sunlock(&sc->sc_lock); + return (EMSGSIZE); + } + error = copyout(&is->is_desc, isl->isl_pdesc + i, + sizeof(is->is_desc)); + if (error != 0) { + sx_sunlock(&sc->sc_lock); + return (error); + } + i++; + } + sx_sunlock(&sc->sc_lock); + + isl->isl_nentries = i; + + return (0); +} + +static int +iscsi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode, + struct thread *td) +{ + struct iscsi_softc *sc; + + sc = dev->si_drv1; + + switch (cmd) { + case ISCSIDWAIT: + return (iscsi_ioctl_daemon_wait(sc, + (struct iscsi_daemon_request *)arg)); + case ISCSIDHANDOFF: + return (iscsi_ioctl_daemon_handoff(sc, + (struct iscsi_daemon_handoff *)arg)); +#ifdef ICL_KERNEL_PROXY + case ISCSIDCONNECT: + return (iscsi_ioctl_daemon_connect(sc, + (struct iscsi_daemon_connect *)arg)); + case ISCSIDSEND: + return (iscsi_ioctl_daemon_send(sc, + (struct iscsi_daemon_send *)arg)); + case ISCSIDRECEIVE: + return (iscsi_ioctl_daemon_receive(sc, + (struct iscsi_daemon_receive *)arg)); + case ISCSIDCLOSE: + return (iscsi_ioctl_daemon_close(sc, + (struct iscsi_daemon_close *)arg)); +#endif /* ICL_KERNEL_PROXY */ + case ISCSISADD: + return (iscsi_ioctl_session_add(sc, + (struct iscsi_session_add *)arg)); + case ISCSISREMOVE: + return (iscsi_ioctl_session_remove(sc, + (struct iscsi_session_remove *)arg)); + case ISCSISLIST: + return (iscsi_ioctl_session_list(sc, + (struct iscsi_session_list *)arg)); + default: + return (EINVAL); + } +} + +static uint64_t +iscsi_encode_lun(uint32_t lun) +{ + uint8_t encoded[8]; + uint64_t result; + + memset(encoded, 0, sizeof(encoded)); + + if (lun < 256) { + /* + * Peripheral device addressing. + */ + encoded[1] = lun; + } else if (lun < 16384) { + /* + * Flat space addressing. + */ + encoded[0] = 0x40; + encoded[0] |= (lun >> 8) & 0x3f; + encoded[1] = lun & 0xff; + } else { + /* + * Extended flat space addressing. + */ + encoded[0] = 0xd2; + encoded[1] = lun >> 16; + encoded[2] = lun >> 8; + encoded[3] = lun; + } + + memcpy(&result, encoded, sizeof(result)); + return (result); +} + +static struct iscsi_outstanding * +iscsi_outstanding_find(struct iscsi_session *is, uint32_t initiator_task_tag) +{ + struct iscsi_outstanding *io; + + ISCSI_SESSION_LOCK_ASSERT(is); + + TAILQ_FOREACH(io, &is->is_outstanding, io_next) { + if (io->io_initiator_task_tag == initiator_task_tag) + return (io); + } + return (NULL); +} + +static int +iscsi_outstanding_add(struct iscsi_session *is, + uint32_t initiator_task_tag, union ccb *ccb) +{ + struct iscsi_outstanding *io; + + ISCSI_SESSION_LOCK_ASSERT(is); + + KASSERT(iscsi_outstanding_find(is, initiator_task_tag) == NULL, + ("initiator_task_tag 0x%x already added", initiator_task_tag)); + + io = uma_zalloc(iscsi_outstanding_zone, M_NOWAIT | M_ZERO); + if (io == NULL) { + ISCSI_SESSION_WARN(is, "failed to allocate %zd bytes", sizeof(*io)); + return (ENOMEM); + } + io->io_initiator_task_tag = initiator_task_tag; + io->io_ccb = ccb; + TAILQ_INSERT_TAIL(&is->is_outstanding, io, io_next); + return (0); +} + +static void +iscsi_outstanding_remove(struct iscsi_session *is, struct iscsi_outstanding *io) +{ + + ISCSI_SESSION_LOCK_ASSERT(is); + + TAILQ_REMOVE(&is->is_outstanding, io, io_next); + uma_zfree(iscsi_outstanding_zone, io); +} + +static void +iscsi_action_scsiio(struct iscsi_session *is, union ccb *ccb) +{ + struct icl_pdu *request; + struct iscsi_bhs_scsi_command *bhssc; + struct ccb_scsiio *csio; + size_t len; + int error; + + ISCSI_SESSION_LOCK_ASSERT(is); + +#if 0 + KASSERT(is->is_login_phase == false, ("%s called during Login Phase", __func__)); +#else + if (is->is_login_phase) { + ISCSI_SESSION_DEBUG(is, "called during login phase"); + ccb->ccb_h.status = CAM_REQ_ABORTED; /* XXX: Proper error code? */ + xpt_done(ccb); + return; + } +#endif + + request = icl_pdu_new_bhs(is->is_conn, M_NOWAIT); + if (request == NULL) { + ccb->ccb_h.status = CAM_REQ_ABORTED; /* XXX: Proper error code? */ + xpt_done(ccb); + return; + } + + csio = &ccb->csio; + bhssc = (struct iscsi_bhs_scsi_command *)request->ip_bhs; + bhssc->bhssc_opcode = ISCSI_BHS_OPCODE_SCSI_COMMAND; + bhssc->bhssc_flags |= BHSSC_FLAGS_F; + switch (csio->ccb_h.flags & CAM_DIR_MASK) { + case CAM_DIR_IN: + bhssc->bhssc_flags |= BHSSC_FLAGS_R; + break; + case CAM_DIR_OUT: + bhssc->bhssc_flags |= BHSSC_FLAGS_W; + break; + } + + switch (csio->tag_action) { + case MSG_HEAD_OF_Q_TAG: + bhssc->bhssc_flags |= BHSSC_FLAGS_ATTR_HOQ; + break; + break; + case MSG_ORDERED_Q_TAG: + bhssc->bhssc_flags |= BHSSC_FLAGS_ATTR_ORDERED; + break; + case MSG_ACA_TASK: + bhssc->bhssc_flags |= BHSSC_FLAGS_ATTR_ACA; + break; + case CAM_TAG_ACTION_NONE: + case MSG_SIMPLE_Q_TAG: + default: + bhssc->bhssc_flags |= BHSSC_FLAGS_ATTR_SIMPLE; + break; + } + + bhssc->bhssc_lun = iscsi_encode_lun(csio->ccb_h.target_lun); + bhssc->bhssc_initiator_task_tag = is->is_initiator_task_tag; + is->is_initiator_task_tag++; + bhssc->bhssc_expected_data_transfer_length = htonl(csio->dxfer_len); + KASSERT(csio->cdb_len <= sizeof(bhssc->bhssc_cdb), + ("unsupported CDB size %zd", csio->cdb_len)); + + if (csio->ccb_h.flags & CAM_CDB_POINTER) + memcpy(&bhssc->bhssc_cdb, csio->cdb_io.cdb_ptr, csio->cdb_len); + else + memcpy(&bhssc->bhssc_cdb, csio->cdb_io.cdb_bytes, csio->cdb_len); + + error = iscsi_outstanding_add(is, bhssc->bhssc_initiator_task_tag, ccb); + if (error != 0) { + icl_pdu_free(request); + ccb->ccb_h.status = CAM_REQ_ABORTED; + xpt_done(ccb); + } + + if (is->is_desc.isd_immediate_data && + (csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) { + len = csio->dxfer_len; + //ISCSI_SESSION_DEBUG(is, "adding %zd of immediate data", len); + if (len > is->is_first_burst_length) { + ISCSI_SESSION_DEBUG(is, "len %zd -> %zd", len, is->is_first_burst_length); + len = is->is_first_burst_length; + } + + /* + * XXX: Ugly. + */ + ISCSI_SESSION_UNLOCK(is); + icl_pdu_append_data(request, csio->data_ptr, len); + ISCSI_SESSION_LOCK(is); + } + iscsi_pdu_queue_locked(request); +} + +static void +iscsi_action(struct cam_sim *sim, union ccb *ccb) +{ + struct iscsi_session *is; + + is = cam_sim_softc(sim); + + ISCSI_SESSION_LOCK_ASSERT(is); + + switch (ccb->ccb_h.func_code) { + case XPT_PATH_INQ: + { + struct ccb_pathinq *cpi = &ccb->cpi; + + cpi->version_num = 1; + cpi->hba_inquiry = PI_TAG_ABLE; + cpi->target_sprt = 0; + //cpi->hba_misc = PIM_NOBUSRESET; + cpi->hba_misc = 0; + cpi->hba_eng_cnt = 0; + cpi->max_target = 0; + cpi->max_lun = 255; + //cpi->initiator_id = 0; /* XXX */ + cpi->initiator_id = 64; /* XXX */ + strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); + strncpy(cpi->hba_vid, "iSCSI", HBA_IDLEN); + strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); + cpi->unit_number = cam_sim_unit(sim); + cpi->bus_id = cam_sim_bus(sim); + cpi->base_transfer_speed = 150000; /* XXX */ + cpi->transport = XPORT_ISCSI; + cpi->transport_version = 0; + cpi->protocol = PROTO_SCSI; + cpi->protocol_version = SCSI_REV_SPC; /* XXX: SPC3? */ + cpi->maxio = MAXPHYS; + cpi->ccb_h.status = CAM_REQ_CMP; + break; + } + case XPT_CALC_GEOMETRY: + cam_calc_geometry(&ccb->ccg, /*extended*/1); + ccb->ccb_h.status = CAM_REQ_CMP; + break; + case XPT_RESET_BUS: + case XPT_ABORT: + case XPT_TERM_IO: + ISCSI_DEBUG("faking success for reset, abort, or term_io"); + ccb->ccb_h.status = CAM_REQ_CMP; + break; + case XPT_SCSI_IO: + iscsi_action_scsiio(is, ccb); + return; + default: + ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; + break; + } + xpt_done(ccb); +} + +static void +iscsi_poll(struct cam_sim *sim) +{ + + KASSERT(0, ("%s: you're not supposed to be here", __func__)); +} + +static void +iscsi_shutdown(struct iscsi_softc *sc) +{ + struct iscsi_session *is; + + ISCSI_DEBUG("removing all sessions due to shutdown"); + + sx_slock(&sc->sc_lock); + TAILQ_FOREACH(is, &sc->sc_sessions, is_next) + iscsi_session_terminate(is); + sx_sunlock(&sc->sc_lock); +} + +static int +iscsi_load(void) +{ + int error; + + sc = malloc(sizeof(*sc), M_ISCSI, M_ZERO | M_WAITOK); + sx_init(&sc->sc_lock, "iscsi"); + TAILQ_INIT(&sc->sc_sessions); + cv_init(&sc->sc_cv, "iscsi_cv"); + + iscsi_outstanding_zone = uma_zcreate("iscsi_outstanding", + sizeof(struct iscsi_outstanding), NULL, NULL, NULL, NULL, + UMA_ALIGN_PTR, UMA_ZONE_NOFREE); + + error = make_dev_p(MAKEDEV_CHECKNAME, &sc->sc_cdev, &iscsi_cdevsw, + NULL, UID_ROOT, GID_WHEEL, 0600, "iscsi"); + if (error != 0) { + ISCSI_WARN("failed to create device node, error %d", error); + sx_destroy(&sc->sc_lock); + cv_destroy(&sc->sc_cv); + uma_zdestroy(iscsi_outstanding_zone); + free(sc, M_ISCSI); + return (error); + } + sc->sc_cdev->si_drv1 = sc; + + /* + * XXX: For some reason this doesn't do its job; active sessions still hang out there + * after final sync, making the reboot effectively hang. + */ + sc->sc_shutdown_eh = EVENTHANDLER_REGISTER(shutdown_post_sync, iscsi_shutdown, sc, SHUTDOWN_PRI_DEFAULT); + + return (0); +} + +static int +iscsi_unload(void) +{ + /* + * XXX: kldunload hangs on "devdrn". + */ + struct iscsi_session *is, *tmp; + + ISCSI_DEBUG("removing device node"); + destroy_dev(sc->sc_cdev); + ISCSI_DEBUG("device node removed"); + + EVENTHANDLER_DEREGISTER(shutdown_post_sync, sc->sc_shutdown_eh); + + sx_slock(&sc->sc_lock); + TAILQ_FOREACH_SAFE(is, &sc->sc_sessions, is_next, tmp) + iscsi_session_terminate(is); + while(!TAILQ_EMPTY(&sc->sc_sessions)) { + ISCSI_DEBUG("waiting for sessions to terminate"); + cv_wait(&sc->sc_cv, &sc->sc_lock); + } + ISCSI_DEBUG("all sessions terminated"); + sx_sunlock(&sc->sc_lock); + + uma_zdestroy(iscsi_outstanding_zone); + sx_destroy(&sc->sc_lock); + cv_destroy(&sc->sc_cv); + free(sc, M_ISCSI); + return (0); +} + +static int +iscsi_quiesce(void) +{ + sx_slock(&sc->sc_lock); + if (!TAILQ_EMPTY(&sc->sc_sessions)) { + sx_sunlock(&sc->sc_lock); + return (EBUSY); + } + sx_sunlock(&sc->sc_lock); + return (0); +} + +static int +iscsi_modevent(module_t mod, int what, void *arg) +{ + int error; + + switch (what) { + case MOD_LOAD: + error = iscsi_load(); + break; + case MOD_UNLOAD: + error = iscsi_unload(); + break; + case MOD_QUIESCE: + error = iscsi_quiesce(); + break; + default: + error = EINVAL; + break; + } + return (error); +} + +moduledata_t iscsi_data = { + "iscsi2", + iscsi_modevent, + 0 +}; + +DECLARE_MODULE(iscsi2, iscsi_data, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); +MODULE_DEPEND(iscsi2, cam, 1, 1, 1); +MODULE_DEPEND(iscsi2, icl, 1, 1, 1); diff -urN p4/freebsd/src/sys/dev/iscsi/iscsi.h p4/iscsi/sys/dev/iscsi/iscsi.h --- p4/freebsd/src/sys/dev/iscsi/iscsi.h 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/sys/dev/iscsi/iscsi.h 2013-08-18 13:07:26.000000000 +0200 @@ -0,0 +1,126 @@ +/*- + * 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_XXX_H +#define ISCSI_XXX_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; + uint32_t io_initiator_task_tag; + uint32_t io_datasn; + union ccb *io_ccb; +}; + +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]; + + 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; + + 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; + struct iscsi_session_desc is_desc; + bool is_simq_frozen; + +#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_XXX_H */ diff -urN p4/freebsd/src/sys/dev/iscsi/iscsi_ioctl.h p4/iscsi/sys/dev/iscsi/iscsi_ioctl.h --- p4/freebsd/src/sys/dev/iscsi/iscsi_ioctl.h 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/sys/dev/iscsi/iscsi_ioctl.h 2013-08-18 13:07:26.000000000 +0200 @@ -0,0 +1,179 @@ +/*- + * 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 +#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 it's defined? */ +#define ISCSI_SECRET_LEN 17 /* 16 + '\0' */ + +#define ISCSI_DIGEST_NONE 0 +#define ISCSI_DIGEST_CRC32C 1 + +struct iscsi_session_desc { + unsigned int isd_id; + char isd_initiator[ISCSI_NAME_LEN]; + char isd_initiator_addr[ISCSI_ADDR_LEN]; + char isd_initiator_alias[ISCSI_ALIAS_LEN]; + char isd_target[ISCSI_NAME_LEN]; + char isd_target_addr[ISCSI_ADDR_LEN]; + char isd_target_alias[ISCSI_ALIAS_LEN]; + char isd_user[ISCSI_NAME_LEN]; + char isd_secret[ISCSI_SECRET_LEN]; + char isd_mutual_user[ISCSI_NAME_LEN]; + char isd_mutual_secret[ISCSI_SECRET_LEN]; + int isd_discovery; + int isd_header_digest; + int isd_data_digest; + int isd_max_data_segment_length; + int isd_immediate_data; + int isd_iser; + int isd_connected; +}; + +/* + * For use by iscsid(8). + */ + +#define ISCSI_DAEMON_CMD_LOGIN 0x01 + +struct iscsi_daemon_request { + int idr_cmd; + struct iscsi_session_desc idr_desc; +}; + +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; +}; + +#define ISCSIDWAIT _IOR('I', 0x01, struct iscsi_daemon_request) +#define ISCSIDHANDOFF _IOW('I', 0x02, struct iscsi_daemon_handoff) + +#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', 0x03, struct iscsi_daemon_connect) +#define ISCSIDSEND _IOWR('I', 0x04, struct iscsi_daemon_send) +#define ISCSIDRECEIVE _IOWR('I', 0x05, struct iscsi_daemon_receive) +#define ISCSIDCLOSE _IOWR('I', 0x06, struct iscsi_daemon_close) + +#endif /* ICL_KERNEL_PROXY */ + +/* + * For use by iscsictl(8). + */ + +struct iscsi_session_add { + struct iscsi_session_desc isa_desc; +}; + +struct iscsi_session_remove { + struct iscsi_session_desc isr_desc; +}; + +struct iscsi_session_list { + unsigned int isl_nentries; + struct iscsi_session_desc *isl_pdesc; +}; + +#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 */ diff -urN p4/freebsd/src/sys/dev/iscsi_initiator/isc_cam.c p4/iscsi/sys/dev/iscsi_initiator/isc_cam.c --- p4/freebsd/src/sys/dev/iscsi_initiator/isc_cam.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/sys/dev/iscsi_initiator/isc_cam.c 2013-07-23 23:45:08.000000000 +0200 @@ -0,0 +1,384 @@ +/*- + * Copyright (c) 2005-2010 Daniel Braniss + * 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. + * + * 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. + * + */ +/* + | $Id: isc_cam.c 998 2009-12-20 10:32:45Z danny $ + */ +#include +__FBSDID("$FreeBSD: head/sys/dev/iscsi/initiator/isc_cam.c 249468 2013-04-14 09:55:48Z mav $"); + +#include "opt_iscsi_initiator.h" + +#include +#include +#include +#if __FreeBSD_version >= 700000 +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +static void +_inq(struct cam_sim *sim, union ccb *ccb) +{ + struct ccb_pathinq *cpi = &ccb->cpi; + isc_session_t *sp = cam_sim_softc(sim); + + debug_called(8); + debug(3, "sid=%d target=%d lun=%d", sp->sid, ccb->ccb_h.target_id, ccb->ccb_h.target_lun); + + cpi->version_num = 1; /* XXX??? */ + cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE | PI_WIDE_32; + cpi->target_sprt = 0; + cpi->hba_misc = 0; + cpi->hba_eng_cnt = 0; + cpi->max_target = 0; //ISCSI_MAX_TARGETS - 1; + cpi->initiator_id = ISCSI_MAX_TARGETS; + cpi->max_lun = sp->opt.maxluns - 1; + cpi->bus_id = cam_sim_bus(sim); + cpi->base_transfer_speed = 3300; // 40000; // XXX: + strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); + strncpy(cpi->hba_vid, "iSCSI", HBA_IDLEN); + strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); + cpi->unit_number = cam_sim_unit(sim); + cpi->ccb_h.status = CAM_REQ_CMP; +#if defined(KNOB_VALID_ADDRESS) + cpi->transport = XPORT_ISCSI; + cpi->transport_version = 0; +#endif +} + +static __inline int +_scsi_encap(struct cam_sim *sim, union ccb *ccb) +{ + int ret; + +#if __FreeBSD_version < 700000 + ret = scsi_encap(sim, ccb); +#else + isc_session_t *sp = cam_sim_softc(sim); + + mtx_unlock(&sp->cam_mtx); + ret = scsi_encap(sim, ccb); + mtx_lock(&sp->cam_mtx); +#endif + return ret; +} + +void +ic_lost_target(isc_session_t *sp, int target) +{ + debug_called(8); + sdebug(2, "lost target=%d", target); + + if(sp->cam_path != NULL) { + mtx_lock(&sp->cam_mtx); + xpt_async(AC_LOST_DEVICE, sp->cam_path, NULL); + xpt_free_path(sp->cam_path); + mtx_unlock(&sp->cam_mtx); + sp->cam_path = 0; // XXX + } +} + +static void +scan_callback(struct cam_periph *periph, union ccb *ccb) +{ + isc_session_t *sp = (isc_session_t *)ccb->ccb_h.spriv_ptr0; + + debug_called(8); + + free(ccb, M_TEMP); + + if(sp->flags & ISC_SCANWAIT) { + sp->flags &= ~ISC_SCANWAIT; + wakeup(sp); + } +} + +static int +ic_scan(isc_session_t *sp) +{ + union ccb *ccb; + + debug_called(8); + sdebug(2, "scanning sid=%d", sp->sid); + + if((ccb = malloc(sizeof(union ccb), M_TEMP, M_WAITOK | M_ZERO)) == NULL) { + xdebug("scan failed (can't allocate CCB)"); + return ENOMEM; // XXX + } + + sp->flags &= ~ISC_CAMDEVS; + sp->flags |= ISC_SCANWAIT; + + CAM_LOCK(sp); + if(xpt_create_path(&sp->cam_path, NULL, cam_sim_path(sp->cam_sim), + 0, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { + xdebug("can't create cam path"); + CAM_UNLOCK(sp); + free(ccb, M_TEMP); + return ENODEV; // XXX + } + xpt_setup_ccb(&ccb->ccb_h, sp->cam_path, 5/*priority (low)*/); + ccb->ccb_h.func_code = XPT_SCAN_BUS; + ccb->ccb_h.cbfcnp = scan_callback; + ccb->crcn.flags = CAM_FLAG_NONE; + ccb->ccb_h.spriv_ptr0 = sp; + + xpt_action(ccb); + CAM_UNLOCK(sp); + + while(sp->flags & ISC_SCANWAIT) + tsleep(sp, PRIBIO, "ffp", 5*hz); // the timeout time should + // be configurable + sdebug(2, "# of luns=%d", sp->target_nluns); + + if(sp->target_nluns > 0) { + sp->flags |= ISC_CAMDEVS; + return 0; + } + + return ENODEV; +} + +static void +ic_action(struct cam_sim *sim, union ccb *ccb) +{ + isc_session_t *sp = cam_sim_softc(sim); + struct ccb_hdr *ccb_h = &ccb->ccb_h; + + debug_called(8); + + ccb_h->spriv_ptr0 = sp; + sdebug(4, "func_code=0x%x flags=0x%x status=0x%x target=%d lun=%d retry_count=%d timeout=%d", + ccb_h->func_code, ccb->ccb_h.flags, ccb->ccb_h.status, + ccb->ccb_h.target_id, ccb->ccb_h.target_lun, + ccb->ccb_h.retry_count, ccb_h->timeout); + if(sp == NULL) { + xdebug("sp == NULL! cannot happen"); + return; + } + switch(ccb_h->func_code) { + case XPT_PATH_INQ: + _inq(sim, ccb); + break; + + case XPT_RESET_BUS: // (can just be a stub that does nothing and completes) + { + struct ccb_pathinq *cpi = &ccb->cpi; + + debug(3, "XPT_RESET_BUS"); + cpi->ccb_h.status = CAM_REQ_CMP; + break; + } + + case XPT_SCSI_IO: + { + struct ccb_scsiio* csio = &ccb->csio; + + debug(4, "XPT_SCSI_IO cmd=0x%x", csio->cdb_io.cdb_bytes[0]); + if(sp == NULL) { + ccb_h->status = CAM_REQ_INVALID; //CAM_NO_NEXUS; + debug(4, "xpt_done.status=%d", ccb_h->status); + break; + } + if(ccb_h->target_lun == CAM_LUN_WILDCARD) { + debug(3, "target=%d: bad lun (-1)", ccb_h->target_id); + ccb_h->status = CAM_LUN_INVALID; + break; + } + if(_scsi_encap(sim, ccb) != 0) + return; + break; + } + + case XPT_CALC_GEOMETRY: + { + struct ccb_calc_geometry *ccg; + + ccg = &ccb->ccg; + debug(4, "sid=%d target=%d lun=%d XPT_CALC_GEOMETRY vsize=%jd bsize=%d", + sp->sid, ccb->ccb_h.target_id, ccb->ccb_h.target_lun, + ccg->volume_size, ccg->block_size); + if(ccg->block_size == 0 || + (ccg->volume_size < ccg->block_size)) { + // print error message ... + /* XXX: what error is appropiate? */ + break; + } + else { + int lun, *off, boff; + + lun = ccb->ccb_h.target_lun; + if(lun > ISCSI_MAX_LUNS) { + // XXX: + xdebug("lun %d > ISCSI_MAX_LUNS!\n", lun); + lun %= ISCSI_MAX_LUNS; + } + off = &sp->target_lun[lun / (sizeof(int)*8)]; + boff = BIT(lun % (sizeof(int)*8)); + debug(4, "sp->target_nluns=%d *off=%x boff=%x", + sp->target_nluns, *off, boff); + + if((*off & boff) == 0) { + sp->target_nluns++; + *off |= boff; + } + cam_calc_geometry(ccg, /*extended*/1); + } + break; + } + + case XPT_GET_TRAN_SETTINGS: + default: + ccb_h->status = CAM_REQ_INVALID; + break; + } +#if __FreeBSD_version < 700000 + XPT_DONE(sp, ccb); +#else + xpt_done(ccb); +#endif + return; +} + +static void +ic_poll(struct cam_sim *sim) +{ + debug_called(4); + +} + +int +ic_getCamVals(isc_session_t *sp, iscsi_cam_t *cp) +{ + debug_called(8); + + if(sp && sp->cam_sim) { + cp->path_id = cam_sim_path(sp->cam_sim); + cp->target_id = 0; + cp->target_nluns = ISCSI_MAX_LUNS; // XXX: -1? + return 0; + } + return ENXIO; +} + +void +ic_destroy(isc_session_t *sp ) +{ + debug_called(8); + + if(sp->cam_path != NULL) { + sdebug(2, "name=%s unit=%d", + cam_sim_name(sp->cam_sim), cam_sim_unit(sp->cam_sim)); + CAM_LOCK(sp); +#if 0 + xpt_async(AC_LOST_DEVICE, sp->cam_path, NULL); +#else + xpt_async(XPT_RESET_BUS, sp->cam_path, NULL); +#endif + xpt_free_path(sp->cam_path); + xpt_bus_deregister(cam_sim_path(sp->cam_sim)); + cam_sim_free(sp->cam_sim, TRUE /*free_devq*/); + + CAM_UNLOCK(sp); + sdebug(2, "done"); + } +} + +int +ic_init(isc_session_t *sp) +{ + struct cam_sim *sim; + struct cam_devq *devq; + + debug_called(8); + + if((devq = cam_simq_alloc(256)) == NULL) + return ENOMEM; + +#if __FreeBSD_version >= 700000 + mtx_init(&sp->cam_mtx, "isc-cam", NULL, MTX_DEF); +#else + isp->cam_mtx = Giant; +#endif + sim = cam_sim_alloc(ic_action, + ic_poll, + "iscsi", + sp, + sp->sid, // unit +#if __FreeBSD_version >= 700000 + &sp->cam_mtx, +#endif + 1, // max_dev_transactions + 0, // max_tagged_dev_transactions + devq); + if(sim == NULL) { + cam_simq_free(devq); +#if __FreeBSD_version >= 700000 + mtx_destroy(&sp->cam_mtx); +#endif + return ENXIO; + } + + CAM_LOCK(sp); + if(xpt_bus_register(sim, +#if __FreeBSD_version >= 700000 + NULL, +#endif + 0/*bus_number*/) != CAM_SUCCESS) { + + cam_sim_free(sim, /*free_devq*/TRUE); + CAM_UNLOCK(sp); +#if __FreeBSD_version >= 700000 + mtx_destroy(&sp->cam_mtx); +#endif + return ENXIO; + } + sp->cam_sim = sim; + CAM_UNLOCK(sp); + + sdebug(1, "cam subsystem initialized"); + + ic_scan(sp); + + return 0; +} diff -urN p4/freebsd/src/sys/dev/iscsi_initiator/isc_sm.c p4/iscsi/sys/dev/iscsi_initiator/isc_sm.c --- p4/freebsd/src/sys/dev/iscsi_initiator/isc_sm.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/sys/dev/iscsi_initiator/isc_sm.c 2013-07-23 23:45:08.000000000 +0200 @@ -0,0 +1,786 @@ +/*- + * Copyright (c) 2005-2010 Daniel Braniss + * 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. + * + * 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. + * + */ +/* + | iSCSI - Session Manager + | $Id: isc_sm.c 743 2009-08-08 10:54:53Z danny $ + */ + +#include +__FBSDID("$FreeBSD: head/sys/dev/iscsi/initiator/isc_sm.c 231378 2012-02-10 12:35:57Z ed $"); + +#include "opt_iscsi_initiator.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +static void +_async(isc_session_t *sp, pduq_t *pq) +{ + debug_called(8); + + iscsi_async(sp, pq); + + pdu_free(sp->isc, pq); +} + +static void +_reject(isc_session_t *sp, pduq_t *pq) +{ + pduq_t *opq; + pdu_t *pdu; + reject_t *reject; + int itt; + + debug_called(8); + pdu = mtod(pq->mp, pdu_t *); + itt = pdu->ipdu.bhs.itt; + reject = &pq->pdu.ipdu.reject; + sdebug(2, "itt=%x reason=0x%x", ntohl(itt), reject->reason); + opq = i_search_hld(sp, itt, 0); + if(opq != NULL) + iscsi_reject(sp, opq, pq); + else { + switch(pq->pdu.ipdu.bhs.opcode) { + case ISCSI_LOGOUT_CMD: // XXX: wasabi does this - can't figure out why + sdebug(2, "ISCSI_LOGOUT_CMD ..."); + break; + default: + xdebug("%d] we lost something itt=%x", + sp->sid, ntohl(pq->pdu.ipdu.bhs.itt)); + } + } + pdu_free(sp->isc, pq); +} + +static void +_r2t(isc_session_t *sp, pduq_t *pq) +{ + pduq_t *opq; + + debug_called(8); + opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1); + if(opq != NULL) { + iscsi_r2t(sp, opq, pq); + } + else { + r2t_t *r2t = &pq->pdu.ipdu.r2t; + + xdebug("%d] we lost something itt=%x r2tSN=%d bo=%x ddtl=%x", + sp->sid, ntohl(pq->pdu.ipdu.bhs.itt), + ntohl(r2t->r2tSN), ntohl(r2t->bo), ntohl(r2t->ddtl)); + } + pdu_free(sp->isc, pq); +} + +static void +_scsi_rsp(isc_session_t *sp, pduq_t *pq) +{ + pduq_t *opq; + + debug_called(8); + opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 0); + debug(5, "itt=%x pq=%p opq=%p", ntohl(pq->pdu.ipdu.bhs.itt), pq, opq); + if(opq != NULL) { + iscsi_done(sp, opq, pq); + i_acked_hld(sp, &pq->pdu); + } + else + xdebug("%d] we lost something itt=%x", + sp->sid, ntohl(pq->pdu.ipdu.bhs.itt)); + pdu_free(sp->isc, pq); +} + +static void +_read_data(isc_session_t *sp, pduq_t *pq) +{ + pduq_t *opq; + + debug_called(8); + opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1); + if(opq != NULL) { + if(scsi_decap(sp, opq, pq) != 1) { + i_remove_hld(sp, opq); // done + pdu_free(sp->isc, opq); + } + } + else + xdebug("%d] we lost something itt=%x", + sp->sid, ntohl(pq->pdu.ipdu.bhs.itt)); + pdu_free(sp->isc, pq); +} +/* + | this is a kludge, + | the jury is not back with a veredict, user or kernel + */ +static void +_nop_out(isc_session_t *sp) +{ + pduq_t *pq; + nop_out_t *nop_out; + + debug_called(8); + + sdebug(4, "cws=%d", sp->cws); + if(sp->cws == 0) { + /* + | only send a nop if window is closed. + */ + if((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL) + // I guess we ran out of resources + return; + nop_out = &pq->pdu.ipdu.nop_out; + nop_out->opcode = ISCSI_NOP_OUT; + nop_out->itt = htonl(sp->sn.itt); + nop_out->ttt = -1; + nop_out->I = 1; + nop_out->F = 1; + if(isc_qout(sp, pq) != 0) { + sdebug(1, "failed"); + pdu_free(sp->isc, pq); + } + } +} + +static void +_nop_in(isc_session_t *sp, pduq_t *pq) +{ + pdu_t *pp = &pq->pdu; + nop_in_t *nop_in = &pp->ipdu.nop_in; + bhs_t *bhs = &pp->ipdu.bhs; + + debug_called(8); + + sdebug(5, "itt=%x ttt=%x", htonl(nop_in->itt), htonl(nop_in->ttt)); + if(nop_in->itt == -1) { + if(pp->ds_len != 0) { + /* + | according to RFC 3720 this should be zero + | what to do if not? + */ + xdebug("%d] dslen not zero", sp->sid); + } + if(nop_in->ttt != -1) { + nop_out_t *nop_out; + /* + | target wants a nop_out + */ + bhs->opcode = ISCSI_NOP_OUT; + bhs->I = 1; + bhs->F = 1; + /* + | we are reusing the pdu, so bhs->ttt == nop_in->ttt; + | and need to zero out 'Reserved' + | small cludge here. + */ + nop_out = &pp->ipdu.nop_out; + nop_out->sn.maxcmd = 0; + memset(nop_out->mbz, 0, sizeof(nop_out->mbz)); + (void)isc_qout(sp, pq); //XXX: should check return? + return; + } + //else { + // just making noise? + // see 10.9.1: target does not want and answer. + //} + + } else + if(nop_in->ttt == -1) { + /* + | it is an answer to a nop_in from us + */ + if(nop_in->itt != -1) { +#ifdef ISC_WAIT4PING + // XXX: MUTEX please + if(sp->flags & ISC_WAIT4PING) { + i_nqueue_rsp(sp, pq); + wakeup(&sp->rsp); + return; + } +#endif + } + } + /* + | drop it + */ + pdu_free(sp->isc, pq); + return; +} + +int +i_prepPDU(isc_session_t *sp, pduq_t *pq) +{ + size_t len, n; + pdu_t *pp = &pq->pdu; + bhs_t *bhp = &pp->ipdu.bhs; + + len = sizeof(bhs_t); + if(pp->ahs_len) { + len += pp->ahs_len; + bhp->AHSLength = pp->ahs_len / 4; + } + if(ISOK2DIG(sp->hdrDigest, pp)) + len += 4; + if(pp->ds_len) { + n = pp->ds_len; + len += n; +#if BYTE_ORDER == LITTLE_ENDIAN + bhp->DSLength = ((n & 0x00ff0000) >> 16) + | (n & 0x0000ff00) + | ((n & 0x000000ff) << 16); +#else + bhp->DSLength = n; +#endif + if(len & 03) { + n = 4 - (len & 03); + len += n; + } + if(ISOK2DIG(sp->dataDigest, pp)) + len += 4; + } + + pq->len = len; + len -= sizeof(bhs_t); + if(sp->opt.maxBurstLength && (len > sp->opt.maxBurstLength)) { + xdebug("%d] pdu len=%zd > %d", + sp->sid, len, sp->opt.maxBurstLength); + // XXX: when this happens it used to hang ... + return E2BIG; + } + return 0; +} + +int +isc_qout(isc_session_t *sp, pduq_t *pq) +{ + int error = 0; + + debug_called(8); + + if(pq->len == 0 && (error = i_prepPDU(sp, pq))) + return error; + + if(pq->pdu.ipdu.bhs.I) + i_nqueue_isnd(sp, pq); + else + if(pq->pdu.ipdu.data_out.opcode == ISCSI_WRITE_DATA) + i_nqueue_wsnd(sp, pq); + else + i_nqueue_csnd(sp, pq); + + sdebug(5, "enqued: pq=%p", pq); + + mtx_lock(&sp->io_mtx); + sp->flags |= ISC_OQNOTEMPTY; + if(sp->flags & ISC_OWAITING) + wakeup(&sp->flags); + mtx_unlock(&sp->io_mtx); + + return error; +} +/* + | called when a fullPhase is restarted + */ +void +ism_restart(isc_session_t *sp) +{ + int lastcmd; + + sdebug(2, "restart ..."); + lastcmd = iscsi_requeue(sp); +#if 0 + if(lastcmd != sp->sn.cmd) { + sdebug(1, "resetting CmdSN to=%d (from %d)", lastcmd, sp->sn.cmd); + sp->sn.cmd = lastcmd; + } +#endif + mtx_lock(&sp->io_mtx); + if(sp->flags & ISC_OWAITING) { + wakeup(&sp->flags); + } + mtx_unlock(&sp->io_mtx); + + sdebug(2, "restarted sn.cmd=0x%x lastcmd=0x%x", sp->sn.cmd, lastcmd); +} + +void +ism_recv(isc_session_t *sp, pduq_t *pq) +{ + bhs_t *bhs; + int statSN; + + debug_called(8); + + bhs = &pq->pdu.ipdu.bhs; + statSN = ntohl(bhs->OpcodeSpecificFields[1]); + +#ifdef notyet + if(sp->sn.expCmd != sn->cmd) { + sdebug(1, "we lost something ... exp=0x%x cmd=0x%x", + sn->expCmd, sn->cmd); + } +#endif + sdebug(5, "opcode=0x%x itt=0x%x stat#0x%x maxcmd=0x%0x", + bhs->opcode, ntohl(bhs->itt), statSN, sp->sn.maxCmd); + + switch(bhs->opcode) { + case ISCSI_READ_DATA: { + data_in_t *cmd = &pq->pdu.ipdu.data_in; + + if(cmd->S == 0) + break; + } + + default: + if(statSN > (sp->sn.stat + 1)) { + sdebug(1, "we lost some rec=0x%x exp=0x%x", + statSN, sp->sn.stat); + // XXX: must do some error recovery here. + } + sp->sn.stat = statSN; + } + + switch(bhs->opcode) { + case ISCSI_LOGIN_RSP: + case ISCSI_TEXT_RSP: + case ISCSI_LOGOUT_RSP: + i_nqueue_rsp(sp, pq); + wakeup(&sp->rsp); + sdebug(3, "wakeup rsp"); + break; + + case ISCSI_NOP_IN: _nop_in(sp, pq); break; + case ISCSI_SCSI_RSP: _scsi_rsp(sp, pq); break; + case ISCSI_READ_DATA: _read_data(sp, pq); break; + case ISCSI_R2T: _r2t(sp, pq); break; + case ISCSI_REJECT: _reject(sp, pq); break; + case ISCSI_ASYNC: _async(sp, pq); break; + + case ISCSI_TASK_RSP: + default: + sdebug(1, "opcode=0x%x itt=0x%x not implemented yet", + bhs->opcode, ntohl(bhs->itt)); + break; + } +} + +/* + | go through the out queues looking for work + | if either nothing to do, or window is closed + | return. + */ +static int +proc_out(isc_session_t *sp) +{ + sn_t *sn = &sp->sn; + pduq_t *pq; + int error, which; + + debug_called(8); + error = 0; + + while(sp->flags & ISC_LINK_UP) { + pdu_t *pp; + bhs_t *bhs; + /* + | check if there is outstanding work in: + | 1- the Immediate queue + | 2- the R2T queue + | 3- the cmd queue, only if the command window allows it. + */ + which = BIT(0) | BIT(1); + if(SNA_GT(sn->cmd, sn->maxCmd) == 0) // if(sn->maxCmd - sn->smc + 1) > 0 + which |= BIT(2); + + sdebug(4, "which=%d sn->maxCmd=%d sn->cmd=%d", which, sn->maxCmd, sn->cmd); + + if((pq = i_dqueue_snd(sp, which)) == NULL) + break; + sdebug(4, "pq=%p", pq); + + pp = &pq->pdu; + bhs = &pp->ipdu.bhs; + switch(bhs->opcode) { + case ISCSI_SCSI_CMD: + sn->itt++; + bhs->itt = htonl(sn->itt); + + case ISCSI_LOGIN_CMD: + case ISCSI_TEXT_CMD: + case ISCSI_LOGOUT_CMD: + case ISCSI_SNACK: + case ISCSI_NOP_OUT: + case ISCSI_TASK_CMD: + bhs->CmdSN = htonl(sn->cmd); + if(bhs->I == 0) + sn->cmd++; + + case ISCSI_WRITE_DATA: + bhs->ExpStSN = htonl(sn->stat + 1); + break; + + default: + // XXX: can this happen? + xdebug("bad opcode=0x%x sn(cmd=0x%x expCmd=0x%x maxCmd=0x%x expStat=0x%x itt=0x%x)", + bhs->opcode, + sn->cmd, sn->expCmd, sn->maxCmd, sn->expStat, sn->itt); + // XXX: and now? + } + + sdebug(4, "opcode=0x%x sn(cmd=0x%x expCmd=0x%x maxCmd=0x%x expStat=0x%x itt=0x%x)", + bhs->opcode, + sn->cmd, sn->expCmd, sn->maxCmd, sn->expStat, sn->itt); + + if(bhs->opcode != ISCSI_NOP_OUT) + /* + | enqued till ack is received + | note: sosend(...) does not mean the packet left + | the host so that freeing resources has to wait + */ + i_nqueue_hld(sp, pq); + + error = isc_sendPDU(sp, pq); + if(bhs->opcode == ISCSI_NOP_OUT) + pdu_free(sp->isc, pq); + if(error) { + xdebug("error=%d opcode=0x%x ccb=%p itt=%x", + error, bhs->opcode, pq->ccb, ntohl(bhs->itt)); + i_remove_hld(sp, pq); + switch(error) { + case EPIPE: + sp->flags &= ~ISC_LINK_UP; + + case EAGAIN: + xdebug("requed"); + i_rqueue_pdu(sp, pq); + break; + + default: + if(pq->ccb) { + xdebug("back to cam"); + pq->ccb->ccb_h.status |= CAM_REQUEUE_REQ; // some better error? + XPT_DONE(sp, pq->ccb); + pdu_free(sp->isc, pq); + } + else + xdebug("we lost it!"); + } + } + } + return error; +} + +/* + | survives link breakdowns. + */ +static void +ism_out(void *vp) +{ + isc_session_t *sp = (isc_session_t *)vp; + int error; + + debug_called(8); + + sp->flags |= ISC_SM_RUNNING; + sdebug(3, "started sp->flags=%x", sp->flags); + do { + if((sp->flags & ISC_HOLD) == 0) { + error = proc_out(sp); + if(error) { + sdebug(3, "error=%d", error); + } + } + mtx_lock(&sp->io_mtx); + if((sp->flags & ISC_LINK_UP) == 0) { + sdebug(3, "ISC_LINK_UP==0, sp->flags=%x ", sp->flags); + if(sp->soc != NULL) + sdebug(3, "so_state=%x", sp->soc->so_state); + wakeup(&sp->soc); + } + + if(!(sp->flags & ISC_OQNOTEMPTY)) { + sp->flags |= ISC_OWAITING; + if(msleep(&sp->flags, &sp->io_mtx, PRIBIO, "isc_proc", hz*30) == EWOULDBLOCK) { + if(sp->flags & ISC_CON_RUNNING) + _nop_out(sp); + } + sp->flags &= ~ISC_OWAITING; + } + sp->flags &= ~ISC_OQNOTEMPTY; + mtx_unlock(&sp->io_mtx); + } while(sp->flags & ISC_SM_RUN); + + sp->flags &= ~ISC_SM_RUNNING; + sdebug(3, "dropped ISC_SM_RUNNING"); + + wakeup(&sp->soc); + wakeup(sp); // XXX: do we need this one? + +#if __FreeBSD_version >= 700000 + destroy_dev(sp->dev); +#endif + + debug(3, "terminated sp=%p sp->sid=%d", sp, sp->sid); + +#if __FreeBSD_version >= 800000 + kproc_exit(0); +#else + kthread_exit(0); +#endif +} + +#if 0 +static int +isc_dump_options(SYSCTL_HANDLER_ARGS) +{ + int error; + isc_session_t *sp; + char buf[1024], *bp; + + sp = (isc_session_t *)arg1; + bp = buf; + sprintf(bp, "targetname='%s'", sp->opt.targetName); + bp += strlen(bp); + sprintf(bp, " targetname='%s'", sp->opt.targetAddress); + error = SYSCTL_OUT(req, buf, strlen(buf)); + return error; +} +#endif + +static int +isc_dump_stats(SYSCTL_HANDLER_ARGS) +{ + isc_session_t *sp; + struct isc_softc *sc; + char buf[1024], *bp; + int error, n; + + sp = (isc_session_t *)arg1; + sc = sp->isc; + + bp = buf; + n = sizeof(buf); + snprintf(bp, n, "recv=%d sent=%d", sp->stats.nrecv, sp->stats.nsent); + bp += strlen(bp); + n -= strlen(bp); + snprintf(bp, n, " flags=0x%08x pdus-alloc=%d pdus-max=%d", + sp->flags, sc->npdu_alloc, sc->npdu_max); + bp += strlen(bp); + n -= strlen(bp); + snprintf(bp, n, " cws=%d cmd=%x exp=%x max=%x stat=%x itt=%x", + sp->cws, sp->sn.cmd, sp->sn.expCmd, sp->sn.maxCmd, sp->sn.stat, sp->sn.itt); + error = SYSCTL_OUT(req, buf, strlen(buf)); + return error; +} + +static int +isc_sysctl_targetName(SYSCTL_HANDLER_ARGS) +{ + char buf[128], **cp; + int error; + + cp = (char **)arg1; + snprintf(buf, sizeof(buf), "%s", *cp); + error = SYSCTL_OUT(req, buf, strlen(buf)); + return error; +} + +static int +isc_sysctl_targetAddress(SYSCTL_HANDLER_ARGS) +{ + char buf[128], **cp; + int error; + + cp = (char **)arg1; + snprintf(buf, sizeof(buf), "%s", *cp); + error = SYSCTL_OUT(req, buf, strlen(buf)); + return error; +} + +static void +isc_add_sysctls(isc_session_t *sp) +{ + debug_called(8); + sdebug(6, "sid=%d %s", sp->sid, devtoname(sp->dev)); + + sysctl_ctx_init(&sp->clist); + sp->oid = SYSCTL_ADD_NODE(&sp->clist, + SYSCTL_CHILDREN(sp->isc->oid), + OID_AUTO, + devtoname(sp->dev) + 5, // iscsi0 + CTLFLAG_RD, + 0, + "initiator"); + SYSCTL_ADD_PROC(&sp->clist, + SYSCTL_CHILDREN(sp->oid), + OID_AUTO, + "targetname", + CTLTYPE_STRING | CTLFLAG_RD, + (void *)&sp->opt.targetName, 0, + isc_sysctl_targetName, "A", "target name"); + + SYSCTL_ADD_PROC(&sp->clist, + SYSCTL_CHILDREN(sp->oid), + OID_AUTO, + "targeaddress", + CTLTYPE_STRING | CTLFLAG_RD, + (void *)&sp->opt.targetAddress, 0, + isc_sysctl_targetAddress, "A", "target address"); + + SYSCTL_ADD_PROC(&sp->clist, + SYSCTL_CHILDREN(sp->oid), + OID_AUTO, + "stats", + CTLTYPE_STRING | CTLFLAG_RD, + (void *)sp, 0, + isc_dump_stats, "A", "statistics"); + + SYSCTL_ADD_INT(&sp->clist, + SYSCTL_CHILDREN(sp->oid), + OID_AUTO, + "douio", + CTLFLAG_RW, + &sp->douio, 0, "enable uio on read"); +} + +void +ism_stop(isc_session_t *sp) +{ + struct isc_softc *sc = sp->isc; + int n; + + debug_called(8); + sdebug(2, "terminating"); + /* + | first stop the receiver + */ + isc_stop_receiver(sp); + /* + | now stop the xmitter + */ + n = 5; + sp->flags &= ~ISC_SM_RUN; + while(n-- && (sp->flags & ISC_SM_RUNNING)) { + sdebug(2, "n=%d", n); + wakeup(&sp->flags); + tsleep(sp, PRIBIO, "-", 5*hz); + } + sdebug(2, "final n=%d", n); + sp->flags &= ~ISC_FFPHASE; + + iscsi_cleanup(sp); + + (void)i_pdu_flush(sp); + + ic_destroy(sp); + + sx_xlock(&sc->unit_sx); + free_unr(sc->unit, sp->sid); + sx_xunlock(&sc->unit_sx); + + mtx_lock(&sc->isc_mtx); + TAILQ_REMOVE(&sc->isc_sess, sp, sp_link); + sc->nsess--; + mtx_unlock(&sc->isc_mtx); + +#if __FreeBSD_version < 700000 + destroy_dev(sp->dev); +#endif + + mtx_destroy(&sp->rsp_mtx); + mtx_destroy(&sp->rsv_mtx); + mtx_destroy(&sp->hld_mtx); + mtx_destroy(&sp->snd_mtx); + mtx_destroy(&sp->io_mtx); + + i_freeopt(&sp->opt); + + if(sysctl_ctx_free(&sp->clist)) + xdebug("sysctl_ctx_free failed"); + + free(sp, M_ISCSI); +} + +int +ism_start(isc_session_t *sp) +{ + debug_called(8); + /* + | now is a good time to do some initialization + */ + TAILQ_INIT(&sp->rsp); + TAILQ_INIT(&sp->rsv); + TAILQ_INIT(&sp->csnd); + TAILQ_INIT(&sp->isnd); + TAILQ_INIT(&sp->wsnd); + TAILQ_INIT(&sp->hld); + + mtx_init(&sp->rsv_mtx, "iscsi-rsv", NULL, MTX_DEF); + mtx_init(&sp->rsp_mtx, "iscsi-rsp", NULL, MTX_DEF); + mtx_init(&sp->snd_mtx, "iscsi-snd", NULL, MTX_DEF); + mtx_init(&sp->hld_mtx, "iscsi-hld", NULL, MTX_DEF); + mtx_init(&sp->io_mtx, "iscsi-io", NULL, MTX_DEF); + + isc_add_sysctls(sp); + + sp->flags |= ISC_SM_RUN; + + debug(4, "starting ism_proc: sp->sid=%d", sp->sid); + +#if __FreeBSD_version >= 800000 + return kproc_create(ism_out, sp, &sp->stp, 0, 0, "isc_out %d", sp->sid); +#else + return kthread_create(ism_out, sp, &sp->stp, 0, 0, "isc_out %d", sp->sid); +#endif +} diff -urN p4/freebsd/src/sys/dev/iscsi_initiator/isc_soc.c p4/iscsi/sys/dev/iscsi_initiator/isc_soc.c --- p4/freebsd/src/sys/dev/iscsi_initiator/isc_soc.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/sys/dev/iscsi_initiator/isc_soc.c 2013-07-23 23:45:08.000000000 +0200 @@ -0,0 +1,701 @@ +/*- + * Copyright (c) 2005-2010 Daniel Braniss + * 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. + * + * 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. + * + */ +/* + | $Id: isc_soc.c 998 2009-12-20 10:32:45Z danny $ + */ +#include +__FBSDID("$FreeBSD: head/sys/dev/iscsi/initiator/isc_soc.c 248188 2013-03-12 08:59:51Z glebius $"); + +#include "opt_iscsi_initiator.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#ifndef NO_USE_MBUF +#define USE_MBUF +#endif + +#ifdef USE_MBUF +static int ou_refcnt = 0; +/* + | function for freeing external storage for mbuf + */ +static void +ext_free(void *a, void *b) +{ + pduq_t *pq = b; + + if(pq->buf != NULL) { + debug(3, "ou_refcnt=%d a=%p b=%p", ou_refcnt, a, pq->buf); + free(pq->buf, M_ISCSIBUF); + pq->buf = NULL; + } +} + +int +isc_sendPDU(isc_session_t *sp, pduq_t *pq) +{ + struct mbuf *mh, **mp; + pdu_t *pp = &pq->pdu; + int len, error; + + debug_called(8); + /* + | mbuf for the iSCSI header + */ + MGETHDR(mh, M_WAITOK, MT_DATA); + mh->m_pkthdr.rcvif = NULL; + mh->m_next = NULL; + mh->m_len = sizeof(union ipdu_u); + + if(ISOK2DIG(sp->hdrDigest, pp)) { + pp->hdr_dig = sp->hdrDigest(&pp->ipdu, sizeof(union ipdu_u), 0); + mh->m_len += sizeof(pp->hdr_dig); + if(pp->ahs_len) { + debug(2, "ahs_len=%d", pp->ahs_len); + pp->hdr_dig = sp->hdrDigest(&pp->ahs_addr, pp->ahs_len, pp->hdr_dig); + } + debug(3, "pp->hdr_dig=%04x", htonl(pp->hdr_dig)); + } + if(pp->ahs_len) { + /* + | Add any AHS to the iSCSI hdr mbuf + */ + if((mh->m_len + pp->ahs_len) < MHLEN) { + MH_ALIGN(mh, mh->m_len + pp->ahs_len); + bcopy(&pp->ipdu, mh->m_data, mh->m_len); + bcopy(pp->ahs_addr, mh->m_data + mh->m_len, pp->ahs_len); + mh->m_len += pp->ahs_len; + } + else + panic("len AHS=%d too big, not impleneted yet", pp->ahs_len); + } + else { + MH_ALIGN(mh, mh->m_len); + bcopy(&pp->ipdu, mh->m_data, mh->m_len); + } + mh->m_pkthdr.len = mh->m_len; + mp = &mh->m_next; + if(pp->ds_len && pq->pdu.ds_addr) { + struct mbuf *md; + int off = 0; + + len = pp->ds_len; + while(len > 0) { + int l; + + MGET(md, M_WAITOK, MT_DATA); + md->m_ext.ref_cnt = &ou_refcnt; + l = min(MCLBYTES, len); + debug(4, "setting ext_free(arg=%p len/l=%d/%d)", pq->buf, len, l); + MEXTADD(md, pp->ds_addr + off, l, ext_free, +#if __FreeBSD_version >= 800000 + pp->ds_addr + off, +#endif + pq, 0, EXT_EXTREF); + md->m_len = l; + md->m_next = NULL; + mh->m_pkthdr.len += l; + *mp = md; + mp = &md->m_next; + len -= l; + off += l; + } + if(((pp->ds_len & 03) != 0) || ISOK2DIG(sp->dataDigest, pp)) { + MGET(md, M_WAITOK, MT_DATA); + if(pp->ds_len & 03) + len = 4 - (pp->ds_len & 03); + else + len = 0; + md->m_len = len; + if(ISOK2DIG(sp->dataDigest, pp)) + md->m_len += sizeof(pp->ds_dig); + M_ALIGN(md, md->m_len); + if(ISOK2DIG(sp->dataDigest, pp)) { + pp->ds_dig = sp->dataDigest(pp->ds_addr, pp->ds_len, 0); + if(len) { + bzero(md->m_data, len); // RFC says SHOULD be 0 + pp->ds_dig = sp->dataDigest(md->m_data, len, pp->ds_dig); + } + bcopy(&pp->ds_dig, md->m_data+len, sizeof(pp->ds_dig)); + } + md->m_next = NULL; + mh->m_pkthdr.len += md->m_len; + *mp = md; + } + } + if((error = sosend(sp->soc, NULL, NULL, mh, 0, 0, sp->td)) != 0) { + sdebug(2, "error=%d", error); + return error; + } + sp->stats.nsent++; + getbintime(&sp->stats.t_sent); + return 0; +} +#else /* NO_USE_MBUF */ +int +isc_sendPDU(isc_session_t *sp, pduq_t *pq) +{ + struct uio *uio = &pq->uio; + struct iovec *iv; + pdu_t *pp = &pq->pdu; + int len, error; + + debug_called(8); + + bzero(uio, sizeof(struct uio)); + uio->uio_rw = UIO_WRITE; + uio->uio_segflg = UIO_SYSSPACE; + uio->uio_td = sp->td; + uio->uio_iov = iv = pq->iov; + + iv->iov_base = &pp->ipdu; + iv->iov_len = sizeof(union ipdu_u); + uio->uio_resid = iv->iov_len; + iv++; + if(ISOK2DIG(sp->hdrDigest, pp)) + pq->pdu.hdr_dig = sp->hdrDigest(&pp->ipdu, sizeof(union ipdu_u), 0); + if(pp->ahs_len) { + iv->iov_base = pp->ahs_addr; + iv->iov_len = pp->ahs_len; + uio->uio_resid += iv->iov_len; + iv++; + if(ISOK2DIG(sp->hdrDigest, pp)) + pp->hdr_dig = sp->hdrDigest(&pp->ahs_addr, pp->ahs_len, pp->hdr_dig); + } + if(ISOK2DIG(sp->hdrDigest, pp)) { + debug(3, "hdr_dig=%04x", htonl(pp->hdr_dig)); + iv->iov_base = &pp->hdr_dig; + iv->iov_len = sizeof(int); + uio->uio_resid += iv->iov_len ; + iv++; + } + if(pq->pdu.ds_addr && pp->ds_len) { + iv->iov_base = pp->ds_addr; + iv->iov_len = pp->ds_len; + while(iv->iov_len & 03) // the specs say it must be int alligned + iv->iov_len++; + uio->uio_resid += iv->iov_len ; + iv++; + if(ISOK2DIG(sp->dataDigest, pp)) { + pp->ds_dig = sp->dataDigest(pp->ds, pp->ds_len, 0); + iv->iov_base = &pp->ds_dig; + iv->iov_len = sizeof(pp->ds_dig); + uio->uio_resid += iv->iov_len ; + iv++; + } + } + uio->uio_iovcnt = iv - pq->iov; + sdebug(4, "pq->len=%d uio->uio_resid=%d uio->uio_iovcnt=%d", pq->len, + uio->uio_resid, + uio->uio_iovcnt); + + sdebug(4, "opcode=%x iovcnt=%d uio_resid=%d itt=%x", + pp->ipdu.bhs.opcode, uio->uio_iovcnt, uio->uio_resid, + ntohl(pp->ipdu.bhs.itt)); + sdebug(5, "sp=%p sp->soc=%p uio=%p sp->td=%p", + sp, sp->soc, uio, sp->td); + do { + len = uio->uio_resid; + error = sosend(sp->soc, NULL, uio, 0, 0, 0, sp->td); + if(uio->uio_resid == 0 || error || len == uio->uio_resid) { + if(uio->uio_resid) { + sdebug(2, "uio->uio_resid=%d uio->uio_iovcnt=%d error=%d len=%d", + uio->uio_resid, uio->uio_iovcnt, error, len); + if(error == 0) + error = EAGAIN; // 35 + } + break; + } + /* + | XXX: untested code + */ + sdebug(1, "uio->uio_resid=%d uio->uio_iovcnt=%d", + uio->uio_resid, uio->uio_iovcnt); + iv = uio->uio_iov; + len -= uio->uio_resid; + while(uio->uio_iovcnt > 0) { + if(iv->iov_len > len) { + caddr_t bp = (caddr_t)iv->iov_base; + + iv->iov_len -= len; + iv->iov_base = (void *)&bp[len]; + break; + } + len -= iv->iov_len; + uio->uio_iovcnt--; + uio->uio_iov++; + iv++; + } + } while(uio->uio_resid); + + if(error == 0) { + sp->stats.nsent++; + getbintime(&sp->stats.t_sent); + } + + return error; +} +#endif /* USE_MBUF */ + +/* + | wait till a PDU header is received + | from the socket. + */ +/* + The format of the BHS is: + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0|.|I| Opcode |F| Opcode-specific fields | + +---------------+---------------+---------------+---------------+ + 4|TotalAHSLength | DataSegmentLength | + +---------------+---------------+---------------+---------------+ + 8| LUN or Opcode-specific fields | + + + + 12| | + +---------------+---------------+---------------+---------------+ + 16| Initiator Task Tag | + +---------------+---------------+---------------+---------------+ + 20/ Opcode-specific fields / + +/ / + +---------------+---------------+---------------+---------------+ + 48 + */ +static __inline int +so_getbhs(isc_session_t *sp) +{ + bhs_t *bhs = &sp->bhs; + struct uio *uio = &sp->uio; + struct iovec *iov = &sp->iov; + int error, flags; + + debug_called(8); + + iov->iov_base = bhs; + iov->iov_len = sizeof(bhs_t); + + uio->uio_iov = iov; + uio->uio_iovcnt = 1; + uio->uio_rw = UIO_READ; + uio->uio_segflg = UIO_SYSSPACE; + uio->uio_td = curthread; // why ... + uio->uio_resid = sizeof(bhs_t); + + flags = MSG_WAITALL; + error = soreceive(sp->soc, NULL, uio, 0, 0, &flags); + + if(error) + debug(2, +#if __FreeBSD_version > 800000 + "error=%d so_error=%d uio->uio_resid=%zd iov.iov_len=%zd", +#else + "error=%d so_error=%d uio->uio_resid=%d iov.iov_len=%zd", +#endif + error, + sp->soc->so_error, uio->uio_resid, iov->iov_len); + if(!error && (uio->uio_resid > 0)) { + error = EPIPE; // was EAGAIN + debug(2, +#if __FreeBSD_version > 800000 + "error=%d so_error=%d uio->uio_resid=%zd iov.iov_len=%zd so_state=%x", +#else + "error=%d so_error=%d uio->uio_resid=%d iov.iov_len=%zd so_state=%x", +#endif + error, + sp->soc->so_error, uio->uio_resid, iov->iov_len, sp->soc->so_state); + } + return error; +} + +/* + | so_recv gets called when + | an iSCSI header has been received. + | Note: the designers had no intentions + | in making programmer's life easy. + */ +static int +so_recv(isc_session_t *sp, pduq_t *pq) +{ + sn_t *sn = &sp->sn; + struct uio *uio = &pq->uio; + pdu_t *pp = &pq->pdu; + bhs_t *bhs = &pp->ipdu.bhs; + struct iovec *iov = pq->iov; + int error; + u_int len; + u_int max, exp; + int flags = MSG_WAITALL; + + debug_called(8); + /* + | now calculate how much data should be in the buffer + */ + uio->uio_iov = iov; + uio->uio_iovcnt = 0; + len = 0; + if(bhs->AHSLength) { + debug(2, "bhs->AHSLength=%d", bhs->AHSLength); + pp->ahs_len = bhs->AHSLength * 4; + len += pp->ahs_len; + pp->ahs_addr = malloc(pp->ahs_len, M_TEMP, M_WAITOK); // XXX: could get stuck here + iov->iov_base = pp->ahs_addr; + iov->iov_len = pp->ahs_len; + uio->uio_iovcnt++; + iov++; + } + if(ISOK2DIG(sp->hdrDigest, pp)) { + len += sizeof(pp->hdr_dig); + iov->iov_base = &pp->hdr_dig; + iov->iov_len = sizeof(pp->hdr_dig); + uio->uio_iovcnt++; + } + if(len) { + uio->uio_rw = UIO_READ; + uio->uio_segflg = UIO_SYSSPACE; + uio->uio_resid = len; + uio->uio_td = sp->td; // why ... + error = soreceive(sp->soc, NULL, uio, NULL, NULL, &flags); + //if(error == EAGAIN) + // XXX: this needs work! it hangs iscontrol + if(error || uio->uio_resid) { + debug(2, +#if __FreeBSD_version > 800000 + "len=%d error=%d uio->uio_resid=%zd", +#else + "len=%d error=%d uio->uio_resid=%d", +#endif + len, error, uio->uio_resid); + goto out; + } + if(ISOK2DIG(sp->hdrDigest, pp)) { + bhs_t *bhs; + u_int digest; + + bhs = (bhs_t *)&pp->ipdu; + digest = sp->hdrDigest(bhs, sizeof(bhs_t), 0); + if(pp->ahs_len) + digest = sp->hdrDigest(pp->ahs_addr, pp->ahs_len, digest); + if(pp->hdr_dig != digest) { + debug(2, "bad header digest: received=%x calculated=%x", pp->hdr_dig, digest); + // XXX: now what? + error = EIO; + goto out; + } + } + if(pp->ahs_len) { + debug(2, "ahs len=%x type=%x spec=%x", + pp->ahs_addr->len, pp->ahs_addr->type, pp->ahs_addr->spec); + // XXX: till I figure out what to do with this + free(pp->ahs_addr, M_TEMP); + } + pq->len += len; // XXX: who needs this? + bzero(uio, sizeof(struct uio)); + len = 0; + } + + if(bhs->DSLength) { + len = bhs->DSLength; +#if BYTE_ORDER == LITTLE_ENDIAN + len = ((len & 0x00ff0000) >> 16) + | (len & 0x0000ff00) + | ((len & 0x000000ff) << 16); +#endif + pp->ds_len = len; + if((sp->opt.maxRecvDataSegmentLength > 0) && (len > sp->opt.maxRecvDataSegmentLength)) { + xdebug("impossible PDU length(%d) opt.maxRecvDataSegmentLength=%d", + len, sp->opt.maxRecvDataSegmentLength); + log(LOG_ERR, + "so_recv: impossible PDU length(%d) from iSCSI %s/%s\n", + len, sp->opt.targetAddress, sp->opt.targetName); + /* + | XXX: this will really screwup the stream. + | should clear up the buffer till a valid header + | is found, or just close connection ... + | should read the RFC. + */ + error = E2BIG; + goto out; + } + while(len & 03) + len++; + if(ISOK2DIG(sp->dataDigest, pp)) + len += 4; + uio->uio_resid = len; + uio->uio_td = sp->td; // why ... + pq->len += len; // XXX: do we need this? + error = soreceive(sp->soc, NULL, uio, &pq->mp, NULL, &flags); + //if(error == EAGAIN) + // XXX: this needs work! it hangs iscontrol + if(error || uio->uio_resid) + goto out; + if(ISOK2DIG(sp->dataDigest, pp)) { + struct mbuf *m; + u_int digest, ds_len, cnt; + + // get the received digest + m_copydata(pq->mp, + len - sizeof(pp->ds_dig), + sizeof(pp->ds_dig), + (caddr_t)&pp->ds_dig); + // calculate all mbufs + digest = 0; + ds_len = len - sizeof(pp->ds_dig); + for(m = pq->mp; m != NULL; m = m->m_next) { + cnt = MIN(ds_len, m->m_len); + digest = sp->dataDigest(mtod(m, char *), cnt, digest); + ds_len -= cnt; + if(ds_len == 0) + break; + } + if(digest != pp->ds_dig) { + sdebug(1, "bad data digest: received=%x calculated=%x", pp->ds_dig, digest); + error = EIO; // XXX: find a better error + goto out; + } + KASSERT(ds_len == 0, ("ds_len not zero")); + } + } + sdebug(6, "len=%d] opcode=0x%x ahs_len=0x%x ds_len=0x%x", + pq->len, bhs->opcode, pp->ahs_len, pp->ds_len); + + max = ntohl(bhs->MaxCmdSN); + exp = ntohl(bhs->ExpStSN); + if(max < exp - 1 && + max > exp - _MAXINCR) { + sdebug(2, "bad cmd window size"); + error = EIO; // XXX: for now; + goto out; // error + } + if(SNA_GT(max, sn->maxCmd)) + sn->maxCmd = max; + if(SNA_GT(exp, sn->expCmd)) + sn->expCmd = exp; + /* + | remove from the holding queue packets + | that have been acked and don't need + | further processing. + */ + i_acked_hld(sp, NULL); + + sp->cws = sn->maxCmd - sn->expCmd + 1; + + return 0; + + out: + // XXX: need some work here + if(pp->ahs_len) { + // XXX: till I figure out what to do with this + free(pp->ahs_addr, M_TEMP); + } + xdebug("have a problem, error=%d", error); + pdu_free(sp->isc, pq); + if(!error && uio->uio_resid > 0) + error = EPIPE; + return error; +} + +/* + | wait for something to arrive. + | and if the pdu is without errors, process it. + */ +static int +so_input(isc_session_t *sp) +{ + pduq_t *pq; + int error; + + debug_called(8); + /* + | first read in the iSCSI header + */ + error = so_getbhs(sp); + if(error == 0) { + /* + | now read the rest. + */ + pq = pdu_alloc(sp->isc, M_NOWAIT); + if(pq == NULL) { // XXX: might cause a deadlock ... + debug(2, "out of pdus, wait"); + pq = pdu_alloc(sp->isc, M_WAITOK); // OK to WAIT + } + pq->pdu.ipdu.bhs = sp->bhs; + pq->len = sizeof(bhs_t); // so far only the header was read + error = so_recv(sp, pq); + if(error != 0) { + error += 0x800; // XXX: just to see the error. + // terminal error + // XXX: close connection and exit + } + else { + sp->stats.nrecv++; + getbintime(&sp->stats.t_recv); + ism_recv(sp, pq); + } + } + return error; +} + +/* + | one per active (connected) session. + | this thread is responsible for reading + | in packets from the target. + */ +static void +isc_in(void *vp) +{ + isc_session_t *sp = (isc_session_t *)vp; + struct socket *so = sp->soc; + int error; + + debug_called(8); + + sp->flags |= ISC_CON_RUNNING; + error = 0; + while((sp->flags & (ISC_CON_RUN | ISC_LINK_UP)) == (ISC_CON_RUN | ISC_LINK_UP)) { + // XXX: hunting ... + if(sp->soc == NULL || !(so->so_state & SS_ISCONNECTED)) { + debug(2, "sp->soc=%p", sp->soc); + break; + } + error = so_input(sp); + if(error == 0) { + mtx_lock(&sp->io_mtx); + if(sp->flags & ISC_OWAITING) { + wakeup(&sp->flags); + } + mtx_unlock(&sp->io_mtx); + } else if(error == EPIPE) { + break; + } + else if(error == EAGAIN) { + if(so->so_state & SS_ISCONNECTED) + // there seems to be a problem in 6.0 ... + tsleep(sp, PRIBIO, "isc_soc", 2*hz); + } + } + sdebug(2, "terminated, flags=%x so_count=%d so_state=%x error=%d proc=%p", + sp->flags, so->so_count, so->so_state, error, sp->proc); + if((sp->proc != NULL) && sp->signal) { + PROC_LOCK(sp->proc); + kern_psignal(sp->proc, sp->signal); + PROC_UNLOCK(sp->proc); + sp->flags |= ISC_SIGNALED; + sdebug(2, "pid=%d signaled(%d)", sp->proc->p_pid, sp->signal); + } + else { + // we have to do something ourselves + // like closing this session ... + } + /* + | we've been terminated + */ + // do we need this mutex ...? + mtx_lock(&sp->io_mtx); + sp->flags &= ~(ISC_CON_RUNNING | ISC_LINK_UP); + wakeup(&sp->soc); + mtx_unlock(&sp->io_mtx); + + sdebug(2, "dropped ISC_CON_RUNNING"); +#if __FreeBSD_version >= 800000 + kproc_exit(0); +#else + kthread_exit(0); +#endif +} + +void +isc_stop_receiver(isc_session_t *sp) +{ + int n; + + debug_called(8); + sdebug(3, "sp=%p sp->soc=%p", sp, sp? sp->soc: 0); + mtx_lock(&sp->io_mtx); + sp->flags &= ~ISC_LINK_UP; + msleep(&sp->soc, &sp->io_mtx, PRIBIO|PDROP, "isc_stpc", 5*hz); + + soshutdown(sp->soc, SHUT_RD); + + mtx_lock(&sp->io_mtx); + sdebug(3, "soshutdown"); + sp->flags &= ~ISC_CON_RUN; + n = 2; + while(n-- && (sp->flags & ISC_CON_RUNNING)) { + sdebug(3, "waiting n=%d... flags=%x", n, sp->flags); + msleep(&sp->soc, &sp->io_mtx, PRIBIO, "isc_stpc", 5*hz); + } + mtx_unlock(&sp->io_mtx); + + if(sp->fp != NULL) + fdrop(sp->fp, sp->td); + fputsock(sp->soc); + sp->soc = NULL; + sp->fp = NULL; + + sdebug(3, "done"); +} + +void +isc_start_receiver(isc_session_t *sp) +{ + debug_called(8); + + sp->flags |= ISC_CON_RUN | ISC_LINK_UP; +#if __FreeBSD_version >= 800000 + kproc_create +#else + kthread_create +#endif + (isc_in, sp, &sp->soc_proc, 0, 0, "isc_in %d", sp->sid); +} diff -urN p4/freebsd/src/sys/dev/iscsi_initiator/isc_subr.c p4/iscsi/sys/dev/iscsi_initiator/isc_subr.c --- p4/freebsd/src/sys/dev/iscsi_initiator/isc_subr.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/sys/dev/iscsi_initiator/isc_subr.c 2013-07-23 23:45:08.000000000 +0200 @@ -0,0 +1,269 @@ +/*- + * Copyright (c) 2005-2011 Daniel Braniss + * 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. + * + * 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. + * + */ +/* + | iSCSI + | $Id: isc_subr.c 560 2009-05-07 07:37:49Z danny $ + */ + +#include +__FBSDID("$FreeBSD: head/sys/dev/iscsi/initiator/isc_subr.c 234233 2012-04-13 18:21:56Z jpaetzel $"); + +#include "opt_iscsi_initiator.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static MALLOC_DEFINE(M_ISC, "iSC", "iSCSI driver options"); + +static char * +i_strdupin(char *s, size_t maxlen) +{ + size_t len; + char *p, *q; + + p = malloc(maxlen, M_ISC, M_WAITOK); + if(copyinstr(s, p, maxlen, &len)) { + free(p, M_ISC); + return NULL; + } + q = malloc(len, M_ISC, M_WAITOK); + bcopy(p, q, len); + free(p, M_ISC); + + return q; +} +#if __FreeBSD_version < 800000 +/*****************************************************************/ +/* */ +/* CRC LOOKUP TABLE */ +/* ================ */ +/* The following CRC lookup table was generated automagically */ +/* by the Rocksoft^tm Model CRC Algorithm Table Generation */ +/* Program V1.0 using the following model parameters: */ +/* */ +/* Width : 4 bytes. */ +/* Poly : 0x1EDC6F41L */ +/* Reverse : TRUE. */ +/* */ +/* For more information on the Rocksoft^tm Model CRC Algorithm, */ +/* see the document titled "A Painless Guide to CRC Error */ +/* Detection Algorithms" by Ross Williams */ +/* (ross@guest.adelaide.edu.au.). This document is likely to be */ +/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */ +/* */ +/*****************************************************************/ + +static uint32_t crc32Table[256] = { + 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, + 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, + 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, + 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, + 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, + 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, + 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, + 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, + 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, + 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, + 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, + 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, + 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, + 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, + 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, + 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, + 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, + 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, + 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, + 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, + 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, + 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, + 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, + 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, + 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, + 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, + 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, + 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, + 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, + 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, + 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, + 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, + 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, + 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, + 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, + 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, + 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, + 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, + 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, + 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, + 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, + 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, + 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, + 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, + 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, + 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, + 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, + 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, + 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, + 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, + 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, + 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, + 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, + 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, + 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, + 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, + 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, + 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, + 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, + 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, + 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, + 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, + 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, + 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L +}; + +static __inline int +calculate_crc32c(uint32_t crc, const void *buf, size_t size) +{ + const uint8_t *p = buf; + + while (size--) + crc = crc32Table[(crc ^ *p++) & 0xff] ^ (crc >> 8); + return crc; +} +#endif + +static uint32_t +i_crc32c(const void *buf, size_t size, uint32_t crc) +{ + crc = crc ^ 0xffffffff; + crc = calculate_crc32c(crc, buf, size); + crc = crc ^ 0xffffffff; + return crc; +} + +/* + | XXX: not finished coding + */ +int +i_setopt(isc_session_t *sp, isc_opt_t *opt) +{ + if(opt->maxRecvDataSegmentLength > 0) { + sp->opt.maxRecvDataSegmentLength = opt->maxRecvDataSegmentLength; + sdebug(2, "maxRecvDataSegmentLength=%d", sp->opt.maxRecvDataSegmentLength); + } + if(opt->maxXmitDataSegmentLength > 0) { + // danny's RFC + sp->opt.maxXmitDataSegmentLength = opt->maxXmitDataSegmentLength; + sdebug(2, "opt.maXmitDataSegmentLength=%d", sp->opt.maxXmitDataSegmentLength); + } + if(opt->maxBurstLength != 0) { + sp->opt.maxBurstLength = opt->maxBurstLength; + sdebug(2, "opt.maxBurstLength=%d", sp->opt.maxBurstLength); + } + + if(opt->targetAddress != NULL) { + if(sp->opt.targetAddress != NULL) + free(sp->opt.targetAddress, M_ISC); + sp->opt.targetAddress = i_strdupin(opt->targetAddress, 128); + sdebug(2, "opt.targetAddress='%s'", sp->opt.targetAddress); + } + if(opt->targetName != NULL) { + if(sp->opt.targetName != NULL) + free(sp->opt.targetName, M_ISC); + sp->opt.targetName = i_strdupin(opt->targetName, 128); + sdebug(2, "opt.targetName='%s'", sp->opt.targetName); + } + if(opt->initiatorName != NULL) { + if(sp->opt.initiatorName != NULL) + free(sp->opt.initiatorName, M_ISC); + sp->opt.initiatorName = i_strdupin(opt->initiatorName, 128); + sdebug(2, "opt.initiatorName='%s'", sp->opt.initiatorName); + } + + if(opt->maxluns > 0) { + if(opt->maxluns > ISCSI_MAX_LUNS) + sp->opt.maxluns = ISCSI_MAX_LUNS; // silently chop it down ... + sp->opt.maxluns = opt->maxluns; + sdebug(2, "opt.maxluns=%d", sp->opt.maxluns); + } + + if(opt->headerDigest != NULL) { + sdebug(2, "opt.headerDigest='%s'", opt->headerDigest); + if(strcmp(opt->headerDigest, "CRC32C") == 0) { + sp->hdrDigest = (digest_t *)i_crc32c; + sdebug(2, "opt.headerDigest set"); + } + } + if(opt->dataDigest != NULL) { + sdebug(2, "opt.dataDigest='%s'", opt->headerDigest); + if(strcmp(opt->dataDigest, "CRC32C") == 0) { + sp->dataDigest = (digest_t *)i_crc32c; + sdebug(2, "opt.dataDigest set"); + } + } + + return 0; +} + +void +i_freeopt(isc_opt_t *opt) +{ + debug_called(8); + + if(opt->targetAddress != NULL) { + free(opt->targetAddress, M_ISC); + opt->targetAddress = NULL; + } + if(opt->targetName != NULL) { + free(opt->targetName, M_ISC); + opt->targetName = NULL; + } + if(opt->initiatorName != NULL) { + free(opt->initiatorName, M_ISC); + opt->initiatorName = NULL; + } +} diff -urN p4/freebsd/src/sys/dev/iscsi_initiator/iscsi.c p4/iscsi/sys/dev/iscsi_initiator/iscsi.c --- p4/freebsd/src/sys/dev/iscsi_initiator/iscsi.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/sys/dev/iscsi_initiator/iscsi.c 2013-07-23 23:45:08.000000000 +0200 @@ -0,0 +1,879 @@ +/*- + * Copyright (c) 2005-2011 Daniel Braniss + * 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. + * + * 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. + * + */ +/* + | $Id: iscsi.c 752 2009-08-20 11:23:28Z danny $ + */ + +#include +__FBSDID("$FreeBSD: head/sys/dev/iscsi/initiator/iscsi.c 247602 2013-03-02 00:53:12Z pjd $"); + +#include "opt_iscsi_initiator.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +static char *iscsi_driver_version = "2.3.1"; + +static struct isc_softc *isc; + +MALLOC_DEFINE(M_ISCSI, "iSCSI", "iSCSI driver"); +MALLOC_DEFINE(M_ISCSIBUF, "iSCbuf", "iSCSI buffers"); +static MALLOC_DEFINE(M_TMP, "iSCtmp", "iSCSI tmp"); + +#ifdef ISCSI_INITIATOR_DEBUG +int iscsi_debug = ISCSI_INITIATOR_DEBUG; +SYSCTL_INT(_debug, OID_AUTO, iscsi_initiator, CTLFLAG_RW, &iscsi_debug, 0, + "iSCSI driver debug flag"); + +struct mtx iscsi_dbg_mtx; +#endif + +static int max_sessions = MAX_SESSIONS; +SYSCTL_INT(_net, OID_AUTO, iscsi_initiator_max_sessions, CTLFLAG_RDTUN, &max_sessions, MAX_SESSIONS, + "Max sessions allowed"); +static int max_pdus = MAX_PDUS; +SYSCTL_INT(_net, OID_AUTO, iscsi_initiator_max_pdus, CTLFLAG_RDTUN, &max_pdus, MAX_PDUS, + "Max pdu pool"); + +static char isid[6+1] = { + 0x80, + 'D', + 'I', + 'B', + '0', + '0', + 0 +}; + +static int i_create_session(struct cdev *dev, int *ndev); + +static int i_ping(struct cdev *dev); +static int i_send(struct cdev *dev, caddr_t arg, struct thread *td); +static int i_recv(struct cdev *dev, caddr_t arg, struct thread *td); +static int i_setsoc(isc_session_t *sp, int fd, struct thread *td); +static int i_fullfeature(struct cdev *dev, int flag); + +static d_open_t iscsi_open; +static d_close_t iscsi_close; +static d_ioctl_t iscsi_ioctl; +#ifdef ISCSI_INITIATOR_DEBUG +static d_read_t iscsi_read; +#endif + +static struct cdevsw iscsi_cdevsw = { + .d_version = D_VERSION, + .d_open = iscsi_open, + .d_close = iscsi_close, + .d_ioctl = iscsi_ioctl, +#ifdef ISCSI_INITIATOR_DEBUG + .d_read = iscsi_read, +#endif + .d_name = "iSCSI", +}; + +static int +iscsi_open(struct cdev *dev, int flags, int otype, struct thread *td) +{ + debug_called(8); + + debug(7, "dev=%d", dev2unit(dev)); + + if(dev2unit(dev) > max_sessions) { + // should not happen + return ENODEV; + } + return 0; +} + +static int +iscsi_close(struct cdev *dev, int flag, int otyp, struct thread *td) +{ + isc_session_t *sp; + + debug_called(8); + + debug(3, "session=%d flag=%x", dev2unit(dev), flag); + + if(dev2unit(dev) == max_sessions) { + return 0; + } + sp = dev->si_drv2; + if(sp != NULL) { + sdebug(3, "sp->flags=%x", sp->flags ); + /* + | if still in full phase, this probably means + | that something went realy bad. + | it could be a result from 'shutdown', in which case + | we will ignore it (so buffers can be flushed). + | the problem is that there is no way of differentiating + | between a shutdown procedure and 'iscontrol' dying. + */ + if(sp->flags & ISC_FFPHASE) + // delay in case this is a shutdown. + tsleep(sp, PRIBIO, "isc-cls", 60*hz); + ism_stop(sp); + } + debug(2, "done"); + return 0; +} + +static int +iscsi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode, struct thread *td) +{ + struct isc_softc *sc; + isc_session_t *sp; + isc_opt_t *opt; + int error; + + debug_called(8); + + error = 0; + if(dev2unit(dev) == max_sessions) { + /* + | non Session commands + */ + sc = dev->si_drv1; + if(sc == NULL) + return ENXIO; + + switch(cmd) { + case ISCSISETSES: + error = i_create_session(dev, (int *)arg); + if(error == 0) + break; + + default: + error = ENXIO; + } + return error; + } + /* + | session commands + */ + sp = dev->si_drv2; + if(sp == NULL) + return ENXIO; + + sdebug(6, "dev=%d cmd=%d", dev2unit(dev), (int)(cmd & 0xff)); + + switch(cmd) { + case ISCSISETSOC: + error = i_setsoc(sp, *(u_int *)arg, td); + break; + + case ISCSISETOPT: + opt = (isc_opt_t *)arg; + error = i_setopt(sp, opt); + break; + + case ISCSISEND: + error = i_send(dev, arg, td); + break; + + case ISCSIRECV: + error = i_recv(dev, arg, td); + break; + + case ISCSIPING: + error = i_ping(dev); + break; + + case ISCSISTART: + error = sp->soc == NULL? ENOTCONN: i_fullfeature(dev, 1); + if(error == 0) { + sp->proc = td->td_proc; + SYSCTL_ADD_INT(&sp->clist, SYSCTL_CHILDREN(sp->oid), + OID_AUTO, "pid", CTLFLAG_RD, + &sp->proc->p_pid, sizeof(pid_t), "control process id"); + } + break; + + case ISCSIRESTART: + error = sp->soc == NULL? ENOTCONN: i_fullfeature(dev, 2); + break; + + case ISCSISTOP: + error = i_fullfeature(dev, 0); + break; + + case ISCSISIGNAL: { + int sig = *(int *)arg; + + if(sig < 0 || sig > _SIG_MAXSIG) + error = EINVAL; + else + sp->signal = sig; + break; + } + + case ISCSIGETCAM: { + iscsi_cam_t *cp = (iscsi_cam_t *)arg; + + error = ic_getCamVals(sp, cp); + break; + } + + default: + error = ENOIOCTL; + } + + return error; +} + +static int +iscsi_read(struct cdev *dev, struct uio *uio, int ioflag) +{ +#ifdef ISCSI_INITIATOR_DEBUG + struct isc_softc *sc; + isc_session_t *sp; + pduq_t *pq; + char buf[1024]; + + sc = dev->si_drv1; + sp = dev->si_drv2; + if(dev2unit(dev) == max_sessions) { + sprintf(buf, "/----- Session ------/\n"); + uiomove(buf, strlen(buf), uio); + int i = 0; + + TAILQ_FOREACH(sp, &sc->isc_sess, sp_link) { + if(uio->uio_resid == 0) + return 0; + sprintf(buf, "%03d] '%s' '%s'\n", i++, sp->opt.targetAddress, sp->opt.targetName); + uiomove(buf, strlen(buf), uio); + } + sprintf(buf, "free npdu_alloc=%d, npdu_max=%d\n", sc->npdu_alloc, sc->npdu_max); + uiomove(buf, strlen(buf), uio); + } + else { + int i = 0; + struct socket *so = sp->soc; +#define pukeit(i, pq) do {\ + sprintf(buf, "%03d] %06x %02x %06x %06x %jd\n",\ + i, ntohl(pq->pdu.ipdu.bhs.CmdSN),\ + pq->pdu.ipdu.bhs.opcode, ntohl(pq->pdu.ipdu.bhs.itt),\ + ntohl(pq->pdu.ipdu.bhs.ExpStSN),\ + (intmax_t)pq->ts.sec);\ + } while(0) + + sprintf(buf, "%d/%d /---- hld -----/\n", sp->stats.nhld, sp->stats.max_hld); + uiomove(buf, strlen(buf), uio); + TAILQ_FOREACH(pq, &sp->hld, pq_link) { + if(uio->uio_resid == 0) + return 0; + pukeit(i, pq); i++; + uiomove(buf, strlen(buf), uio); + } + sprintf(buf, "%d/%d /---- rsp -----/\n", sp->stats.nrsp, sp->stats.max_rsp); + uiomove(buf, strlen(buf), uio); + i = 0; + TAILQ_FOREACH(pq, &sp->rsp, pq_link) { + if(uio->uio_resid == 0) + return 0; + pukeit(i, pq); i++; + uiomove(buf, strlen(buf), uio); + } + sprintf(buf, "%d/%d /---- csnd -----/\n", sp->stats.ncsnd, sp->stats.max_csnd); + i = 0; + uiomove(buf, strlen(buf), uio); + TAILQ_FOREACH(pq, &sp->csnd, pq_link) { + if(uio->uio_resid == 0) + return 0; + pukeit(i, pq); i++; + uiomove(buf, strlen(buf), uio); + } + sprintf(buf, "%d/%d /---- wsnd -----/\n", sp->stats.nwsnd, sp->stats.max_wsnd); + i = 0; + uiomove(buf, strlen(buf), uio); + TAILQ_FOREACH(pq, &sp->wsnd, pq_link) { + if(uio->uio_resid == 0) + return 0; + pukeit(i, pq); i++; + uiomove(buf, strlen(buf), uio); + } + sprintf(buf, "%d/%d /---- isnd -----/\n", sp->stats.nisnd, sp->stats.max_isnd); + i = 0; + uiomove(buf, strlen(buf), uio); + TAILQ_FOREACH(pq, &sp->isnd, pq_link) { + if(uio->uio_resid == 0) + return 0; + pukeit(i, pq); i++; + uiomove(buf, strlen(buf), uio); + } + + sprintf(buf, "/---- Stats ---/\n"); + uiomove(buf, strlen(buf), uio); + + sprintf(buf, "recv=%d sent=%d\n", sp->stats.nrecv, sp->stats.nsent); + uiomove(buf, strlen(buf), uio); + + sprintf(buf, "flags=%x pdus: alloc=%d max=%d\n", + sp->flags, sc->npdu_alloc, sc->npdu_max); + uiomove(buf, strlen(buf), uio); + + sprintf(buf, "cws=%d last cmd=%x exp=%x max=%x stat=%x itt=%x\n", + sp->cws, sp->sn.cmd, sp->sn.expCmd, sp->sn.maxCmd, sp->sn.stat, sp->sn.itt); + uiomove(buf, strlen(buf), uio); + + sprintf(buf, "/---- socket -----/\nso_count=%d so_state=%x\n", so->so_count, so->so_state); + uiomove(buf, strlen(buf), uio); + + } +#endif + return 0; +} + +static int +i_ping(struct cdev *dev) +{ + return 0; +} +/* + | low level I/O + */ +static int +i_setsoc(isc_session_t *sp, int fd, struct thread *td) +{ + int error = 0; + + if(sp->soc != NULL) + isc_stop_receiver(sp); + + error = fget(td, fd, CAP_SOCK_CLIENT, &sp->fp); + if(error) + return error; + + if((error = fgetsock(td, fd, CAP_SOCK_CLIENT, &sp->soc, 0)) == 0) { + sp->td = td; + isc_start_receiver(sp); + } + else { + fdrop(sp->fp, td); + sp->fp = NULL; + } + + return error; +} + +static int +i_send(struct cdev *dev, caddr_t arg, struct thread *td) +{ + isc_session_t *sp = dev->si_drv2; + caddr_t bp; + pduq_t *pq; + pdu_t *pp; + int n, error; + + debug_called(8); + + if(sp->soc == NULL) + return ENOTCONN; + + if((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL) + return EAGAIN; + pp = &pq->pdu; + pq->pdu = *(pdu_t *)arg; + if((error = i_prepPDU(sp, pq)) != 0) + goto out; + + bp = NULL; + if((pq->len - sizeof(union ipdu_u)) > 0) { + pq->buf = bp = malloc(pq->len - sizeof(union ipdu_u), M_ISCSIBUF, M_NOWAIT); + if(pq->buf == NULL) { + error = EAGAIN; + goto out; + } + } + else + pq->buf = NULL; // just in case? + + sdebug(2, "len=%d ahs_len=%d ds_len=%d buf=%zu@%p", + pq->len, pp->ahs_len, pp->ds_len, pq->len - sizeof(union ipdu_u), bp); + + if(pp->ahs_len) { + // XXX: never tested, looks suspicious + n = pp->ahs_len; + error = copyin(pp->ahs_addr, bp, n); + if(error != 0) { + sdebug(3, "copyin ahs: error=%d", error); + goto out; + } + pp->ahs_addr = (ahs_t *)bp; + bp += n; + } + if(pp->ds_len) { + n = pp->ds_len; + error = copyin(pp->ds_addr, bp, n); + if(error != 0) { + sdebug(3, "copyin ds: error=%d", error); + goto out; + } + pp->ds_addr = bp; + bp += n; + while(n & 03) { + n++; + *bp++ = 0; + } + } + + error = isc_qout(sp, pq); + if(error == 0) + wakeup(&sp->flags); // XXX: to 'push' proc_out ... +out: + if(error) + pdu_free(sp->isc, pq); + + return error; +} + +static int +i_recv(struct cdev *dev, caddr_t arg, struct thread *td) +{ + isc_session_t *sp = dev->si_drv2; + pduq_t *pq; + pdu_t *pp, *up; + caddr_t bp; + int error, mustfree, cnt; + size_t need, have, n; + + debug_called(8); + + if(sp == NULL) + return EIO; + + if(sp->soc == NULL) + return ENOTCONN; + cnt = 6; // XXX: maybe the user can request a time out? + mtx_lock(&sp->rsp_mtx); + while((pq = TAILQ_FIRST(&sp->rsp)) == NULL) { + msleep(&sp->rsp, &sp->rsp_mtx, PRIBIO, "isc_rsp", hz*10); + if(cnt-- == 0) break; // XXX: for now, needs work + } + if(pq != NULL) { + sp->stats.nrsp--; + TAILQ_REMOVE(&sp->rsp, pq, pq_link); + } + mtx_unlock(&sp->rsp_mtx); + + sdebug(6, "cnt=%d", cnt); + + if(pq == NULL) { + error = ENOTCONN; + sdebug(3, "error=%d sp->flags=%x ", error, sp->flags); + return error; + } + up = (pdu_t *)arg; + pp = &pq->pdu; + up->ipdu = pp->ipdu; + n = 0; + up->ds_len = 0; + up->ahs_len = 0; + error = 0; + + if(pq->mp) { + u_int len; + + // Grr... + len = 0; + if(pp->ahs_len) { + len += pp->ahs_len; + } + if(pp->ds_len) { + len += pp->ds_len; + } + + mustfree = 0; + if(len > pq->mp->m_len) { + mustfree++; + bp = malloc(len, M_TMP, M_WAITOK); + sdebug(4, "need mbufcopy: %d", len); + i_mbufcopy(pq->mp, bp, len); + } + else + bp = mtod(pq->mp, caddr_t); + + if(pp->ahs_len) { + need = pp->ahs_len; + n = MIN(up->ahs_size, need); + error = copyout(bp, (caddr_t)up->ahs_addr, n); + up->ahs_len = n; + bp += need; + } + if(!error && pp->ds_len) { + need = pp->ds_len; + if((have = up->ds_size) == 0) { + have = up->ahs_size - n; + up->ds_addr = (caddr_t)up->ahs_addr + n; + } + n = MIN(have, need); + error = copyout(bp, (caddr_t)up->ds_addr, n); + up->ds_len = n; + } + + if(mustfree) + free(bp, M_TMP); + } + + sdebug(6, "len=%d ahs_len=%d ds_len=%d", pq->len, pp->ahs_len, pp->ds_len); + + pdu_free(sp->isc, pq); + + return error; +} + +static int +i_fullfeature(struct cdev *dev, int flag) +{ + isc_session_t *sp = dev->si_drv2; + int error; + + sdebug(2, "flag=%d", flag); + + error = 0; + switch(flag) { + case 0: // stop + sp->flags &= ~ISC_FFPHASE; + break; + case 1: // start + sp->flags |= ISC_FFPHASE; + error = ic_init(sp); + break; + case 2: // restart + sp->flags |= ISC_FFPHASE; + ism_restart(sp); + break; + } + return error; +} + +static int +i_create_session(struct cdev *dev, int *ndev) +{ + struct isc_softc *sc = dev->si_drv1; + isc_session_t *sp; + int error, n; + + debug_called(8); + + sp = malloc(sizeof(isc_session_t), M_ISCSI, M_WAITOK | M_ZERO); + if(sp == NULL) + return ENOMEM; + + sx_xlock(&sc->unit_sx); + if((n = alloc_unr(sc->unit)) < 0) { + sx_unlock(&sc->unit_sx); + free(sp, M_ISCSI); + xdebug("too many sessions!"); + return EPERM; + } + sx_unlock(&sc->unit_sx); + + mtx_lock(&sc->isc_mtx); + TAILQ_INSERT_TAIL(&sc->isc_sess, sp, sp_link); + isc->nsess++; + mtx_unlock(&sc->isc_mtx); + + sp->dev = make_dev(&iscsi_cdevsw, n, UID_ROOT, GID_WHEEL, 0600, "iscsi%d", n); + *ndev = sp->sid = n; + sp->isc = sc; + sp->dev->si_drv1 = sc; + sp->dev->si_drv2 = sp; + + sp->opt.maxRecvDataSegmentLength = 8192; + sp->opt.maxXmitDataSegmentLength = 8192; + sp->opt.maxBurstLength = 65536; // 64k + sp->opt.maxluns = ISCSI_MAX_LUNS; + + error = ism_start(sp); + + return error; +} + +#ifdef notused +static void +iscsi_counters(isc_session_t *sp) +{ + int h, r, s; + pduq_t *pq; + +#define _puke(i, pq) do {\ + debug(2, "%03d] %06x %02x %x %ld %jd %x\n",\ + i, ntohl( pq->pdu.ipdu.bhs.CmdSN), \ + pq->pdu.ipdu.bhs.opcode, ntohl(pq->pdu.ipdu.bhs.itt),\ + (long)pq->ts.sec, pq->ts.frac, pq->flags);\ + } while(0) + + h = r = s = 0; + TAILQ_FOREACH(pq, &sp->hld, pq_link) { + _puke(h, pq); + h++; + } + TAILQ_FOREACH(pq, &sp->rsp, pq_link) r++; + TAILQ_FOREACH(pq, &sp->csnd, pq_link) s++; + TAILQ_FOREACH(pq, &sp->wsnd, pq_link) s++; + TAILQ_FOREACH(pq, &sp->isnd, pq_link) s++; + debug(2, "hld=%d rsp=%d snd=%d", h, r, s); +} +#endif + +static void +iscsi_shutdown(void *v) +{ + struct isc_softc *sc = v; + isc_session_t *sp; + int n; + + debug_called(8); + if(sc == NULL) { + xdebug("sc is NULL!"); + return; + } +#ifdef DO_EVENTHANDLER + if(sc->eh == NULL) + debug(2, "sc->eh is NULL"); + else { + EVENTHANDLER_DEREGISTER(shutdown_pre_sync, sc->eh); + debug(2, "done n=%d", sc->nsess); + } +#endif + n = 0; + TAILQ_FOREACH(sp, &sc->isc_sess, sp_link) { + debug(2, "%2d] sp->flags=0x%08x", n, sp->flags); + n++; + } + debug(2, "done"); +} + +static void +free_pdus(struct isc_softc *sc) +{ + debug_called(8); + + if(sc->pdu_zone != NULL) { + uma_zdestroy(sc->pdu_zone); + sc->pdu_zone = NULL; + } +} + +static void +iscsi_start(void) +{ + debug_called(8); + + TUNABLE_INT_FETCH("net.iscsi_initiator.max_sessions", &max_sessions); + TUNABLE_INT_FETCH("net.iscsi_initiator.max_pdus", &max_pdus); + + isc = malloc(sizeof(struct isc_softc), M_ISCSI, M_ZERO|M_WAITOK); + isc->dev = make_dev(&iscsi_cdevsw, max_sessions, UID_ROOT, GID_WHEEL, 0600, "iscsi"); + isc->dev->si_drv1 = isc; + mtx_init(&isc->isc_mtx, "iscsi", NULL, MTX_DEF); + + TAILQ_INIT(&isc->isc_sess); + /* + | now init the free pdu list + */ + isc->pdu_zone = uma_zcreate("pdu", sizeof(pduq_t), + NULL, NULL, NULL, NULL, + 0, 0); + if(isc->pdu_zone == NULL) { + xdebug("iscsi_initiator: uma_zcreate failed"); + // XXX: should fail... + } + uma_zone_set_max(isc->pdu_zone, max_pdus); + isc->unit = new_unrhdr(0, max_sessions-1, NULL); + sx_init(&isc->unit_sx, "iscsi sx"); + +#ifdef DO_EVENTHANDLER + if((isc->eh = EVENTHANDLER_REGISTER(shutdown_pre_sync, iscsi_shutdown, + sc, SHUTDOWN_PRI_DEFAULT-1)) == NULL) + xdebug("shutdown event registration failed\n"); +#endif + /* + | sysctl stuff + */ + sysctl_ctx_init(&isc->clist); + isc->oid = SYSCTL_ADD_NODE(&isc->clist, + SYSCTL_STATIC_CHILDREN(_net), + OID_AUTO, + "iscsi_initiator", + CTLFLAG_RD, + 0, + "iSCSI Subsystem"); + + SYSCTL_ADD_STRING(&isc->clist, + SYSCTL_CHILDREN(isc->oid), + OID_AUTO, + "driver_version", + CTLFLAG_RD, + iscsi_driver_version, + 0, + "iscsi driver version"); + + SYSCTL_ADD_STRING(&isc->clist, + SYSCTL_CHILDREN(isc->oid), + OID_AUTO, + "isid", + CTLFLAG_RW, + isid, + 6+1, + "initiator part of the Session Identifier"); + + SYSCTL_ADD_INT(&isc->clist, + SYSCTL_CHILDREN(isc->oid), + OID_AUTO, + "sessions", + CTLFLAG_RD, + &isc->nsess, + sizeof(isc->nsess), + "number of active session"); + +#ifdef ISCSI_INITIATOR_DEBUG + mtx_init(&iscsi_dbg_mtx, "iscsi_dbg", NULL, MTX_DEF); +#endif + + printf("iscsi: version %s\n", iscsi_driver_version); +} + +/* + | Notes: + | unload SHOULD fail if there is activity + | activity: there is/are active session/s + */ +static void +iscsi_stop(void) +{ + isc_session_t *sp, *sp_tmp; + + debug_called(8); + + /* + | go through all the sessions + | Note: close should have done this ... + */ + TAILQ_FOREACH_SAFE(sp, &isc->isc_sess, sp_link, sp_tmp) { + //XXX: check for activity ... + ism_stop(sp); + if(sp->cam_sim != NULL) + ic_destroy(sp); + } + mtx_destroy(&isc->isc_mtx); + sx_destroy(&isc->unit_sx); + + free_pdus(isc); + + if(isc->dev) + destroy_dev(isc->dev); + + if(sysctl_ctx_free(&isc->clist)) + xdebug("sysctl_ctx_free failed"); + + iscsi_shutdown(isc); // XXX: check EVENTHANDLER_ ... + +#ifdef ISCSI_INITIATOR_DEBUG + mtx_destroy(&iscsi_dbg_mtx); +#endif + + free(isc, M_ISCSI); +} + +static int +iscsi_modevent(module_t mod, int what, void *arg) +{ + debug_called(8); + + switch(what) { + case MOD_LOAD: + iscsi_start(); + break; + + case MOD_QUIESCE: + if(isc->nsess) { + xdebug("iscsi module busy(nsess=%d), cannot unload", isc->nsess); + log(LOG_ERR, "iscsi module busy, cannot unload"); + } + return isc->nsess; + + case MOD_SHUTDOWN: + break; + + case MOD_UNLOAD: + iscsi_stop(); + break; + + default: + break; + } + return 0; +} + +moduledata_t iscsi_mod = { + "iscsi", + (modeventhand_t) iscsi_modevent, + 0 +}; + +#ifdef ISCSI_ROOT +static void +iscsi_rootconf(void) +{ +#if 0 + nfs_setup_diskless(); + if (nfs_diskless_valid) + rootdevnames[0] = "nfs:"; +#endif + printf("** iscsi_rootconf **\n"); +} + +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); diff -urN p4/freebsd/src/sys/dev/iscsi_initiator/iscsi.h p4/iscsi/sys/dev/iscsi_initiator/iscsi.h --- p4/freebsd/src/sys/dev/iscsi_initiator/iscsi.h 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/sys/dev/iscsi_initiator/iscsi.h 2013-07-23 23:45:08.000000000 +0200 @@ -0,0 +1,500 @@ +/*- + * Copyright (c) 2005-2010 Daniel Braniss + * 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. + * + * 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: head/sys/dev/iscsi/initiator/iscsi.h 211095 2010-08-09 12:36:36Z des $ + */ +/* + | $Id: iscsi.h 743 2009-08-08 10:54:53Z danny $ + */ +#define TRUE 1 +#define FALSE 0 +#ifndef _KERNEL +typedef int boolean_t; +#endif + +#include + +#define ISCSIDEV "iscsi" +#define ISCSI_MAX_TARGETS 64 +/* + | iSCSI commands + */ + +/* + | Initiator Opcodes: + */ +#define ISCSI_NOP_OUT 0x00 +#define ISCSI_SCSI_CMD 0x01 +#define ISCSI_TASK_CMD 0x02 +#define ISCSI_LOGIN_CMD 0x03 +#define ISCSI_TEXT_CMD 0x04 +#define ISCSI_WRITE_DATA 0x05 +#define ISCSI_LOGOUT_CMD 0x06 +#define ISCSI_SNACK 0x10 +/* + | Target Opcodes: + */ +#define ISCSI_NOP_IN 0x20 +#define ISCSI_SCSI_RSP 0x21 +#define ISCSI_TASK_RSP 0x22 +#define ISCSI_LOGIN_RSP 0x23 +#define ISCSI_TEXT_RSP 0x24 +#define ISCSI_READ_DATA 0x25 +#define ISCSI_LOGOUT_RSP 0x26 +#define ISCSI_R2T 0x31 +#define ISCSI_ASYNC 0x32 +#define ISCSI_REJECT 0x3f +/* + | PDU stuff + */ +/* + | BHS Basic Header Segment + */ +typedef struct bhs { + // the order is network byte order! + u_char opcode:6; + u_char I:1; + u_char _:1; + u_char __:7; + u_char F:1; // Final bit + u_char ___[2]; + + u_int AHSLength:8; // in 4byte words + u_int DSLength:24; // in bytes + + u_int LUN[2]; // or Opcode-specific fields + u_int itt; + u_int OpcodeSpecificFields[7]; +#define CmdSN OpcodeSpecificFields[1] +#define ExpStSN OpcodeSpecificFields[2] +#define MaxCmdSN OpcodeSpecificFields[3] +} bhs_t; + +typedef struct ahs { + u_int len:16; + u_int type:8; + u_int spec:8; + char data[0]; +} ahs_t; + +typedef struct { + // Sequence Numbers + // (computers were invented to count, right?) + int cmd; + int expcmd; + int maxcmd; +} req_sn_t; + +typedef struct { + // Sequence Numbers + // (computers were invented to count, right?) + int stat; + int expcmd; + int maxcmd; +} rsp_sn_t; + +typedef struct scsi_req { + u_char opcode:6; // 0x01 + u_char I:1; + u_char _:1; + + u_char attr:3; + u_char _0:2; + u_char W:1; + u_char R:1; + u_char F:1; +#define iSCSI_TASK_UNTAGGED 0 +#define iSCSI_TASK_SIMPLE 1 +#define iSCSI_TASK_ORDER 2 +#define iSCSI_TASK_HOFQ 3 +#define iSCSI_TASK_ACA 4 + char _1[2]; + int len; + int lun[2]; + int itt; + int edtlen; // expectect data transfere length + int cmdSN; + int extStatSN; + int cdb[4]; +} scsi_req_t; + +typedef struct scsi_rsp { + char opcode; // 0x21 + u_char flag; + u_char response; + u_char status; + + int len; + int _[2]; + int itt; + int stag; + rsp_sn_t sn; + int expdatasn; + int bdrcnt; // bidirectional residual count + int rcnt; // residual count +} scsi_rsp_t; + +typedef struct nop_out { + // the order is network byte order! + u_char opcode:6; + u_char I:1; + u_char _:1; + u_char __:7; + u_char F:1; // Final bit + u_char ___[2]; + + u_int len; + u_int lun[2]; + u_int itt; + u_int ttt; + req_sn_t sn; + u_int mbz[3]; +} nop_out_t; + +typedef struct nop_in { + // the order is network byte order! + u_char opcode:6; + u_char I:1; + u_char _:1; + u_char __:7; + u_char F:1; // Final bit + u_char ___[2]; + + u_int len; + u_int lun[2]; + u_int itt; + u_int ttt; + rsp_sn_t sn; + u_int ____[2]; + +} nop_in_t; + +typedef struct r2t { + u_char opcode:6; + u_char I:1; + u_char _:1; + u_char __:7; + u_char F:1; // Final bit + u_char ___[2]; + + u_int len; + u_int lun[2]; + u_int itt; + u_int ttt; + rsp_sn_t sn; + u_int r2tSN; + u_int bo; + u_int ddtl; +} r2t_t; + +typedef struct data_out { + u_char opcode:6; + u_char I:1; + u_char _:1; + u_char __:7; + u_char F:1; // Final bit + u_char ___[2]; + + u_int len; + u_int lun[2]; + u_int itt; + u_int ttt; + rsp_sn_t sn; + u_int dsn; // data seq. number + u_int bo; + u_int ____; +} data_out_t; + +typedef struct data_in { + u_char opcode:6; + u_char I:1; + u_char _:1; + + u_char S:1; + u_char U:1; + u_char O:1; + u_char __:3; + u_char A:1; + u_char F:1; // Final bit + u_char ___[1]; + u_char status; + + u_int len; + u_int lun[2]; + u_int itt; + u_int ttt; + rsp_sn_t sn; + u_int dataSN; + u_int bo; + u_int ____; +} data_in_t; + +typedef struct reject { + u_char opcode:6; + u_char _:2; + u_char F:1; + u_char __:7; + u_char reason; + u_char ___; + + u_int len; + u_int ____[2]; + u_int tt[2]; // must be -1 + rsp_sn_t sn; + u_int dataSN; // or R2TSN or reserved + u_int _____[2]; +} reject_t; + +typedef struct async { + u_char opcode:6; + u_char _:2; + u_char F:1; + u_char __:7; + u_char ___[2]; + + u_int len; + u_int lun[2]; + u_int itt; // must be -1 + u_int ____; + rsp_sn_t sn; + + u_char asyncEvent; + u_char asyncVCode; + u_char param1[2]; + u_char param2[2]; + u_char param3[2]; + + u_int _____; + +} async_t; + +typedef struct login_req { + char cmd; // 0x03 + + u_char NSG:2; + u_char CSG:2; + u_char _:2; + u_char C:1; + u_char T:1; + + char v_max; + char v_min; + + int len; // remapped via standard bhs + char isid[6]; + short tsih; + int itt; // Initiator Task Tag; + + int CID:16; + int rsv:16; + + int cmdSN; + int expStatSN; + int unused[4]; +} login_req_t; + +typedef struct login_rsp { + char cmd; // 0x23 + u_char NSG:2; + u_char CSG:2; + u_char _1:2; + u_char C:1; + u_char T:1; + + char v_max; + char v_act; + + int len; // remapped via standard bhs + char isid[6]; + short tsih; + int itt; // Initiator Task Tag; + int _2; + rsp_sn_t sn; + int status:16; + int _3:16; + int _4[2]; +} login_rsp_t; + +typedef struct text_req { + char cmd; // 0x04 + + u_char _1:6; + u_char C:1; // Continuation + u_char F:1; // Final + char _2[2]; + + int len; + int itt; // Initiator Task Tag + int LUN[2]; + int ttt; // Target Transfer Tag + int cmdSN; + int expStatSN; + int unused[4]; +} text_req_t; + +typedef struct logout_req { + char cmd; // 0x06 + char reason; // 0 - close session + // 1 - close connection + // 2 - remove the connection for recovery + char _2[2]; + + int len; + int _r[2]; + int itt; // Initiator Task Tag; + + u_int CID:16; + u_int rsv:16; + + int cmdSN; + int expStatSN; + int unused[4]; +} logout_req_t; + +typedef struct logout_rsp { + char cmd; // 0x26 + char cbits; + char _1[2]; + int len; + int _2[2]; + int itt; + int _3; + rsp_sn_t sn; + short time2wait; + short time2retain; + int _4; +} logout_rsp_t; + +union ipdu_u { + bhs_t bhs; + scsi_req_t scsi_req; + scsi_rsp_t scsi_rsp; + nop_out_t nop_out; + nop_in_t nop_in; + r2t_t r2t; + data_out_t data_out; + data_in_t data_in; + reject_t reject; + async_t async; +}; + +/* + | Sequence Numbers + */ +typedef struct { + u_int itt; + u_int cmd; + u_int expCmd; + u_int maxCmd; + u_int stat; + u_int expStat; + u_int data; +} sn_t; + +/* + | in-core version of a Protocol Data Unit + */ +typedef struct { + union ipdu_u ipdu; + u_int hdr_dig; // header digest + + ahs_t *ahs_addr; + u_int ahs_len; + u_int ahs_size; // the allocated size + + u_char *ds_addr; + u_int ds_len; + u_int ds_size; // the allocated size + u_int ds_dig; // data digest +} pdu_t; + +typedef struct opvals { + int port; + int tags; + int maxluns; + int sockbufsize; + + int maxConnections; + int maxRecvDataSegmentLength; + int maxXmitDataSegmentLength; // pseudo ... + int maxBurstLength; + int firstBurstLength; + int defaultTime2Wait; + int defaultTime2Retain; + int maxOutstandingR2T; + int errorRecoveryLevel; + int targetPortalGroupTag; + + boolean_t initialR2T; + boolean_t immediateData; + boolean_t dataPDUInOrder; + boolean_t dataSequenceInOrder; + char *headerDigest; + char *dataDigest; + char *sessionType; + char *sendTargets; + char *targetAddress; + char *targetAlias; + char *targetName; + char *initiatorName; + char *initiatorAlias; + char *authMethod; + char *chapSecret; + char *chapIName; + char *chapDigest; + char *tgtChapName; + char *tgtChapSecret; + int tgtChallengeLen; + u_char tgtChapID; + char *tgtChapDigest; + char *iqn; + char *pidfile; +} isc_opt_t; + +/* + | ioctl + */ +#define ISCSISETSES _IOR('i', 1, int) +#define ISCSISETSOC _IOW('i', 2, int) +#define ISCSISETOPT _IOW('i', 5, isc_opt_t) +#define ISCSIGETOPT _IOR('i', 6, isc_opt_t) + +#define ISCSISEND _IOW('i', 10, pdu_t) +#define ISCSIRECV _IOWR('i', 11, pdu_t) + +#define ISCSIPING _IO('i', 20) +#define ISCSISIGNAL _IOW('i', 21, int *) + +#define ISCSISTART _IO('i', 30) +#define ISCSIRESTART _IO('i', 31) +#define ISCSISTOP _IO('i', 32) + +typedef struct iscsi_cam { + path_id_t path_id; + target_id_t target_id; + int target_nluns; +} iscsi_cam_t; + +#define ISCSIGETCAM _IOR('i', 33, iscsi_cam_t) diff -urN p4/freebsd/src/sys/dev/iscsi_initiator/iscsi_subr.c p4/iscsi/sys/dev/iscsi_initiator/iscsi_subr.c --- p4/freebsd/src/sys/dev/iscsi_initiator/iscsi_subr.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/sys/dev/iscsi_initiator/iscsi_subr.c 2013-07-23 23:45:08.000000000 +0200 @@ -0,0 +1,608 @@ +/*- + * Copyright (c) 2005-2010 Daniel Braniss + * 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. + * + * 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. + * + */ +/* + | $Id: iscsi_subr.c 743 2009-08-08 10:54:53Z danny $ + */ + +#include +__FBSDID("$FreeBSD: head/sys/dev/iscsi/initiator/iscsi_subr.c 234233 2012-04-13 18:21:56Z jpaetzel $"); + +#include "opt_iscsi_initiator.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + | Interface to the SCSI layer + */ +void +iscsi_r2t(isc_session_t *sp, pduq_t *opq, pduq_t *pq) +{ + union ccb *ccb = opq->ccb; + struct ccb_scsiio *csio = &ccb->csio; + pdu_t *opp = &opq->pdu; + bhs_t *bhp = &opp->ipdu.bhs; + r2t_t *r2t = &pq->pdu.ipdu.r2t; + pduq_t *wpq; + int error; + + debug_called(8); + sdebug(4, "itt=%x r2tSN=%d bo=%x ddtl=%x W=%d", ntohl(r2t->itt), + ntohl(r2t->r2tSN), ntohl(r2t->bo), ntohl(r2t->ddtl), opp->ipdu.scsi_req.W); + + switch(bhp->opcode) { + case ISCSI_SCSI_CMD: + if(opp->ipdu.scsi_req.W) { + data_out_t *cmd; + u_int ddtl = ntohl(r2t->ddtl); + u_int edtl = ntohl(opp->ipdu.scsi_req.edtlen); + u_int bleft, bs, dsn, bo; + caddr_t bp = csio->data_ptr; + + bo = ntohl(r2t->bo); + bp += MIN(bo, edtl - ddtl); + bleft = ddtl; + + if(sp->opt.maxXmitDataSegmentLength > 0) // danny's RFC + bs = MIN(sp->opt.maxXmitDataSegmentLength, ddtl); + else + bs = ddtl; + dsn = 0; + sdebug(4, "edtl=%x ddtl=%x bo=%x dsn=%x bs=%x maxX=%x", + edtl, ddtl, bo, dsn, bs, sp->opt.maxXmitDataSegmentLength); + while(bleft > 0) { + wpq = pdu_alloc(sp->isc, M_NOWAIT); // testing ... + if(wpq == NULL) { + sdebug(3, "itt=%x r2tSN=%d bo=%x ddtl=%x W=%d", ntohl(r2t->itt), + ntohl(r2t->r2tSN), ntohl(r2t->bo), ntohl(r2t->ddtl), opp->ipdu.scsi_req.W); + sdebug(1, "npdu_max=%d npdu_alloc=%d", sp->isc->npdu_max, sp->isc->npdu_alloc); + + while((wpq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL) { + sdebug(2, "waiting..."); +#if __FreeBSD_version >= 700000 + pause("isc_r2t", 5*hz); +#else + tsleep(sp->isc, 0, "isc_r2t", 5*hz); +#endif + } + } + cmd = &wpq->pdu.ipdu.data_out; + cmd->opcode = ISCSI_WRITE_DATA; + cmd->lun[0] = r2t->lun[0]; + cmd->lun[1] = r2t->lun[1]; + cmd->ttt = r2t->ttt; + cmd->itt = r2t->itt; + + cmd->dsn = htonl(dsn); + cmd->bo = htonl(bo); + + cmd->F = (bs < bleft)? 0: 1; // is this the last one? + bs = MIN(bs, bleft); + + wpq->pdu.ds_len = bs; + wpq->pdu.ds_addr = bp; + + error = isc_qout(sp, wpq); + sdebug(6, "bs=%x bo=%x bp=%p dsn=%x error=%d", bs, bo, bp, dsn, error); + if(error) + break; + bo += bs; + bp += bs; + bleft -= bs; + dsn++; + } + } + break; + + default: + // XXX: should not happen ... + xdebug("huh? opcode=0x%x", bhp->opcode); + } +} + +static int +getSenseData(u_int status, union ccb *ccb, pduq_t *pq) +{ + pdu_t *pp = &pq->pdu; + struct ccb_scsiio *scsi = (struct ccb_scsiio *)ccb; + struct scsi_sense_data *sense = &scsi->sense_data; + struct mbuf *m = pq->mp; + scsi_rsp_t *cmd = &pp->ipdu.scsi_rsp; + caddr_t bp; + int sense_len, mustfree = 0; + int error_code, sense_key, asc, ascq; + + bp = mtod(pq->mp, caddr_t); + if((sense_len = scsi_2btoul(bp)) == 0) + return 0; + debug(4, "sense_len=%d", sense_len); + /* + | according to the specs, the sense data cannot + | be larger than 252 ... + */ + if(sense_len > m->m_len) { + bp = malloc(sense_len, M_ISCSI, M_WAITOK); + debug(3, "calling i_mbufcopy(len=%d)", sense_len); + i_mbufcopy(pq->mp, bp, sense_len); + mustfree++; + } + scsi->scsi_status = status; + + bcopy(bp+2, sense, min(sense_len, scsi->sense_len)); + scsi->sense_resid = 0; + if(cmd->flag & (BIT(1)|BIT(2))) + scsi->sense_resid = ntohl(pp->ipdu.scsi_rsp.rcnt); + scsi_extract_sense_len(sense, scsi->sense_len - scsi->sense_resid, + &error_code, &sense_key, &asc, &ascq, /*show_errors*/ 1); + + debug(3, "sense_len=%d rcnt=%d sense_resid=%d dsl=%d error_code=%x flags=%x", + sense_len, + ntohl(pp->ipdu.scsi_rsp.rcnt), scsi->sense_resid, + pp->ds_len, error_code, sense_key); + + if(mustfree) + free(bp, M_ISCSI); + + return 1; +} + +/* + | Some information is from SAM draft. + */ +static void +_scsi_done(isc_session_t *sp, u_int response, u_int status, union ccb *ccb, pduq_t *pq) +{ + struct ccb_hdr *ccb_h = &ccb->ccb_h; + + debug_called(8); + + if(status || response) { + sdebug(3, "response=%x status=%x ccb=%p pq=%p", response, status, ccb, pq); + if(pq != NULL) + sdebug(3, "mp=%p buf=%p len=%d", pq->mp, pq->buf, pq->len); + } + ccb_h->status = 0; + switch(response) { + case 0: // Command Completed at Target + switch(status) { + case 0: // Good, all is ok + ccb_h->status = CAM_REQ_CMP; + break; + + case 0x02: // Check Condition + if((pq != NULL) && (pq->mp != NULL) && getSenseData(status, ccb, pq)) + ccb_h->status |= CAM_AUTOSNS_VALID; + + case 0x14: // Intermediate-Condition Met + case 0x10: // Intermediate + case 0x04: // Condition Met + ccb_h->status |= CAM_SCSI_STATUS_ERROR; + break; + + case 0x08: + ccb_h->status = CAM_BUSY; + break; + + case 0x18: // Reservation Conflict + case 0x28: // Task Set Full + ccb_h->status = CAM_REQUEUE_REQ; + break; + default: + //case 0x22: // Command Terminated + //case 0x30: // ACA Active + //case 0x40: // Task Aborted + ccb_h->status = CAM_REQ_CMP_ERR; //CAM_REQ_ABORTED; + } + break; + + default: + if((response >= 0x80) && (response <= 0xFF)) { + // Vendor specific ... + } + case 1: // target failure + ccb_h->status = CAM_REQ_CMP_ERR; //CAM_REQ_ABORTED; + break; + } + sdebug(5, "ccb_h->status=%x", ccb_h->status); + + XPT_DONE(sp, ccb); +} + +/* + | returns the lowest cmdseq that was not acked + */ +int +iscsi_requeue(isc_session_t *sp) +{ + pduq_t *pq; + u_int i, n, last; + + debug_called(8); + i = last = 0; + sp->flags |= ISC_HOLD; + while((pq = i_dqueue_hld(sp)) != NULL) { + i++; + if(pq->ccb != NULL) { + _scsi_done(sp, 0, 0x28, pq->ccb, NULL); + n = ntohl(pq->pdu.ipdu.bhs.CmdSN); + if(last==0 || (last > n)) + last = n; + sdebug(2, "last=%x n=%x", last, n); + } + pdu_free(sp->isc, pq); + } + sp->flags &= ~ISC_HOLD; + return i? last: sp->sn.cmd; +} + +int +i_pdu_flush(isc_session_t *sp) +{ + int n = 0; + pduq_t *pq; + + debug_called(8); + while((pq = i_dqueue_rsp(sp)) != NULL) { + pdu_free(sp->isc, pq); + n++; + } + while((pq = i_dqueue_rsv(sp)) != NULL) { + pdu_free(sp->isc, pq); + n++; + } + while((pq = i_dqueue_snd(sp, -1)) != NULL) { + pdu_free(sp->isc, pq); + n++; + } + while((pq = i_dqueue_hld(sp)) != NULL) { + pdu_free(sp->isc, pq); + n++; + } + while((pq = i_dqueue_wsnd(sp)) != NULL) { + pdu_free(sp->isc, pq); + n++; + } + if(n != 0) + xdebug("%d pdus recovered, should have been ZERO!", n); + return n; +} +/* + | called from ism_destroy. + */ +void +iscsi_cleanup(isc_session_t *sp) +{ + pduq_t *pq, *pqtmp; + + debug_called(8); + + TAILQ_FOREACH_SAFE(pq, &sp->hld, pq_link, pqtmp) { + sdebug(3, "hld pq=%p", pq); + if(pq->ccb) + _scsi_done(sp, 1, 0x40, pq->ccb, NULL); + TAILQ_REMOVE(&sp->hld, pq, pq_link); + if(pq->buf) { + free(pq->buf, M_ISCSIBUF); + pq->buf = NULL; + } + pdu_free(sp->isc, pq); + } + while((pq = i_dqueue_snd(sp, BIT(0)|BIT(1)|BIT(2))) != NULL) { + sdebug(3, "pq=%p", pq); + if(pq->ccb) + _scsi_done(sp, 1, 0x40, pq->ccb, NULL); + if(pq->buf) { + free(pq->buf, M_ISCSIBUF); + pq->buf = NULL; + } + pdu_free(sp->isc, pq); + } + + wakeup(&sp->rsp); +} + +void +iscsi_done(isc_session_t *sp, pduq_t *opq, pduq_t *pq) +{ + pdu_t *pp = &pq->pdu; + scsi_rsp_t *cmd = &pp->ipdu.scsi_rsp; + + debug_called(8); + + _scsi_done(sp, cmd->response, cmd->status, opq->ccb, pq); + + pdu_free(sp->isc, opq); +} + +// see RFC 3720, 10.9.1 page 146 +/* + | NOTE: + | the call to isc_stop_receiver is a kludge, + | instead, it should be handled by the userland controller, + | but that means that there should be a better way, other than + | sending a signal. Somehow, this packet should be supplied to + | the userland via read. + */ +void +iscsi_async(isc_session_t *sp, pduq_t *pq) +{ + pdu_t *pp = &pq->pdu; + async_t *cmd = &pp->ipdu.async; + + debug_called(8); + + sdebug(3, "asyncevent=0x%x asyncVCode=0x%0x", cmd->asyncEvent, cmd->asyncVCode); + switch(cmd->asyncEvent) { + case 0: // check status ... + break; + + case 1: // target request logout + isc_stop_receiver(sp); // XXX: temporary solution + break; + + case 2: // target indicates it wants to drop connection + isc_stop_receiver(sp); // XXX: temporary solution + break; + + case 3: // target indicates it will drop all connections. + isc_stop_receiver(sp); // XXX: temporary solution + break; + + case 4: // target request parameter negotiation + break; + + default: + break; + } +} + +void +iscsi_reject(isc_session_t *sp, pduq_t *opq, pduq_t *pq) +{ + union ccb *ccb = opq->ccb; + //reject_t *reject = &pq->pdu.ipdu.reject; + + debug_called(8); + //XXX: check RFC 10.17.1 (page 176) + ccb->ccb_h.status = CAM_REQ_ABORTED; + XPT_DONE(sp, ccb); + + pdu_free(sp->isc, opq); +} + +/* + | deal with lun + */ +static int +dwl(isc_session_t *sp, int lun, u_char *lp) +{ + debug_called(8); + sdebug(4, "lun=%d", lun); + /* + | mapping LUN to iSCSI LUN + | check the SAM-2 specs + | hint: maxLUNS is a small number, cam's LUN is 32bits + | iSCSI is 64bits, scsi is ? + */ + // XXX: check if this will pass the endian test + if(lun < 256) { + lp[0] = 0; + lp[1] = lun; + } else + if(lun < 16384) { + lp[0] = (1 << 5) | ((lun >> 8) & 0x3f); + lp[1] = lun & 0xff; + } + else { + xdebug("lun %d: is unsupported!", lun); + return -1; + } + + return 0; +} + +/* + | encapsulate the scsi command and + */ +int +scsi_encap(struct cam_sim *sim, union ccb *ccb) +{ + isc_session_t *sp = cam_sim_softc(sim); + struct ccb_scsiio *csio = &ccb->csio; + struct ccb_hdr *ccb_h = &ccb->ccb_h; + pduq_t *pq; + scsi_req_t *cmd; + + debug_called(8); + + debug(4, "ccb->sp=%p", ccb_h->spriv_ptr0); + sp = ccb_h->spriv_ptr0; + + if((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL) { + debug(2, "ccb->sp=%p", ccb_h->spriv_ptr0); + sdebug(1, "pdu_alloc failed sc->npdu_max=%d npdu_alloc=%d", + sp->isc->npdu_max, sp->isc->npdu_alloc); + while((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL) { + sdebug(2, "waiting..."); +#if __FreeBSD_version >= 700000 + pause("isc_encap", 5*hz); +#else + tsleep(sp->isc, 0, "isc_encap", 5*hz); +#endif + } + } + cmd = &pq->pdu.ipdu.scsi_req; + cmd->opcode = ISCSI_SCSI_CMD; + cmd->F = 1; +#if 0 +// this breaks at least Isilon's iscsi target. + /* + | map tag option, default is UNTAGGED + */ + switch(csio->tag_action) { + case MSG_SIMPLE_Q_TAG: cmd->attr = iSCSI_TASK_SIMPLE; break; + case MSG_HEAD_OF_Q_TAG: cmd->attr = iSCSI_TASK_HOFQ; break; + case MSG_ORDERED_Q_TAG: cmd->attr = iSCSI_TASK_ORDER; break; + case MSG_ACA_TASK: cmd->attr = iSCSI_TASK_ACA; break; + } +#else + cmd->attr = iSCSI_TASK_SIMPLE; +#endif + + dwl(sp, ccb_h->target_lun, (u_char *)&cmd->lun); + + if((ccb_h->flags & CAM_CDB_POINTER) != 0) { + if((ccb_h->flags & CAM_CDB_PHYS) == 0) { + if(csio->cdb_len > 16) { + sdebug(3, "oversize cdb %d > 16", csio->cdb_len); + goto invalid; + } + } + else { + sdebug(3, "not phys"); + goto invalid; + } + } + + if(csio->cdb_len > sizeof(cmd->cdb)) + xdebug("guevalt! %d > %ld", csio->cdb_len, (long)sizeof(cmd->cdb)); + + memcpy(cmd->cdb, + ccb_h->flags & CAM_CDB_POINTER? csio->cdb_io.cdb_ptr: csio->cdb_io.cdb_bytes, + csio->cdb_len); + + cmd->W = (ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT; + cmd->R = (ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN; + cmd->edtlen = htonl(csio->dxfer_len); + + pq->ccb = ccb; + /* + | place it in the out queue + */ + if(isc_qout(sp, pq) == 0) + return 1; + invalid: + ccb->ccb_h.status = CAM_REQ_INVALID; + pdu_free(sp->isc, pq); + + return 0; +} + +int +scsi_decap(isc_session_t *sp, pduq_t *opq, pduq_t *pq) +{ + union ccb *ccb = opq->ccb; + struct ccb_scsiio *csio = &ccb->csio; + pdu_t *opp = &opq->pdu; + bhs_t *bhp = &opp->ipdu.bhs; + + debug_called(8); + sdebug(6, "pq=%p opq=%p bhp->opcode=0x%x len=%d", + pq, opq, bhp->opcode, pq->pdu.ds_len); + if(ccb == NULL) { + sdebug(1, "itt=0x%x pq=%p opq=%p bhp->opcode=0x%x len=%d", + ntohl(pq->pdu.ipdu.bhs.itt), + pq, opq, bhp->opcode, pq->pdu.ds_len); + xdebug("%d] ccb == NULL!", sp->sid); + return 0; + } + if(pq->pdu.ds_len != 0) { + switch(bhp->opcode) { + case ISCSI_SCSI_CMD: { + scsi_req_t *cmd = &opp->ipdu.scsi_req; + sdebug(5, "itt=0x%x opcode=%x R=%d", + ntohl(pq->pdu.ipdu.bhs.itt), + pq->pdu.ipdu.bhs.opcode, cmd->R); + + switch(pq->pdu.ipdu.bhs.opcode) { + case ISCSI_READ_DATA: // SCSI Data in + { + caddr_t bp = NULL; // = mtod(pq->mp, caddr_t); + data_in_t *rcmd = &pq->pdu.ipdu.data_in; + + if(cmd->R) { + sdebug(5, "copy to=%p from=%p l1=%d l2=%d mp@%p", + csio->data_ptr, bp? mtod(pq->mp, caddr_t): 0, + ntohl(cmd->edtlen), pq->pdu.ds_len, pq->mp); + if(ntohl(cmd->edtlen) >= pq->pdu.ds_len) { + int offset, len = pq->pdu.ds_len; + + if(pq->mp != NULL) { + caddr_t dp; + + offset = ntohl(rcmd->bo); + dp = csio->data_ptr + offset; + i_mbufcopy(pq->mp, dp, len); + } + } + else { + xdebug("edtlen=%d < ds_len=%d", + ntohl(cmd->edtlen), pq->pdu.ds_len); + } + } + if(rcmd->S) { + /* + | contains also the SCSI Status + */ + _scsi_done(sp, 0, rcmd->status, opq->ccb, NULL); + return 0; + } else + return 1; + } + break; + } + } + default: + sdebug(3, "opcode=%02x", bhp->opcode); + break; + } + } + /* + | XXX: error ... + */ + return 1; +} diff -urN p4/freebsd/src/sys/dev/iscsi_initiator/iscsivar.h p4/iscsi/sys/dev/iscsi_initiator/iscsivar.h --- p4/freebsd/src/sys/dev/iscsi_initiator/iscsivar.h 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/sys/dev/iscsi_initiator/iscsivar.h 2013-07-23 23:45:08.000000000 +0200 @@ -0,0 +1,599 @@ +/*- + * Copyright (c) 2005-2011 Daniel Braniss + * 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. + * + * 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: head/sys/dev/iscsi/initiator/iscsivar.h 234233 2012-04-13 18:21:56Z jpaetzel $ + */ + +/* + | $Id: iscsivar.h 743 2009-08-08 10:54:53Z danny $ + */ +#define ISCSI_MAX_LUNS 128 // don't touch this +#if ISCSI_MAX_LUNS > 8 +/* + | for this to work + | sysctl kern.cam.cam_srch_hi=1 + */ +#endif + +#ifndef ISCSI_INITIATOR_DEBUG +#define ISCSI_INITIATOR_DEBUG 1 +#endif + +#ifdef ISCSI_INITIATOR_DEBUG +extern int iscsi_debug; +#define debug(level, fmt, args...) do {if(level <= iscsi_debug)\ + printf("%s: " fmt "\n", __func__ , ##args);} while(0) +#define sdebug(level, fmt, args...) do {if(level <= iscsi_debug)\ + printf("%d] %s: " fmt "\n", sp->sid, __func__ , ##args);} while(0) +#define debug_called(level) do {if(level <= iscsi_debug)\ + printf("%s: called\n", __func__);} while(0) +#else +#define debug(level, fmt, args...) +#define debug_called(level) +#define sdebug(level, fmt, args...) +#endif /* ISCSI_INITIATOR_DEBUG */ + +#define xdebug(fmt, args...) printf(">>> %s: " fmt "\n", __func__ , ##args) + +#define MAX_SESSIONS ISCSI_MAX_TARGETS +#define MAX_PDUS (MAX_SESSIONS*256) // XXX: at the moment this is arbitrary + +typedef uint32_t digest_t(const void *, int len, uint32_t ocrc); + +MALLOC_DECLARE(M_ISCSI); +MALLOC_DECLARE(M_ISCSIBUF); + +#define ISOK2DIG(dig, pp) ((dig != NULL) && ((pp->ipdu.bhs.opcode & 0x1f) != ISCSI_LOGIN_CMD)) + +#ifndef BIT +#define BIT(n) (1 <<(n)) +#endif + +#define ISC_SM_RUN BIT(0) +#define ISC_SM_RUNNING BIT(1) + +#define ISC_LINK_UP BIT(2) +#define ISC_CON_RUN BIT(3) +#define ISC_CON_RUNNING BIT(4) +#define ISC_KILL BIT(5) +#define ISC_OQNOTEMPTY BIT(6) +#define ISC_OWAITING BIT(7) +#define ISC_FFPHASE BIT(8) + +#define ISC_CAMDEVS BIT(9) +#define ISC_SCANWAIT BIT(10) + +#define ISC_MEMWAIT BIT(11) +#define ISC_SIGNALED BIT(12) + +#define ISC_HOLD BIT(15) +#define ISC_HOLDED BIT(16) + +#define ISC_SHUTDOWN BIT(31) + +/* + | some stats + */ +struct i_stats { + int npdu; // number of pdus malloc'ed. + int nrecv; // unprocessed received pdus + int nsent; // sent pdus + + int nrsp, max_rsp; + int nrsv, max_rsv; + int ncsnd, max_csnd; + int nisnd, max_isnd; + int nwsnd, max_wsnd; + int nhld, max_hld; + + struct bintime t_sent; + struct bintime t_recv; +}; + +/* + | one per 'session' + */ + +typedef TAILQ_HEAD(, pduq) queue_t; + +typedef struct isc_session { + TAILQ_ENTRY(isc_session) sp_link; + int flags; + struct cdev *dev; + struct socket *soc; + struct file *fp; + struct thread *td; + + struct proc *proc; // the userland process + int signal; + struct proc *soc_proc; + struct proc *stp; // the sm thread + + struct isc_softc *isc; + + digest_t *hdrDigest; // the digest alg. if any + digest_t *dataDigest; // the digest alg. if any + + int sid; // Session ID + sn_t sn; // sequence number stuff; + int cws; // current window size + + int target_nluns; // this and target_lun are + // hopefully temporal till I + // figure out a better way. + int target_lun[ISCSI_MAX_LUNS/(sizeof(int)*8) + 1]; + + struct mtx rsp_mtx; + struct mtx rsv_mtx; + struct mtx snd_mtx; + struct mtx hld_mtx; + struct mtx io_mtx; + queue_t rsp; + queue_t rsv; + queue_t csnd; + queue_t isnd; + queue_t wsnd; + queue_t hld; + + isc_opt_t opt; // negotiable values + + struct i_stats stats; + bhs_t bhs; + struct uio uio; + struct iovec iov; + /* + | cam stuff + */ + struct cam_sim *cam_sim; + struct cam_path *cam_path; + struct mtx cam_mtx; + /* + | sysctl stuff + */ + struct sysctl_ctx_list clist; + struct sysctl_oid *oid; + int douio; //XXX: turn on/off uio on read +} isc_session_t; + +typedef struct pduq { + TAILQ_ENTRY(pduq) pq_link; + + caddr_t buf; + u_int len; // the total length of the pdu + pdu_t pdu; + union ccb *ccb; + + struct uio uio; + struct iovec iov[5]; // XXX: careful ... + struct mbuf *mp; + struct bintime ts; + queue_t *pduq; +} pduq_t; +/* + */ +struct isc_softc { + struct mtx isc_mtx; + TAILQ_HEAD(,isc_session) isc_sess; + int nsess; + struct cdev *dev; + char isid[6]; // Initiator Session ID (48 bits) + struct unrhdr *unit; + struct sx unit_sx; + + uma_zone_t pdu_zone; // pool of free pdu's + TAILQ_HEAD(,pduq) freepdu; + +#ifdef ISCSI_INITIATOR_DEBUG + int npdu_alloc, npdu_max; // for instrumentation +#endif +#ifdef DO_EVENTHANDLER + eventhandler_tag eh; +#endif + /* + | sysctl stuff + */ + struct sysctl_ctx_list clist; + struct sysctl_oid *oid; +}; + +#ifdef ISCSI_INITIATOR_DEBUG +extern struct mtx iscsi_dbg_mtx; +#endif + +void isc_start_receiver(isc_session_t *sp); +void isc_stop_receiver(isc_session_t *sp); + +int isc_sendPDU(isc_session_t *sp, pduq_t *pq); +int isc_qout(isc_session_t *sp, pduq_t *pq); +int i_prepPDU(isc_session_t *sp, pduq_t *pq); + +int ism_fullfeature(struct cdev *dev, int flag); + +int i_pdu_flush(isc_session_t *sc); +int i_setopt(isc_session_t *sp, isc_opt_t *opt); +void i_freeopt(isc_opt_t *opt); + +int ic_init(isc_session_t *sp); +void ic_destroy(isc_session_t *sp); +void ic_lost_target(isc_session_t *sp, int target); +int ic_getCamVals(isc_session_t *sp, iscsi_cam_t *cp); + +void ism_recv(isc_session_t *sp, pduq_t *pq); +int ism_start(isc_session_t *sp); +void ism_restart(isc_session_t *sp); +void ism_stop(isc_session_t *sp); + +int scsi_encap(struct cam_sim *sim, union ccb *ccb); +int scsi_decap(isc_session_t *sp, pduq_t *opq, pduq_t *pq); +void iscsi_r2t(isc_session_t *sp, pduq_t *opq, pduq_t *pq); +void iscsi_done(isc_session_t *sp, pduq_t *opq, pduq_t *pq); +void iscsi_reject(isc_session_t *sp, pduq_t *opq, pduq_t *pq); +void iscsi_async(isc_session_t *sp, pduq_t *pq); +void iscsi_cleanup(isc_session_t *sp); +int iscsi_requeue(isc_session_t *sp); + +// Serial Number Arithmetic +#define _MAXINCR 0x7FFFFFFF // 2 ^ 31 - 1 +#define SNA_GT(i1, i2) ((i1 != i2) && (\ + (i1 < i2 && i2 - i1 > _MAXINCR) ||\ + (i1 > i2 && i1 - i2 < _MAXINCR))?1: 0) + +/* + | inlines + */ +#ifdef _CAM_CAM_XPT_SIM_H + +#if __FreeBSD_version < 600000 +#define CAM_LOCK(arg) +#define CAM_ULOCK(arg) + +static __inline void +XPT_DONE(isc_session_t *sp, union ccb *ccb) +{ + mtx_lock(&Giant); + xpt_done(ccb); + mtx_unlock(&Giant); +} +#elif __FreeBSD_version >= 700000 +#define CAM_LOCK(arg) mtx_lock(&arg->cam_mtx) +#define CAM_UNLOCK(arg) mtx_unlock(&arg->cam_mtx) + +static __inline void +XPT_DONE(isc_session_t *sp, union ccb *ccb) +{ + CAM_LOCK(sp); + xpt_done(ccb); + CAM_UNLOCK(sp); +} +#else +//__FreeBSD_version >= 600000 +#define CAM_LOCK(arg) +#define CAM_UNLOCK(arg) +#define XPT_DONE(ignore, arg) xpt_done(arg) +#endif + +#endif /* _CAM_CAM_XPT_SIM_H */ + +static __inline pduq_t * +pdu_alloc(struct isc_softc *isc, int wait) +{ + pduq_t *pq; + + pq = (pduq_t *)uma_zalloc(isc->pdu_zone, wait /* M_WAITOK or M_NOWAIT*/); + if(pq == NULL) { + debug(7, "out of mem"); + return NULL; + } +#ifdef ISCSI_INITIATOR_DEBUG + mtx_lock(&iscsi_dbg_mtx); + isc->npdu_alloc++; + if(isc->npdu_alloc > isc->npdu_max) + isc->npdu_max = isc->npdu_alloc; + mtx_unlock(&iscsi_dbg_mtx); +#endif + memset(pq, 0, sizeof(pduq_t)); + + return pq; +} + +static __inline void +pdu_free(struct isc_softc *isc, pduq_t *pq) +{ + if(pq->mp) + m_freem(pq->mp); +#ifdef NO_USE_MBUF + if(pq->buf != NULL) + free(pq->buf, M_ISCSIBUF); +#endif + uma_zfree(isc->pdu_zone, pq); +#ifdef ISCSI_INITIATOR_DEBUG + mtx_lock(&iscsi_dbg_mtx); + isc->npdu_alloc--; + mtx_unlock(&iscsi_dbg_mtx); +#endif +} + +static __inline void +i_nqueue_rsp(isc_session_t *sp, pduq_t *pq) +{ + mtx_lock(&sp->rsp_mtx); + if(++sp->stats.nrsp > sp->stats.max_rsp) + sp->stats.max_rsp = sp->stats.nrsp; + TAILQ_INSERT_TAIL(&sp->rsp, pq, pq_link); + mtx_unlock(&sp->rsp_mtx); +} + +static __inline pduq_t * +i_dqueue_rsp(isc_session_t *sp) +{ + pduq_t *pq; + + mtx_lock(&sp->rsp_mtx); + if((pq = TAILQ_FIRST(&sp->rsp)) != NULL) { + sp->stats.nrsp--; + TAILQ_REMOVE(&sp->rsp, pq, pq_link); + } + mtx_unlock(&sp->rsp_mtx); + + return pq; +} + +static __inline void +i_nqueue_rsv(isc_session_t *sp, pduq_t *pq) +{ + mtx_lock(&sp->rsv_mtx); + if(++sp->stats.nrsv > sp->stats.max_rsv) + sp->stats.max_rsv = sp->stats.nrsv; + TAILQ_INSERT_TAIL(&sp->rsv, pq, pq_link); + mtx_unlock(&sp->rsv_mtx); +} + +static __inline pduq_t * +i_dqueue_rsv(isc_session_t *sp) +{ + pduq_t *pq; + + mtx_lock(&sp->rsv_mtx); + if((pq = TAILQ_FIRST(&sp->rsv)) != NULL) { + sp->stats.nrsv--; + TAILQ_REMOVE(&sp->rsv, pq, pq_link); + } + mtx_unlock(&sp->rsv_mtx); + + return pq; +} + +static __inline void +i_nqueue_csnd(isc_session_t *sp, pduq_t *pq) +{ + mtx_lock(&sp->snd_mtx); + if(++sp->stats.ncsnd > sp->stats.max_csnd) + sp->stats.max_csnd = sp->stats.ncsnd; + TAILQ_INSERT_TAIL(&sp->csnd, pq, pq_link); + mtx_unlock(&sp->snd_mtx); +} + +static __inline pduq_t * +i_dqueue_csnd(isc_session_t *sp) +{ + pduq_t *pq; + + mtx_lock(&sp->snd_mtx); + if((pq = TAILQ_FIRST(&sp->csnd)) != NULL) { + sp->stats.ncsnd--; + TAILQ_REMOVE(&sp->csnd, pq, pq_link); + } + mtx_unlock(&sp->snd_mtx); + + return pq; +} + +static __inline void +i_nqueue_isnd(isc_session_t *sp, pduq_t *pq) +{ + mtx_lock(&sp->snd_mtx); + if(++sp->stats.nisnd > sp->stats.max_isnd) + sp->stats.max_isnd = sp->stats.nisnd; + TAILQ_INSERT_TAIL(&sp->isnd, pq, pq_link); + mtx_unlock(&sp->snd_mtx); +} + +static __inline pduq_t * +i_dqueue_isnd(isc_session_t *sp) +{ + pduq_t *pq; + + mtx_lock(&sp->snd_mtx); + if((pq = TAILQ_FIRST(&sp->isnd)) != NULL) { + sp->stats.nisnd--; + TAILQ_REMOVE(&sp->isnd, pq, pq_link); + } + mtx_unlock(&sp->snd_mtx); + + return pq; +} + +static __inline void +i_nqueue_wsnd(isc_session_t *sp, pduq_t *pq) +{ + mtx_lock(&sp->snd_mtx); + if(++sp->stats.nwsnd > sp->stats.max_wsnd) + sp->stats.max_wsnd = sp->stats.nwsnd; + TAILQ_INSERT_TAIL(&sp->wsnd, pq, pq_link); + mtx_unlock(&sp->snd_mtx); +} + +static __inline pduq_t * +i_dqueue_wsnd(isc_session_t *sp) +{ + pduq_t *pq; + + mtx_lock(&sp->snd_mtx); + if((pq = TAILQ_FIRST(&sp->wsnd)) != NULL) { + sp->stats.nwsnd--; + TAILQ_REMOVE(&sp->wsnd, pq, pq_link); + } + mtx_unlock(&sp->snd_mtx); + + return pq; +} + +static __inline pduq_t * +i_dqueue_snd(isc_session_t *sp, int which) +{ + pduq_t *pq; + + pq = NULL; + mtx_lock(&sp->snd_mtx); + if((which & BIT(0)) && (pq = TAILQ_FIRST(&sp->isnd)) != NULL) { + sp->stats.nisnd--; + TAILQ_REMOVE(&sp->isnd, pq, pq_link); + pq->pduq = &sp->isnd; // remember where you came from + } else + if((which & BIT(1)) && (pq = TAILQ_FIRST(&sp->wsnd)) != NULL) { + sp->stats.nwsnd--; + TAILQ_REMOVE(&sp->wsnd, pq, pq_link); + pq->pduq = &sp->wsnd; // remember where you came from + } else + if((which & BIT(2)) && (pq = TAILQ_FIRST(&sp->csnd)) != NULL) { + sp->stats.ncsnd--; + TAILQ_REMOVE(&sp->csnd, pq, pq_link); + pq->pduq = &sp->csnd; // remember where you came from + } + mtx_unlock(&sp->snd_mtx); + + return pq; +} + +static __inline void +i_rqueue_pdu(isc_session_t *sp, pduq_t *pq) +{ + mtx_lock(&sp->snd_mtx); + KASSERT(pq->pduq != NULL, ("pq->pduq is NULL")); + TAILQ_INSERT_TAIL(pq->pduq, pq, pq_link); + mtx_unlock(&sp->snd_mtx); +} + +/* + | Waiting for ACK (or something :-) + */ +static __inline void +i_nqueue_hld(isc_session_t *sp, pduq_t *pq) +{ + getbintime(&pq->ts); + mtx_lock(&sp->hld_mtx); + if(++sp->stats.nhld > sp->stats.max_hld) + sp->stats.max_hld = sp->stats.nhld; + TAILQ_INSERT_TAIL(&sp->hld, pq, pq_link); + mtx_unlock(&sp->hld_mtx); + return; +} + +static __inline void +i_remove_hld(isc_session_t *sp, pduq_t *pq) +{ + mtx_lock(&sp->hld_mtx); + sp->stats.nhld--; + TAILQ_REMOVE(&sp->hld, pq, pq_link); + mtx_unlock(&sp->hld_mtx); +} + +static __inline pduq_t * +i_dqueue_hld(isc_session_t *sp) +{ + pduq_t *pq; + + mtx_lock(&sp->hld_mtx); + if((pq = TAILQ_FIRST(&sp->hld)) != NULL) { + sp->stats.nhld--; + TAILQ_REMOVE(&sp->hld, pq, pq_link); + } + mtx_unlock(&sp->hld_mtx); + + return pq; +} + +static __inline pduq_t * +i_search_hld(isc_session_t *sp, int itt, int keep) +{ + pduq_t *pq, *tmp; + + pq = NULL; + + mtx_lock(&sp->hld_mtx); + TAILQ_FOREACH_SAFE(pq, &sp->hld, pq_link, tmp) { + if(pq->pdu.ipdu.bhs.itt == itt) { + if(!keep) { + sp->stats.nhld--; + TAILQ_REMOVE(&sp->hld, pq, pq_link); + } + break; + } + } + mtx_unlock(&sp->hld_mtx); + + return pq; +} + +static __inline void +i_acked_hld(isc_session_t *sp, pdu_t *op) +{ + pduq_t *pq, *tmp; + u_int exp = sp->sn.expCmd; + + pq = NULL; + mtx_lock(&sp->hld_mtx); + TAILQ_FOREACH_SAFE(pq, &sp->hld, pq_link, tmp) { + if((op && op->ipdu.bhs.itt == pq->pdu.ipdu.bhs.itt) + || (pq->ccb == NULL + && (pq->pdu.ipdu.bhs.opcode != ISCSI_WRITE_DATA) + && SNA_GT(exp, ntohl(pq->pdu.ipdu.bhs.ExpStSN)))) { + sp->stats.nhld--; + TAILQ_REMOVE(&sp->hld, pq, pq_link); + pdu_free(sp->isc, pq); + } + } + mtx_unlock(&sp->hld_mtx); +} + +static __inline void +i_mbufcopy(struct mbuf *mp, caddr_t dp, int len) +{ + struct mbuf *m; + caddr_t bp; + + for(m = mp; m != NULL; m = m->m_next) { + bp = mtod(m, caddr_t); + /* + | the pdu is word (4 octed) aligned + | so len <= packet + */ + memcpy(dp, bp, MIN(len, m->m_len)); + dp += m->m_len; + len -= m->m_len; + if(len <= 0) + break; + } +} diff -urN p4/freebsd/src/sys/modules/Makefile p4/iscsi/sys/modules/Makefile --- p4/freebsd/src/sys/modules/Makefile 2013-08-18 13:08:41.000000000 +0200 +++ p4/iscsi/sys/modules/Makefile 2013-08-18 13:36:35.000000000 +0200 @@ -160,6 +160,7 @@ ${_ipwfw} \ ${_isci} \ iscsi \ + iscsi_initiator \ isp \ ${_ispfw} \ ${_iwi} \ diff -urN p4/freebsd/src/sys/modules/ctl/Makefile p4/iscsi/sys/modules/ctl/Makefile --- p4/freebsd/src/sys/modules/ctl/Makefile 2013-04-20 20:14:46.000000000 +0200 +++ p4/iscsi/sys/modules/ctl/Makefile 2013-07-23 23:46:43.000000000 +0200 @@ -12,6 +12,7 @@ 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,7 @@ SRCS+= opt_cam.h SRCS+= opt_kdtrace.h +CFLAGS+=-DINVARIANTS +#CFLAGS+=-DICL_KERNEL_PROXY + .include diff -urN p4/freebsd/src/sys/modules/iscsi/Makefile p4/iscsi/sys/modules/iscsi/Makefile --- p4/freebsd/src/sys/modules/iscsi/Makefile 2013-04-20 20:14:48.000000000 +0200 +++ p4/iscsi/sys/modules/iscsi/Makefile 2013-07-23 23:46:43.000000000 +0200 @@ -1,5 +1,26 @@ -# $FreeBSD: head/sys/modules/iscsi/Makefile 171568 2007-07-24 15:35:02Z scottl $ +# $FreeBSD$ -SUBDIR= initiator +.PATH: ${.CURDIR}/../../dev/iscsi/ +KMOD= iscsi -.include +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+=-DINVARIANTS +#CFLAGS+=-DICL_KERNEL_PROXY + +.include diff -urN p4/freebsd/src/sys/modules/iscsi/initiator/Makefile p4/iscsi/sys/modules/iscsi/initiator/Makefile --- p4/freebsd/src/sys/modules/iscsi/initiator/Makefile 2013-04-20 20:14:48.000000000 +0200 +++ p4/iscsi/sys/modules/iscsi/initiator/Makefile 1970-01-01 01:00:00.000000000 +0100 @@ -1,14 +0,0 @@ -# $FreeBSD: head/sys/modules/iscsi/initiator/Makefile 234481 2012-04-20 04:40:39Z delphij $ - -.PATH: ${.CURDIR}/../../../dev/iscsi/initiator -KMOD=iscsi_initiator - -SRCS= iscsi.h iscsivar.h -SRCS+= iscsi.c isc_cam.c isc_soc.c isc_sm.c isc_subr.c iscsi_subr.c -SRCS+= opt_cam.h opt_iscsi_initiator.h -SRCS+= bus_if.h device_if.h - -# Debugging -# CFLAGS+= -DISCSI_INITIATOR_DEBUG=9 - -.include diff -urN p4/freebsd/src/sys/modules/iscsi_initiator/Makefile p4/iscsi/sys/modules/iscsi_initiator/Makefile --- p4/freebsd/src/sys/modules/iscsi_initiator/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/sys/modules/iscsi_initiator/Makefile 2013-07-23 23:46:43.000000000 +0200 @@ -0,0 +1,14 @@ +# $FreeBSD: head/sys/modules/iscsi/initiator/Makefile 234481 2012-04-20 04:40:39Z delphij $ + +.PATH: ${.CURDIR}/../../dev/iscsi_initiator +KMOD=iscsi_initiator + +SRCS= iscsi.h iscsivar.h +SRCS+= iscsi.c isc_cam.c isc_soc.c isc_sm.c isc_subr.c iscsi_subr.c +SRCS+= opt_cam.h opt_iscsi_initiator.h +SRCS+= bus_if.h device_if.h + +# Debugging +# CFLAGS+= -DISCSI_INITIATOR_DEBUG=9 + +.include diff -urN p4/freebsd/src/tools/regression/iscsi/ctl.conf p4/iscsi/tools/regression/iscsi/ctl.conf --- p4/freebsd/src/tools/regression/iscsi/ctl.conf 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/tools/regression/iscsi/ctl.conf 2013-07-23 23:47:15.000000000 +0200 @@ -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 + } +} + diff -urN p4/freebsd/src/tools/regression/iscsi/initiator-instructions.txt p4/iscsi/tools/regression/iscsi/initiator-instructions.txt --- p4/freebsd/src/tools/regression/iscsi/initiator-instructions.txt 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/tools/regression/iscsi/initiator-instructions.txt 2013-07-23 23:47:15.000000000 +0200 @@ -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 + diff -urN p4/freebsd/src/tools/regression/iscsi/iscsi-test.sh p4/iscsi/tools/regression/iscsi/iscsi-test.sh --- p4/freebsd/src/tools/regression/iscsi/iscsi-test.sh 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/tools/regression/iscsi/iscsi-test.sh 2013-07-23 23:47:15.000000000 +0200 @@ -0,0 +1,725 @@ +#!/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() { + 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_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) + test_discovery_freebsd + ;; + Linux) + test_discovery_linux + ;; + SunOS) + test_discovery_solaris + ;; + *) + die "unsupported system" + ;; + esac +} + +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 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_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) + test_attach_freebsd + ;; + 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) + echo "*** detaching not supported on FreeBSD ***" + echo "*** please reboot the initiator VM before trying to run this script again ***" + ;; + 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 ***" + diff -urN p4/freebsd/src/usr.bin/Makefile p4/iscsi/usr.bin/Makefile --- p4/freebsd/src/usr.bin/Makefile 2013-07-24 07:52:16.000000000 +0200 +++ p4/iscsi/usr.bin/Makefile 2013-07-25 19:17:23.000000000 +0200 @@ -68,6 +68,7 @@ id \ ipcrm \ ipcs \ + iscsictl \ join \ jot \ ${_kdump} \ diff -urN p4/freebsd/src/usr.bin/iscsictl/Makefile p4/iscsi/usr.bin/iscsictl/Makefile --- p4/freebsd/src/usr.bin/iscsictl/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.bin/iscsictl/Makefile 2013-07-23 23:47:58.000000000 +0200 @@ -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 diff -urN p4/freebsd/src/usr.bin/iscsictl/iscsictl.8 p4/iscsi/usr.bin/iscsictl/iscsictl.8 --- p4/freebsd/src/usr.bin/iscsictl/iscsictl.8 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.bin/iscsictl/iscsictl.8 2013-07-23 23:47:58.000000000 +0200 @@ -0,0 +1,142 @@ +.\" 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 +.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 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. diff -urN p4/freebsd/src/usr.bin/iscsictl/iscsictl.c p4/iscsi/usr.bin/iscsictl/iscsictl.c --- p4/freebsd/src/usr.bin/iscsictl/iscsictl.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.bin/iscsictl/iscsictl.c 2013-08-18 13:07:26.000000000 +0200 @@ -0,0 +1,708 @@ +/*- + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#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 +desc_from_target(struct iscsi_session_desc *desc, + const struct target *targ) +{ + memset(desc, 0, sizeof(*desc)); + + /* + * XXX: Check bounds and return error instead of silently truncating. + */ + if (targ->t_initiator_name != NULL) + strlcpy(desc->isd_initiator, targ->t_initiator_name, + sizeof(desc->isd_initiator)); + if (targ->t_initiator_address != NULL) + strlcpy(desc->isd_initiator_addr, targ->t_initiator_address, + sizeof(desc->isd_initiator_addr)); + if (targ->t_initiator_alias != NULL) + strlcpy(desc->isd_initiator_alias, targ->t_initiator_alias, + sizeof(desc->isd_initiator_alias)); + if (targ->t_name != NULL) + strlcpy(desc->isd_target, targ->t_name, + sizeof(desc->isd_target)); + if (targ->t_address != NULL) + strlcpy(desc->isd_target_addr, targ->t_address, + sizeof(desc->isd_target_addr)); + if (targ->t_user != NULL) + strlcpy(desc->isd_user, targ->t_user, + sizeof(desc->isd_user)); + if (targ->t_secret != NULL) + strlcpy(desc->isd_secret, targ->t_secret, + sizeof(desc->isd_secret)); + if (targ->t_mutual_user != NULL) + strlcpy(desc->isd_mutual_user, targ->t_mutual_user, + sizeof(desc->isd_mutual_user)); + if (targ->t_mutual_secret != NULL) + strlcpy(desc->isd_mutual_secret, targ->t_mutual_secret, + sizeof(desc->isd_mutual_secret)); + if (targ->t_session_type == SESSION_TYPE_DISCOVERY) + desc->isd_discovery = 1; + if (targ->t_protocol == PROTOCOL_ISER) + desc->isd_iser = 1; + if (targ->t_header_digest == DIGEST_CRC32C) + desc->isd_header_digest = 1; + if (targ->t_data_digest == DIGEST_CRC32C) + desc->isd_data_digest = 1; +} + +static int +kernel_add(int iscsi_fd, const struct target *targ) +{ + struct iscsi_session_add isa; + int error; + + desc_from_target(&isa.isa_desc, 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; + + desc_from_target(&isr.isr_desc, 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_desc *descs = NULL; + struct iscsi_session_list isl; + const char *state; + unsigned int i, nentries = 1; + int error; + + for (;;) { + descs = realloc(descs, + nentries * sizeof(struct iscsi_session_desc)); + if (descs == NULL) + err(1, "realloc"); + + isl.isl_nentries = nentries; + isl.isl_pdesc = descs; + + 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++) { + printf("Session ID: %d\n", descs[i].isd_id); + printf("Session type: %s\n", + descs[i].isd_discovery ? "Discovery" : "Normal"); + printf("Session state: %s\n", + descs[i].isd_connected ? + "Connected" : "Disconnected"); + printf("Initiator name: %s\n", descs[i].isd_initiator); + printf("Initiator addr: %s\n", + descs[i].isd_initiator_addr); + printf("Initiator alias: %s\n", + descs[i].isd_initiator_alias); + printf("Target name: %s\n", descs[i].isd_target); + printf("Target addr: %s\n", + descs[i].isd_target_addr); + printf("Target alias: %s\n", + descs[i].isd_target_alias); + printf("User: %s\n", descs[i].isd_user); + printf("Secret: %s\n", descs[i].isd_secret); + printf("Mutual user: %s\n", + descs[i].isd_mutual_user); + printf("Mutual secret: %s\n", + descs[i].isd_mutual_secret); + printf("Header digest: %s\n", + descs[i].isd_header_digest ? "CRC32C" : "None"); + printf("Data digest: %s\n", + descs[i].isd_data_digest ? "CRC32C" : "None"); + printf("DataSegmentLen: %d\n", + descs[i].isd_max_data_segment_length); + printf("ImmediateData: %s\n", + descs[i].isd_immediate_data ? "Yes" : "No"); + printf("iSER (RDMA): %s\n", + descs[i].isd_iser ? "Yes" : "No"); + printf("Devices: "); + print_periphs(descs[i].isd_id); + printf("\n\n"); + } + } else { + printf("%-36s %-16s %-16s %s\n", + "Target name", "Target addr", "State", "Devices"); + for (i = 0; i < isl.isl_nentries; i++) { + if (descs[i].isd_discovery) + state = "Discovery"; + else if (descs[i].isd_connected) + state = "Connected"; + else + state = "Disconnected"; + printf("%-36s %-16s %-16s ", + descs[i].isd_target, descs[i].isd_target_addr, + state); + print_periphs(descs[i].isd_id); + printf("\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); +} diff -urN p4/freebsd/src/usr.bin/iscsictl/iscsictl.h p4/iscsi/usr.bin/iscsictl/iscsictl.h --- p4/freebsd/src/usr.bin/iscsictl/iscsictl.h 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.bin/iscsictl/iscsictl.h 2013-07-24 12:25:00.000000000 +0200 @@ -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 +#include +#include + +#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 */ diff -urN p4/freebsd/src/usr.bin/iscsictl/parse.y p4/iscsi/usr.bin/iscsictl/parse.y --- p4/freebsd/src/usr.bin/iscsictl/parse.y 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.bin/iscsictl/parse.y 2013-07-24 12:25:00.000000000 +0200 @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +%% + +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); +} diff -urN p4/freebsd/src/usr.bin/iscsictl/periphs.c p4/iscsi/usr.bin/iscsictl/periphs.c --- p4/freebsd/src/usr.bin/iscsictl/periphs.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.bin/iscsictl/periphs.c 2013-07-23 23:47:58.000000000 +0200 @@ -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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} + diff -urN p4/freebsd/src/usr.bin/iscsictl/token.l p4/iscsi/usr.bin/iscsictl/token.l --- p4/freebsd/src/usr.bin/iscsictl/token.l 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.bin/iscsictl/token.l 2013-07-24 12:25:00.000000000 +0200 @@ -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 +#include +#include + +#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 */; +%% diff -urN p4/freebsd/src/usr.sbin/Makefile p4/iscsi/usr.sbin/Makefile --- p4/freebsd/src/usr.sbin/Makefile 2013-07-24 07:52:16.000000000 +0200 +++ p4/iscsi/usr.sbin/Makefile 2013-07-25 19:17:23.000000000 +0200 @@ -17,6 +17,7 @@ crashinfo \ cron \ ctladm \ + ctld \ daemon \ dconschat \ devinfo \ @@ -35,6 +36,7 @@ ifmcstat \ inetd \ iostat \ + iscsid \ isfctl \ kldxref \ mailwrapper \ diff -urN p4/freebsd/src/usr.sbin/ctladm/ctladm.8 p4/iscsi/usr.sbin/ctladm/ctladm.8 --- p4/freebsd/src/usr.sbin/ctladm/ctladm.8 2013-04-20 20:21:50.000000000 +0200 +++ p4/iscsi/usr.sbin/ctladm/ctladm.8 2013-07-23 23:48:06.000000000 +0200 @@ -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 @@ .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 @@ .Xr cam 4 , .Xr ctl 4 , .Xr xpt 4 , -.Xr camcontrol 8 +.Xr camcontrol 8 , +.Xr ctld 8 .Sh HISTORY The .Nm diff -urN p4/freebsd/src/usr.sbin/ctladm/ctladm.c p4/iscsi/usr.sbin/ctladm/ctladm.c --- p4/freebsd/src/usr.sbin/ctladm/ctladm.c 2013-06-19 11:03:28.000000000 +0200 +++ p4/iscsi/usr.sbin/ctladm/ctladm.c 2013-08-18 13:07:26.000000000 +0200 @@ -117,7 +117,10 @@ 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 @@ {"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 @@ case CTL_PORT_ISC: type = "ISC"; break; + case CTL_PORT_ISCSI: + type = "ISCSI"; + break; default: type = "UNKNOWN"; break; @@ -578,6 +587,7 @@ {"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 @@ 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 @@ " [-s len fmt [args]] [-c] [-d delete_id]\n" " ctladm port <-l | -o | [-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 @@ 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); diff -urN p4/freebsd/src/usr.sbin/ctld/Makefile p4/iscsi/usr.sbin/ctld/Makefile --- p4/freebsd/src/usr.sbin/ctld/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.sbin/ctld/Makefile 2013-08-19 12:30:04.000000000 +0200 @@ -0,0 +1,20 @@ +# $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+= -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 diff -urN p4/freebsd/src/usr.sbin/ctld/ctl.conf.5 p4/iscsi/usr.sbin/ctld/ctl.conf.5 --- p4/freebsd/src/usr.sbin/ctld/ctl.conf.5 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.sbin/ctld/ctl.conf.5 2013-07-23 23:48:06.000000000 +0200 @@ -0,0 +1,234 @@ +.\" 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 + +auth-group { + chap + ... +} + +portal-group { + listen
+ listen-iser
+ discovery-auth-group + ... +} + +target { + auth-group + portal-group + lun { + 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 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. +.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. diff -urN p4/freebsd/src/usr.sbin/ctld/ctld.8 p4/iscsi/usr.sbin/ctld/ctld.8 --- p4/freebsd/src/usr.sbin/ctld/ctld.8 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.sbin/ctld/ctld.8 2013-07-23 23:48:06.000000000 +0200 @@ -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. diff -urN p4/freebsd/src/usr.sbin/ctld/ctld.c p4/iscsi/usr.sbin/ctld/ctld.c --- p4/freebsd/src/usr.sbin/ctld/ctld.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.sbin/ctld/ctld.c 2013-08-18 13:07:26.000000000 +0200 @@ -0,0 +1,1599 @@ +/*- + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ctld.h" + +static volatile bool sighup_received = false; +static volatile bool sigterm_received = false; + +static void +usage(void) +{ + + fprintf(stderr, "usage: ctld [-d][-f config-file]\n"); + exit(1); +} + +char * +checked_strdup(const char *s) +{ + char *c; + + c = strdup(s); + if (c == NULL) + log_err(1, "strdup"); + return (c); +} + +struct conf * +conf_new(void) +{ + struct conf *conf; + + conf = calloc(1, sizeof(*conf)); + if (conf == NULL) + log_err(1, "calloc"); + TAILQ_INIT(&conf->conf_targets); + TAILQ_INIT(&conf->conf_auth_groups); + TAILQ_INIT(&conf->conf_portal_groups); + + return (conf); +} + +void +conf_delete(struct conf *conf) +{ + struct target *targ, *tmp; + struct auth_group *ag, *cagtmp; + struct portal_group *pg, *cpgtmp; + + assert(conf->conf_pidfh == NULL); + + TAILQ_FOREACH_SAFE(targ, &conf->conf_targets, t_next, tmp) + target_delete(targ); + TAILQ_FOREACH_SAFE(ag, &conf->conf_auth_groups, ag_next, cagtmp) + auth_group_delete(ag); + TAILQ_FOREACH_SAFE(pg, &conf->conf_portal_groups, pg_next, cpgtmp) + portal_group_delete(pg); + free(conf->conf_pidfile_path); + free(conf); +} + +static struct auth * +auth_new(struct auth_group *ag) +{ + struct auth *auth; + + auth = calloc(1, sizeof(*auth)); + if (auth == NULL) + log_err(1, "calloc"); + auth->a_auth_group = ag; + TAILQ_INSERT_TAIL(&ag->ag_auths, auth, a_next); + return (auth); +} + +static void +auth_delete(struct auth *auth) +{ + TAILQ_REMOVE(&auth->a_auth_group->ag_auths, auth, a_next); + + free(auth->a_user); + free(auth->a_secret); + free(auth->a_mutual_user); + free(auth->a_mutual_secret); + free(auth); +} + +const struct auth * +auth_find(struct auth_group *ag, const char *user) +{ + const struct auth *auth; + + TAILQ_FOREACH(auth, &ag->ag_auths, a_next) { + if (strcmp(auth->a_user, user) == 0) + return (auth); + } + + return (NULL); +} + +struct auth_group * +auth_group_new(struct conf *conf, const char *name) +{ + struct auth_group *ag; + + if (name != NULL) { + ag = auth_group_find(conf, name); + if (ag != NULL) { + log_warnx("duplicated auth-group \"%s\"", name); + return (NULL); + } + } + + ag = calloc(1, sizeof(*ag)); + if (ag == NULL) + log_err(1, "calloc"); + if (name != NULL) + ag->ag_name = checked_strdup(name); + TAILQ_INIT(&ag->ag_auths); + ag->ag_conf = conf; + TAILQ_INSERT_TAIL(&conf->conf_auth_groups, ag, ag_next); + + return (ag); +} + +void +auth_group_delete(struct auth_group *ag) +{ + struct auth *auth, *tmp; + + TAILQ_REMOVE(&ag->ag_conf->conf_auth_groups, ag, ag_next); + + TAILQ_FOREACH_SAFE(auth, &ag->ag_auths, a_next, tmp) + auth_delete(auth); + free(ag->ag_name); + free(ag); +} + +struct auth_group * +auth_group_find(struct conf *conf, const char *name) +{ + struct auth_group *ag; + + TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) { + if (ag->ag_name != NULL && strcmp(ag->ag_name, name) == 0) + return (ag); + } + + return (NULL); +} + +static void +auth_check_secret_length(struct auth *auth) +{ + size_t len; + + len = strlen(auth->a_secret); + if (len > 16) { + if (auth->a_auth_group->ag_name != NULL) + log_warnx("secret for user \"%s\", auth-group \"%s\", " + "is too long; it should be at most 16 characters " + "long", auth->a_user, auth->a_auth_group->ag_name); + else + log_warnx("secret for user \"%s\", target \"%s\", " + "is too long; it should be at most 16 characters " + "long", auth->a_user, + auth->a_auth_group->ag_target->t_iqn); + } + if (len < 12) { + if (auth->a_auth_group->ag_name != NULL) + log_warnx("secret for user \"%s\", auth-group \"%s\", " + "is too short; it should be at least 12 characters " + "long", auth->a_user, + auth->a_auth_group->ag_name); + else + log_warnx("secret for user \"%s\", target \"%s\", " + "is too short; it should be at least 16 characters " + "long", auth->a_user, + auth->a_auth_group->ag_target->t_iqn); + } + + if (auth->a_mutual_secret != NULL) { + len = strlen(auth->a_secret); + if (len > 16) { + if (auth->a_auth_group->ag_name != NULL) + log_warnx("mutual secret for user \"%s\", " + "auth-group \"%s\", is too long; it should " + "be at most 16 characters long", + auth->a_user, auth->a_auth_group->ag_name); + else + log_warnx("mutual secret for user \"%s\", " + "target \"%s\", is too long; it should " + "be at most 16 characters long", + auth->a_user, + auth->a_auth_group->ag_target->t_iqn); + } + if (len < 12) { + if (auth->a_auth_group->ag_name != NULL) + log_warnx("mutual secret for user \"%s\", " + "auth-group \"%s\", is too short; it " + "should be at least 12 characters long", + auth->a_user, auth->a_auth_group->ag_name); + else + log_warnx("mutual secret for user \"%s\", " + "target \"%s\", is too short; it should be " + "at least 16 characters long", + auth->a_user, + auth->a_auth_group->ag_target->t_iqn); + } + } +} + +const struct auth * +auth_new_chap(struct auth_group *ag, const char *user, + const char *secret) +{ + struct auth *auth; + + if (ag->ag_type == AG_TYPE_UNKNOWN) + ag->ag_type = AG_TYPE_CHAP; + if (ag->ag_type != AG_TYPE_CHAP) { + if (ag->ag_name != NULL) + log_warnx("cannot mix \"chap\" authentication with " + "other types for auth-group \"%s\"", ag->ag_name); + else + log_warnx("cannot mix \"chap\" authentication with " + "other types for target \"%s\"", + ag->ag_target->t_iqn); + return (NULL); + } + + auth = auth_new(ag); + auth->a_user = checked_strdup(user); + auth->a_secret = checked_strdup(secret); + + auth_check_secret_length(auth); + + return (auth); +} + +const struct auth * +auth_new_chap_mutual(struct auth_group *ag, const char *user, + const char *secret, const char *user2, const char *secret2) +{ + struct auth *auth; + + if (ag->ag_type == AG_TYPE_UNKNOWN) + ag->ag_type = AG_TYPE_CHAP_MUTUAL; + if (ag->ag_type != AG_TYPE_CHAP_MUTUAL) { + if (ag->ag_name != NULL) + log_warnx("cannot mix \"chap-mutual\" authentication " + "with other types for auth-group \"%s\"", + ag->ag_name); + else + log_warnx("cannot mix \"chap-mutual\" authentication " + "with other types for target \"%s\"", + ag->ag_target->t_iqn); + return (NULL); + } + + auth = auth_new(ag); + auth->a_user = checked_strdup(user); + auth->a_secret = checked_strdup(secret); + auth->a_mutual_user = checked_strdup(user2); + auth->a_mutual_secret = checked_strdup(secret2); + + auth_check_secret_length(auth); + + return (auth); +} + +static struct portal * +portal_new(struct portal_group *pg) +{ + struct portal *portal; + + portal = calloc(1, sizeof(*portal)); + if (portal == NULL) + log_err(1, "calloc"); + TAILQ_INIT(&portal->p_targets); + portal->p_portal_group = pg; + TAILQ_INSERT_TAIL(&pg->pg_portals, portal, p_next); + return (portal); +} + +static void +portal_delete(struct portal *portal) +{ + TAILQ_REMOVE(&portal->p_portal_group->pg_portals, portal, p_next); + freeaddrinfo(portal->p_ai); + free(portal->p_listen); + free(portal); +} + +struct portal_group * +portal_group_new(struct conf *conf, const char *name) +{ + struct portal_group *pg; + + if (name != NULL) { + pg = portal_group_find(conf, name); + if (pg != NULL) { + log_warnx("duplicated portal-group \"%s\"", name); + return (NULL); + } + } + + pg = calloc(1, sizeof(*pg)); + if (pg == NULL) + log_err(1, "calloc"); + pg->pg_name = checked_strdup(name); + TAILQ_INIT(&pg->pg_portals); + pg->pg_conf = conf; + conf->conf_last_portal_group_tag++; + pg->pg_tag = conf->conf_last_portal_group_tag; + TAILQ_INSERT_TAIL(&conf->conf_portal_groups, pg, pg_next); + + return (pg); +} + +void +portal_group_delete(struct portal_group *pg) +{ + struct portal *portal, *tmp; + + TAILQ_REMOVE(&pg->pg_conf->conf_portal_groups, pg, pg_next); + + TAILQ_FOREACH_SAFE(portal, &pg->pg_portals, p_next, tmp) + portal_delete(portal); + free(pg->pg_name); + free(pg); +} + +struct portal_group * +portal_group_find(struct conf *conf, const char *name) +{ + struct portal_group *pg; + + TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { + if (strcmp(pg->pg_name, name) == 0) + return (pg); + } + + return (NULL); +} + +int +portal_group_add_listen(struct portal_group *pg, const char *value, bool iser) +{ + struct addrinfo hints; + struct portal *portal; + char *addr, *ch, *arg; + const char *port; + int error, colons = 0; + +#ifndef ICL_KERNEL_PROXY + if (iser) { + log_warnx("ctld(8) compiled without ICL_KERNEL_PROXY " + "does not support iSER protocol"); + return (-1); + } +#endif + + portal = portal_new(pg); + portal->p_listen = checked_strdup(value); + portal->p_iser = iser; + + arg = portal->p_listen; + if (arg[0] == '\0') { + log_warnx("empty listen address"); + free(portal->p_listen); + free(portal); + return (1); + } + if (arg[0] == '[') { + /* + * IPv6 address in square brackets, perhaps with port. + */ + arg++; + addr = strsep(&arg, "]"); + if (arg == NULL) { + log_warnx("invalid listen address %s", + portal->p_listen); + free(portal->p_listen); + free(portal); + return (1); + } + if (arg[0] == '\0') { + port = "3260"; + } else if (arg[0] == ':') { + port = arg + 1; + } else { + log_warnx("invalid listen address %s", + portal->p_listen); + free(portal->p_listen); + free(portal); + 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, &portal->p_ai); + if (error != 0) { + log_warnx("getaddrinfo for %s failed: %s", + portal->p_listen, gai_strerror(error)); + free(portal->p_listen); + free(portal); + return (1); + } + + /* + * XXX: getaddrinfo(3) may return multiple addresses; we should turn + * those into multiple portals. + */ + + return (0); +} + +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) { + log_warnx("overlong name for target \"%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; + log_warnx("invalid character \"%c\" in target 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) + log_warnx("invalid target 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])) { + log_warnx("invalid character \"%c\" in target " + "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) + log_warnx("invalid target 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])) { + log_warnx("invalid character \"%c\" in target " + "name \"%s\"; allowed characters are 1-9 " + "and A-F", name[i], name); + break; + } + } + } else { + log_warnx("invalid target name \"%s\"; should start with " + "either \".iqn\", \"eui.\", or \"naa.\"", + name); + } + return (true); +} + +struct target * +target_new(struct conf *conf, const char *iqn) +{ + struct target *targ; + int i, len; + + targ = target_find(conf, iqn); + if (targ != NULL) { + log_warnx("duplicated target \"%s\"", iqn); + return (NULL); + } + if (valid_iscsi_name(iqn) == false) { + log_warnx("target name \"%s\" is invalid", iqn); + return (NULL); + } + targ = calloc(1, sizeof(*targ)); + if (targ == NULL) + log_err(1, "calloc"); + targ->t_iqn = checked_strdup(iqn); + + /* + * RFC 3722 requires us to normalize the name to lowercase. + */ + len = strlen(iqn); + for (i = 0; i < len; i++) + targ->t_iqn[i] = tolower(targ->t_iqn[i]); + + TAILQ_INIT(&targ->t_luns); + targ->t_conf = conf; + TAILQ_INSERT_TAIL(&conf->conf_targets, targ, t_next); + + return (targ); +} + +void +target_delete(struct target *targ) +{ + struct lun *lun, *tmp; + + TAILQ_REMOVE(&targ->t_conf->conf_targets, targ, t_next); + + TAILQ_FOREACH_SAFE(lun, &targ->t_luns, l_next, tmp) + lun_delete(lun); + free(targ->t_iqn); + free(targ); +} + +struct target * +target_find(struct conf *conf, const char *iqn) +{ + struct target *targ; + + TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { + if (strcasecmp(targ->t_iqn, iqn) == 0) + return (targ); + } + + return (NULL); +} + +struct lun * +lun_new(struct target *targ, int lun_id) +{ + struct lun *lun; + + lun = lun_find(targ, lun_id); + if (lun != NULL) { + log_warnx("duplicated lun %d for target \"%s\"", + lun_id, targ->t_iqn); + return (NULL); + } + + lun = calloc(1, sizeof(*lun)); + if (lun == NULL) + log_err(1, "calloc"); + lun->l_lun = lun_id; + TAILQ_INIT(&lun->l_options); + lun->l_target = targ; + TAILQ_INSERT_TAIL(&targ->t_luns, lun, l_next); + + return (lun); +} + +void +lun_delete(struct lun *lun) +{ + struct lun_option *lo, *tmp; + + TAILQ_REMOVE(&lun->l_target->t_luns, lun, l_next); + + TAILQ_FOREACH_SAFE(lo, &lun->l_options, lo_next, tmp) + lun_option_delete(lo); + free(lun->l_backend); + free(lun->l_device_id); + free(lun->l_path); + free(lun->l_serial); + free(lun); +} + +struct lun * +lun_find(struct target *targ, int lun_id) +{ + struct lun *lun; + + TAILQ_FOREACH(lun, &targ->t_luns, l_next) { + if (lun->l_lun == lun_id) + return (lun); + } + + return (NULL); +} + +void +lun_set_backend(struct lun *lun, const char *value) +{ + free(lun->l_backend); + lun->l_backend = checked_strdup(value); +} + +void +lun_set_blocksize(struct lun *lun, size_t value) +{ + + lun->l_blocksize = value; +} + +void +lun_set_device_id(struct lun *lun, const char *value) +{ + free(lun->l_device_id); + lun->l_device_id = checked_strdup(value); +} + +void +lun_set_path(struct lun *lun, const char *value) +{ + free(lun->l_path); + lun->l_path = checked_strdup(value); +} + +void +lun_set_serial(struct lun *lun, const char *value) +{ + free(lun->l_serial); + lun->l_serial = checked_strdup(value); +} + +void +lun_set_size(struct lun *lun, size_t value) +{ + + lun->l_size = value; +} + +void +lun_set_ctl_lun(struct lun *lun, uint32_t value) +{ + + lun->l_ctl_lun = value; +} + +struct lun_option * +lun_option_new(struct lun *lun, const char *name, const char *value) +{ + struct lun_option *lo; + + lo = lun_option_find(lun, name); + if (lo != NULL) { + log_warnx("duplicated lun option %s for lun %d, target \"%s\"", + name, lun->l_lun, lun->l_target->t_iqn); + return (NULL); + } + + lo = calloc(1, sizeof(*lo)); + if (lo == NULL) + log_err(1, "calloc"); + lo->lo_name = checked_strdup(name); + lo->lo_value = checked_strdup(value); + lo->lo_lun = lun; + TAILQ_INSERT_TAIL(&lun->l_options, lo, lo_next); + + return (lo); +} + +void +lun_option_delete(struct lun_option *lo) +{ + + TAILQ_REMOVE(&lo->lo_lun->l_options, lo, lo_next); + + free(lo->lo_name); + free(lo->lo_value); + free(lo); +} + +struct lun_option * +lun_option_find(struct lun *lun, const char *name) +{ + struct lun_option *lo; + + TAILQ_FOREACH(lo, &lun->l_options, lo_next) { + if (strcmp(lo->lo_name, name) == 0) + return (lo); + } + + return (NULL); +} + +void +lun_option_set(struct lun_option *lo, const char *value) +{ + + free(lo->lo_value); + lo->lo_value = checked_strdup(value); +} + +static struct connection * +connection_new(struct portal *portal, int fd, const char *host) +{ + struct connection *conn; + + conn = calloc(1, sizeof(*conn)); + if (conn == NULL) + log_err(1, "calloc"); + conn->conn_portal = portal; + conn->conn_socket = fd; + conn->conn_initiator_addr = checked_strdup(host); + + /* + * Default values, from RFC 3720, section 12. + */ + conn->conn_max_data_segment_length = 8192; + conn->conn_max_burst_length = 262144; + conn->conn_immediate_data = true; + + return (conn); +} + +#if 0 +static void +conf_print(struct conf *conf) +{ + struct auth_group *ag; + struct auth *auth; + struct portal_group *pg; + struct portal *portal; + struct target *targ; + struct lun *lun; + struct lun_option *lo; + + TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) { + fprintf(stderr, "auth-group %s {\n", ag->ag_name); + TAILQ_FOREACH(auth, &ag->ag_auths, a_next) + fprintf(stderr, "\t chap-mutual %s %s %s %s\n", + auth->a_user, auth->a_secret, + auth->a_mutual_user, auth->a_mutual_secret); + fprintf(stderr, "}\n"); + } + TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { + fprintf(stderr, "portal-group %s {\n", pg->pg_name); + TAILQ_FOREACH(portal, &pg->pg_portals, p_next) + fprintf(stderr, "\t listen %s\n", portal->p_listen); + fprintf(stderr, "}\n"); + } + TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { + fprintf(stderr, "target %s {\n", targ->t_iqn); + if (targ->t_alias != NULL) + fprintf(stderr, "\t alias %s\n", targ->t_alias); + TAILQ_FOREACH(lun, &targ->t_luns, l_next) { + fprintf(stderr, "\tlun %d {\n", lun->l_lun); + fprintf(stderr, "\t\tpath %s\n", lun->l_path); + TAILQ_FOREACH(lo, &lun->l_options, lo_next) + fprintf(stderr, "\t\toption %s %s\n", + lo->lo_name, lo->lo_value); + fprintf(stderr, "\t}\n"); + } + fprintf(stderr, "}\n"); + } +} +#endif + +int +conf_verify(struct conf *conf) +{ + struct auth_group *ag; + struct portal_group *pg; + struct target *targ; + struct lun *lun, *lun2; + bool found_lun0; + + if (conf->conf_pidfile_path == NULL) + conf->conf_pidfile_path = checked_strdup(DEFAULT_PIDFILE); + + TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { + if (targ->t_auth_group == NULL) { + log_warnx("missing authentication for target \"%s\"; " + "must specify either \"auth-group\", \"chap\", " + "or \"chap-mutual\"", targ->t_iqn); + return (1); + } + if (targ->t_portal_group == NULL) { + targ->t_portal_group = portal_group_find(conf, + "default"); + assert(targ->t_portal_group != NULL); + } + found_lun0 = false; + TAILQ_FOREACH(lun, &targ->t_luns, l_next) { + if (lun->l_lun == 0) + found_lun0 = true; + if (lun->l_backend == NULL) + lun_set_backend(lun, "block"); + if (strcmp(lun->l_backend, "block") == 0 && + lun->l_path == NULL) { + log_warnx("missing path for lun %d, " + "target \"%s\"", lun->l_lun, targ->t_iqn); + return (1); + } + if (strcmp(lun->l_backend, "ramdisk") == 0) { + if (lun->l_size == 0) { + log_warnx("missing size for " + "ramdisk-backed lun %d, " + "target \"%s\"", + lun->l_lun, targ->t_iqn); + return (1); + } + if (lun->l_path != NULL) { + log_warnx("path must not be specified " + "for ramdisk-backed lun %d, " + "target \"%s\"", + lun->l_lun, targ->t_iqn); + return (1); + } + } + if (lun->l_lun < 0 || lun->l_lun > 255) { + log_warnx("invalid lun number for lun %d, " + "target \"%s\"; must be between 0 and 255", + lun->l_lun, targ->t_iqn); + return (1); + } +#if 1 /* Should we? */ + TAILQ_FOREACH(lun2, &targ->t_luns, l_next) { + if (lun == lun2) + continue; + if (lun->l_path != NULL && + lun2->l_path != NULL && + strcmp(lun->l_path, lun2->l_path) == 0) + log_debugx("WARNING: duplicate path " + "for lun %d, target \"%s\"", + lun->l_lun, targ->t_iqn); + } +#endif + if (lun->l_blocksize == 0) { + lun_set_blocksize(lun, DEFAULT_BLOCKSIZE); + } else if (lun->l_blocksize <= 0) { + log_warnx("invalid blocksize for lun %d, " + "target \"%s\"; must be larger than 0", + lun->l_lun, targ->t_iqn); + return (1); + } + if (lun->l_size != 0 && + lun->l_size % lun->l_blocksize != 0) { + log_warnx("invalid size for lun %d, target " + "\"%s\"; must be multiple of blocksize", + lun->l_lun, targ->t_iqn); + return (1); + } + } + if (!found_lun0) { + log_warnx("mandatory LUN 0 not configured " + "for target \"%s\"", targ->t_iqn); + return (1); + } + } + TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { + assert(pg->pg_name != NULL); + if (pg->pg_discovery_auth_group == NULL) { + pg->pg_discovery_auth_group = + auth_group_find(conf, "no-access"); + assert(pg->pg_discovery_auth_group != NULL); + } + + TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { + if (targ->t_portal_group == pg) + break; + } + if (targ == NULL) { + if (strcmp(pg->pg_name, "default") != 0) + log_warnx("portal-group \"%s\" not assigned " + "to any target", pg->pg_name); + pg->pg_unassigned = true; + } else + pg->pg_unassigned = false; + } + TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) { + if (ag->ag_name == NULL) + assert(ag->ag_target != NULL); + else + assert(ag->ag_target == NULL); + + TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { + if (targ->t_auth_group == ag) + break; + } + if (targ == NULL && ag->ag_name != NULL && + strcmp(ag->ag_name, "no-authentication") != 0 && + strcmp(ag->ag_name, "no-access") != 0) { + log_warnx("auth-group \"%s\" not assigned " + "to any target", ag->ag_name); + } + } + + return (0); +} + +static int +conf_apply(struct conf *oldconf, struct conf *newconf) +{ + struct target *oldtarg, *newtarg, *tmptarg; + struct lun *oldlun, *newlun, *tmplun; + struct portal_group *oldpg, *newpg; + struct portal *oldp, *newp; + pid_t otherpid; + int changed, cumulated_error = 0, error; +#ifndef ICL_KERNEL_PROXY + int one = 1; +#endif + + if (oldconf->conf_debug != newconf->conf_debug) { + log_debugx("changing debug level to %d", newconf->conf_debug); + log_init(newconf->conf_debug); + } + + if (oldconf->conf_pidfh != NULL) { + assert(oldconf->conf_pidfile_path != NULL); + if (newconf->conf_pidfile_path != NULL && + strcmp(oldconf->conf_pidfile_path, + newconf->conf_pidfile_path) == 0) { + newconf->conf_pidfh = oldconf->conf_pidfh; + oldconf->conf_pidfh = NULL; + } else { + log_debugx("removing pidfile %s", + oldconf->conf_pidfile_path); + pidfile_remove(oldconf->conf_pidfh); + oldconf->conf_pidfh = NULL; + } + } + + if (newconf->conf_pidfh == NULL && newconf->conf_pidfile_path != NULL) { + log_debugx("opening pidfile %s", newconf->conf_pidfile_path); + newconf->conf_pidfh = + pidfile_open(newconf->conf_pidfile_path, 0600, &otherpid); + if (newconf->conf_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\"", + newconf->conf_pidfile_path); + } + } + + TAILQ_FOREACH_SAFE(oldtarg, &oldconf->conf_targets, t_next, tmptarg) { + /* + * First, remove any targets present in the old configuration + * and missing in the new one. + */ + newtarg = target_find(newconf, oldtarg->t_iqn); + if (newtarg == NULL) { + TAILQ_FOREACH_SAFE(oldlun, &oldtarg->t_luns, l_next, + tmplun) { + log_debugx("target %s not found in the " + "configuration file; removing its lun %d, " + "backed by CTL lun %d", + oldtarg->t_iqn, oldlun->l_lun, + oldlun->l_ctl_lun); + error = kernel_lun_remove(oldlun); + if (error != 0) { + log_warnx("failed to remove lun %d, " + "target %s, CTL lun %d", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + cumulated_error++; + } + lun_delete(oldlun); + } + target_delete(oldtarg); + continue; + } + + /* + * Second, remove any LUNs present in the old target + * and missing in the new one. + */ + TAILQ_FOREACH_SAFE(oldlun, &oldtarg->t_luns, l_next, tmplun) { + newlun = lun_find(newtarg, oldlun->l_lun); + if (newlun == NULL) { + log_debugx("lun %d, target %s, CTL lun %d " + "not found in the configuration file; " + "removing", oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + error = kernel_lun_remove(oldlun); + if (error != 0) { + log_warnx("failed to remove lun %d, " + "target %s, CTL lun %d", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + cumulated_error++; + } + lun_delete(oldlun); + continue; + } + + /* + * Also remove the LUNs changed by more than size. + */ + changed = 0; + assert(oldlun->l_backend != NULL); + assert(newlun->l_backend != NULL); + if (strcmp(newlun->l_backend, oldlun->l_backend) != 0) { + log_debugx("backend for lun %d, target %s, " + "CTL lun %d changed; removing", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + changed = 1; + } + if (oldlun->l_blocksize != newlun->l_blocksize) { + log_debugx("blocksize for lun %d, target %s, " + "CTL lun %d changed; removing", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + changed = 1; + } + if (newlun->l_device_id != NULL && + (oldlun->l_device_id == NULL || + strcmp(oldlun->l_device_id, newlun->l_device_id) != + 0)) { + log_debugx("device-id for lun %d, target %s, " + "CTL lun %d changed; removing", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + changed = 1; + } + if (newlun->l_path != NULL && + (oldlun->l_path == NULL || + strcmp(oldlun->l_path, newlun->l_path) != 0)) { + log_debugx("path for lun %d, target %s, " + "CTL lun %d, changed; removing", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + changed = 1; + } + if (newlun->l_serial != NULL && + (oldlun->l_serial == NULL || + strcmp(oldlun->l_serial, newlun->l_serial) != 0)) { + log_debugx("serial for lun %d, target %s, " + "CTL lun %d changed; removing", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + changed = 1; + } + if (changed) { + error = kernel_lun_remove(oldlun); + if (error != 0) { + log_warnx("failed to remove lun %d, " + "target %s, CTL lun %d", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + cumulated_error++; + } + lun_delete(oldlun); + continue; + } + + lun_set_ctl_lun(newlun, oldlun->l_ctl_lun); + } + } + + /* + * Now add new targets or modify existing ones. + */ + TAILQ_FOREACH(newtarg, &newconf->conf_targets, t_next) { + oldtarg = target_find(oldconf, newtarg->t_iqn); + + TAILQ_FOREACH(newlun, &newtarg->t_luns, l_next) { + if (oldtarg != NULL) { + oldlun = lun_find(oldtarg, newlun->l_lun); + if (oldlun != NULL) { + if (newlun->l_size != oldlun->l_size) { + log_debugx("resizing lun %d, " + "target %s, CTL lun %d", + newlun->l_lun, + newtarg->t_iqn, + newlun->l_ctl_lun); + error = + kernel_lun_resize(newlun); + if (error != 0) { + log_warnx("failed to " + "resize lun %d, " + "target %s, " + "CTL lun %d", + newlun->l_lun, + newtarg->t_iqn, + newlun->l_lun); + cumulated_error++; + } + } + continue; + } + } + log_debugx("adding lun %d, target %s", + newlun->l_lun, newtarg->t_iqn); + error = kernel_lun_add(newlun); + if (error != 0) { + log_warnx("failed to add lun %d, target %s", + newlun->l_lun, newtarg->t_iqn); + cumulated_error++; + } + } + } + + /* + * Go through the new portals, opening the sockets as neccessary. + */ + TAILQ_FOREACH(newpg, &newconf->conf_portal_groups, pg_next) { + if (newpg->pg_unassigned) { + log_debugx("not listening on portal-group \"%s\", " + "not assigned to any target", + newpg->pg_name); + continue; + } + TAILQ_FOREACH(newp, &newpg->pg_portals, p_next) { + /* + * Try to find already open portal and reuse + * the listening socket. We don't care about + * what portal or portal group that was, what + * matters is the listening address. + */ + TAILQ_FOREACH(oldpg, &oldconf->conf_portal_groups, + pg_next) { + TAILQ_FOREACH(oldp, &oldpg->pg_portals, + p_next) { + if (strcmp(newp->p_listen, + oldp->p_listen) == 0 && + oldp->p_socket > 0) { + newp->p_socket = + oldp->p_socket; + oldp->p_socket = 0; + break; + } + } + } + if (newp->p_socket > 0) { + /* + * We're done with this portal. + */ + continue; + } + +#ifdef ICL_KERNEL_PROXY + log_debugx("listening on %s, portal-group \"%s\" using ICL proxy", + newp->p_listen, newpg->pg_name); + kernel_listen(newp->p_ai, newp->p_iser); +#else + assert(newp->p_iser == false); + + log_debugx("listening on %s, portal-group \"%s\"", + newp->p_listen, newpg->pg_name); + newp->p_socket = socket(newp->p_ai->ai_family, + newp->p_ai->ai_socktype, + newp->p_ai->ai_protocol); + if (newp->p_socket < 0) { + log_warn("socket(2) failed for %s", + newp->p_listen); + cumulated_error++; + continue; + } + error = setsockopt(newp->p_socket, SOL_SOCKET, + SO_REUSEADDR, &one, sizeof(one)); + if (error != 0) { + log_warn("setsockopt(SO_REUSEADDR) failed " + "for %s", newp->p_listen); + close(newp->p_socket); + newp->p_socket = 0; + cumulated_error++; + continue; + } + error = bind(newp->p_socket, newp->p_ai->ai_addr, + newp->p_ai->ai_addrlen); + if (error != 0) { + log_warn("bind(2) failed for %s", + newp->p_listen); + close(newp->p_socket); + newp->p_socket = 0; + cumulated_error++; + continue; + } + error = listen(newp->p_socket, -1); + if (error != 0) { + log_warn("listen(2) failed for %s", + newp->p_listen); + close(newp->p_socket); + newp->p_socket = 0; + cumulated_error++; + continue; + } +#endif /* !ICL_KERNEL_PROXY */ + } + } + + /* + * Go through the no longer used sockets, closing them. + */ + TAILQ_FOREACH(oldpg, &oldconf->conf_portal_groups, pg_next) { + TAILQ_FOREACH(oldp, &oldpg->pg_portals, p_next) { + if (oldp->p_socket <= 0) + continue; + log_debugx("closing socket for %s, portal-group \"%s\"", + oldp->p_listen, oldpg->pg_name); + close(oldp->p_socket); + oldp->p_socket = 0; + } + } + + return (cumulated_error); +} + +static void +new_connection(struct portal *portal, int fd, bool dont_fork) +{ + struct connection *conn; +#ifndef ICL_KERNEL_PROXY + struct sockaddr_storage ss; + socklen_t sslen = sizeof(ss); + int error; +#endif + pid_t pid; + char host[NI_MAXHOST + 1]; + + if (dont_fork) { + log_debugx("incoming connection; not forking due to -d flag"); + } else { + pid = fork(); + if (pid < 0) + log_err(1, "fork"); + if (pid > 0) { + close(fd); + return; + } + } + pidfile_close(portal->p_portal_group->pg_conf->conf_pidfh); + +#ifdef ICL_KERNEL_PROXY + /* + * XXX + */ + log_set_peer_addr("XXX"); +#else + error = getpeername(fd, (struct sockaddr *)&ss, &sslen); + if (error != 0) + log_err(1, "getpeername"); + error = getnameinfo((struct sockaddr *)&ss, sslen, + host, sizeof(host), NULL, 0, NI_NUMERICHOST); + if (error != 0) + log_errx(1, "getaddrinfo: %s", gai_strerror(error)); + + log_debugx("accepted connection from %s; portal group \"%s\"", + host, portal->p_portal_group->pg_name); + log_set_peer_addr(host); + setproctitle("%s", host); +#endif + + conn = connection_new(portal, fd, host); + kernel_capsicate(); + login(conn); + if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { + kernel_handoff(conn); + log_debugx("connection handed off to the kernel"); + } else { + assert(conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY); + discovery(conn); + } + log_debugx("nothing more to do; exiting"); + exit(0); +} + +#ifndef ICL_KERNEL_PROXY +static int +fd_add(int fd, fd_set *fdset, int nfds) +{ + + /* + * Skip sockets which we failed to bind. + */ + if (fd <= 0) + return (nfds); + + FD_SET(fd, fdset); + if (fd > nfds) + nfds = fd; + return (nfds); +} +#endif + +static void +main_loop(struct conf *conf, bool dont_fork) +{ + struct portal_group *pg; + struct portal *portal; +#ifdef ICL_KERNEL_PROXY + int connection_id; +#else + fd_set fdset; + int error, nfds, client_fd; +#endif + + pidfile_write(conf->conf_pidfh); + + for (;;) { + if (sighup_received || sigterm_received) + return; + +#ifdef ICL_KERNEL_PROXY + connection_id = kernel_accept(); + if (connection_id == 0) + continue; + + /* + * XXX: This is obviously temporary. + */ + pg = TAILQ_FIRST(&conf->conf_portal_groups); + portal = TAILQ_FIRST(&pg->pg_portals); + + new_connection(portal, connection_id, dont_fork); +#else + FD_ZERO(&fdset); + nfds = 0; + TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { + TAILQ_FOREACH(portal, &pg->pg_portals, p_next) + nfds = fd_add(portal->p_socket, &fdset, nfds); + } + error = select(nfds + 1, &fdset, NULL, NULL, NULL); + if (error <= 0) { + if (errno == EINTR) + return; + log_err(1, "select"); + } + TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { + TAILQ_FOREACH(portal, &pg->pg_portals, p_next) { + if (!FD_ISSET(portal->p_socket, &fdset)) + continue; + client_fd = accept(portal->p_socket, NULL, 0); + if (client_fd < 0) + log_err(1, "accept"); + new_connection(portal, client_fd, dont_fork); + break; + } + } +#endif /* !ICL_KERNEL_PROXY */ + } +} + +static void +sighup_handler(int dummy __unused) +{ + + sighup_received = true; +} + +static void +sigterm_handler(int dummy __unused) +{ + + sigterm_received = true; +} + +static void +register_signals(void) +{ + struct sigaction sa; + int error; + + bzero(&sa, sizeof(sa)); + sa.sa_handler = sighup_handler; + sigfillset(&sa.sa_mask); + error = sigaction(SIGHUP, &sa, NULL); + if (error != 0) + log_err(1, "sigaction"); + + sa.sa_handler = sigterm_handler; + error = sigaction(SIGTERM, &sa, NULL); + if (error != 0) + log_err(1, "sigaction"); + + sa.sa_handler = sigterm_handler; + error = sigaction(SIGINT, &sa, NULL); + if (error != 0) + log_err(1, "sigaction"); + + sa.sa_handler = SIG_IGN; + error = sigaction(SIGCHLD, &sa, NULL); + if (error != 0) + log_err(1, "sigaction"); +} + +int +main(int argc, char **argv) +{ + struct conf *oldconf, *newconf, *tmpconf; + const char *config_path = DEFAULT_CONFIG_PATH; + int debug = 0, ch, error; + bool dont_daemonize = false; + + while ((ch = getopt(argc, argv, "df:")) != -1) { + switch (ch) { + case 'd': + dont_daemonize = true; + debug++; + break; + case 'f': + config_path = optarg; + break; + case '?': + default: + usage(); + } + } + argc -= optind; + if (argc != 0) + usage(); + + log_init(debug); + kernel_init(); + + oldconf = conf_new_from_kernel(); + newconf = conf_new_from_file(config_path); + if (newconf == NULL) + log_errx(1, "configuration error, exiting"); + if (debug > 0) { + oldconf->conf_debug = debug; + newconf->conf_debug = debug; + } + + if (dont_daemonize == false) { + if (daemon(0, 0) == -1) { + log_warn("cannot daemonize"); + pidfile_remove(newconf->conf_pidfh); + exit(1); + } + } + +#ifdef ICL_KERNEL_PROXY + log_debugx("enabling CTL iSCSI port"); + error = kernel_port_on(); + if (error != 0) + log_errx(1, "failed to enable CTL iSCSI port, exiting"); +#endif + + error = conf_apply(oldconf, newconf); + if (error != 0) + log_errx(1, "failed to apply configuration, exiting"); + conf_delete(oldconf); + oldconf = NULL; + + register_signals(); + +#ifndef ICL_KERNEL_PROXY + log_debugx("enabling CTL iSCSI port"); + error = kernel_port_on(); + if (error != 0) + log_errx(1, "failed to enable CTL iSCSI port, exiting"); +#endif + + for (;;) { + main_loop(newconf, dont_daemonize); + if (sighup_received) { + sighup_received = false; + log_debugx("received SIGHUP, reloading configuration"); + tmpconf = conf_new_from_file(config_path); + if (tmpconf == NULL) { + log_warnx("configuration error, " + "continuing with old configuration"); + } else { + if (debug > 0) + tmpconf->conf_debug = debug; + oldconf = newconf; + newconf = tmpconf; + error = conf_apply(oldconf, newconf); + if (error != 0) + log_warnx("failed to reload " + "configuration"); + conf_delete(oldconf); + oldconf = NULL; + } + } else { + log_debugx("exiting on signal; " + "reloading empty configuration"); + + log_debugx("disabling CTL iSCSI port " + "and terminating all connections"); + error = kernel_port_off(); + if (error != 0) + log_warnx("failed to disable CTL iSCSI port"); + + oldconf = newconf; + newconf = conf_new(); + if (debug > 0) + newconf->conf_debug = debug; + error = conf_apply(oldconf, newconf); + if (error != 0) + log_warnx("failed to apply configuration"); + + log_warnx("exiting on signal"); + exit(0); + } + } + /* NOTREACHED */ +} diff -urN p4/freebsd/src/usr.sbin/ctld/ctld.h p4/iscsi/usr.sbin/ctld/ctld.h --- p4/freebsd/src/usr.sbin/ctld/ctld.h 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.sbin/ctld/ctld.h 2013-08-18 13:07:26.000000000 +0200 @@ -0,0 +1,274 @@ +/*- + * 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 +#include +#include + +#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; + + 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); + +#endif /* !CTLD_H */ diff -urN p4/freebsd/src/usr.sbin/ctld/discovery.c p4/iscsi/usr.sbin/ctld/discovery.c --- p4/freebsd/src/usr.sbin/ctld/discovery.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.sbin/ctld/discovery.c 2013-07-23 23:48:06.000000000 +0200 @@ -0,0 +1,220 @@ +/*- + * 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 +#include +#include +#include +#include +#include +#include "ctld.h" +#include "iscsi.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"); +} diff -urN p4/freebsd/src/usr.sbin/ctld/kernel.c p4/iscsi/usr.sbin/ctld/kernel.c --- p4/freebsd/src/usr.sbin/ctld/kernel.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.sbin/ctld/kernel.c 2013-08-18 13:07:26.000000000 +0200 @@ -0,0 +1,780 @@ +/*- + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ICL_KERNEL_PROXY +#include +#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 *ctld_target; + char *ctld_target_alias; + int ctld_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, "ctld_target") == 0) { + cur_lun->ctld_target = str; + str = NULL; + } else if (strcmp(name, "ctld_target_alias") == 0) { + cur_lun->ctld_target_alias = str; + str = NULL; + } else if (strcmp(name, "ctld_lun") == 0) { + cur_lun->ctld_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->ctld_target == NULL) { + log_debugx("CTL lun %ju wasn't managed by ctld; " + "ignoring", (uintmax_t)lun->lun_id); + continue; + } + + targ = target_find(conf, lun->ctld_target); + if (targ == NULL) { +#if 0 + log_debugx("found new kernel target %s for CTL lun %ld", + lun->ctld_target, lun->lun_id); +#endif + targ = target_new(conf, lun->ctld_target); + if (targ == NULL) { + log_warnx("target_new failed"); + continue; + } + } + + cl = lun_find(targ, lun->ctld_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->ctld_lun, lun->ctld_target); + + cl = lun_new(targ, lun->ctld_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, "ctld_target"); + if (lo != NULL) { + lun_option_set(lo, lun->l_target->t_iqn); + } else { + lo = lun_option_new(lun, "ctld_target", + lun->l_target->t_iqn); + assert(lo != NULL); + } + + if (lun->l_target->t_alias != NULL) { + lo = lun_option_find(lun, "ctld_target_alias"); + if (lo != NULL) { + lun_option_set(lo, lun->l_target->t_alias); + } else { + lo = lun_option_new(lun, "ctld_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, "ctld_lun"); + if (lo != NULL) { + lun_option_set(lo, tmp); + free(tmp); + } else { + lo = lun_option_new(lun, "ctld_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; + const unsigned long cmds[] = { CTL_ISCSI }; + + error = cap_rights_limit(ctl_fd, CAP_IOCTL); + 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"); +} + diff -urN p4/freebsd/src/usr.sbin/ctld/keys.c p4/iscsi/usr.sbin/ctld/keys.c --- p4/freebsd/src/usr.sbin/ctld/keys.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.sbin/ctld/keys.c 2013-07-23 23:48:06.000000000 +0200 @@ -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 +#include +#include +#include +#include +#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); +} diff -urN p4/freebsd/src/usr.sbin/ctld/log.c p4/iscsi/usr.sbin/ctld/log.c --- p4/freebsd/src/usr.sbin/ctld/log.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.sbin/ctld/log.c 2013-07-23 23:48:06.000000000 +0200 @@ -0,0 +1,181 @@ +/*- + * 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 +#include +#include +#include +#include +#include + +#include "ctld.h" + +static int log_level = 0; +static char *peer_name = NULL; +static char *peer_addr = NULL; + +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) +{ + int ret; + char *msg; + + ret = vasprintf(&msg, fmt, ap); + if (ret == -1) { + /* + * XXX: vasprintf(3) doesn't set errno, right? + */ + fprintf(stderr, "%s: %s: %s\n", + getprogname(), __func__, strerror(ENOMEM)); + syslog(LOG_CRIT, "%s: %s", __func__, strerror(ENOMEM)); + exit(1); + } + + if (log_errno == -1) { + if (peer_name != NULL) { + fprintf(stderr, "%s: %s (%s): %s\n", getprogname(), peer_addr, peer_name, msg); + syslog(priority, "%s (%s): %s", peer_addr, peer_name, msg); + } else if (peer_addr != NULL) { + fprintf(stderr, "%s: %s: %s\n", getprogname(), peer_addr, msg); + syslog(priority, "%s: %s", peer_addr, msg); + } else { + fprintf(stderr, "%s: %s\n", getprogname(), msg); + syslog(priority, "%s", msg); + } + + } else { + if (peer_name != NULL) { + fprintf(stderr, "%s: %s (%s): %s: %s\n", getprogname(), peer_addr, peer_name, msg, strerror(errno)); + syslog(priority, "%s (%s): %s: %s", peer_addr, peer_name, msg, strerror(errno)); + } else if (peer_addr != NULL) { + fprintf(stderr, "%s: %s: %s: %s\n", getprogname(), peer_addr, msg, strerror(errno)); + syslog(priority, "%s: %s: %s", peer_addr, msg, strerror(errno)); + } else { + fprintf(stderr, "%s: %s: %s\n", getprogname(), msg, strerror(errno)); + syslog(priority, "%s: %s", msg, strerror(errno)); + } + } + + free(msg); +} + +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); +} diff -urN p4/freebsd/src/usr.sbin/ctld/login.c p4/iscsi/usr.sbin/ctld/login.c --- p4/freebsd/src/usr.sbin/ctld/login.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.sbin/ctld/login.c 2013-08-18 13:07:26.000000000 +0200 @@ -0,0 +1,1050 @@ +/*- + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ctld.h" +#include "iscsi.h" + +static void login_send_error(struct pdu *request, + char class, char detail); + +static void +login_set_nsg(struct pdu *response, int nsg) +{ + struct iscsi_bhs_login_response *bhslr; + + assert(nsg == BHSLR_STAGE_SECURITY_NEGOTIATION || + nsg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION || + nsg == BHSLR_STAGE_FULL_FEATURE_PHASE); + + bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs; + + bhslr->bhslr_flags &= 0xFC; + bhslr->bhslr_flags |= nsg; +} + +static int +login_csg(const struct pdu *request) +{ + struct iscsi_bhs_login_request *bhslr; + + bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; + + return ((bhslr->bhslr_flags & 0x0C) >> 2); +} + +static void +login_set_csg(struct pdu *response, int csg) +{ + struct iscsi_bhs_login_response *bhslr; + + assert(csg == BHSLR_STAGE_SECURITY_NEGOTIATION || + csg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION || + csg == BHSLR_STAGE_FULL_FEATURE_PHASE); + + bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs; + + bhslr->bhslr_flags &= 0xF3; + bhslr->bhslr_flags |= csg << 2; +} + +static struct pdu * +login_receive(struct connection *conn, bool initial) +{ + struct pdu *request; + struct iscsi_bhs_login_request *bhslr; + + request = pdu_new(conn); + pdu_receive(request); + if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != + ISCSI_BHS_OPCODE_LOGIN_REQUEST) { + /* + * The first PDU in session is special - if we receive any PDU + * different than login request, we have to drop the connection + * without sending response ("A target receiving any PDU + * except a Login request before the Login phase is started MUST + * immediately terminate the connection on which the PDU + * was received.") + */ + if (initial == false) + login_send_error(request, 0x02, 0x0b); + log_errx(1, "protocol error: received invalid opcode 0x%x", + request->pdu_bhs->bhs_opcode); + } + bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; + /* + * XXX: Implement the C flag some day. + */ + if ((bhslr->bhslr_flags & BHSLR_FLAGS_CONTINUE) != 0) { + login_send_error(request, 0x03, 0x00); + log_errx(1, "received Login PDU with unsupported \"C\" flag"); + } + if (bhslr->bhslr_version_max != 0x00) { + login_send_error(request, 0x02, 0x05); + log_errx(1, "received Login PDU with unsupported " + "Version-max 0x%x", bhslr->bhslr_version_max); + } + if (bhslr->bhslr_version_min != 0x00) { + login_send_error(request, 0x02, 0x05); + log_errx(1, "received Login PDU with unsupported " + "Version-min 0x%x", bhslr->bhslr_version_min); + } + if (request->pdu_data_len == 0) { + login_send_error(request, 0x02, 0x00); + log_errx(1, "received Login PDU with empty data segment"); + } + if (ntohl(bhslr->bhslr_cmdsn) < conn->conn_cmdsn) { + login_send_error(request, 0x02, 0x05); + log_errx(1, "received Login PDU with decreasing CmdSN: " + "was %d, is %d", conn->conn_cmdsn, + ntohl(bhslr->bhslr_cmdsn)); + } + if (initial == false && + ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) { + login_send_error(request, 0x02, 0x05); + log_errx(1, "received Login PDU with wrong ExpStatSN: " + "is %d, should be %d", ntohl(bhslr->bhslr_expstatsn), + conn->conn_statsn); + } + conn->conn_cmdsn = ntohl(bhslr->bhslr_cmdsn); + + return (request); +} + +static struct pdu * +login_new_response(struct pdu *request) +{ + struct pdu *response; + struct connection *conn; + struct iscsi_bhs_login_request *bhslr; + struct iscsi_bhs_login_response *bhslr2; + + bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; + conn = request->pdu_connection; + + response = pdu_new_response(request); + bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; + bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGIN_RESPONSE; + login_set_csg(response, BHSLR_STAGE_SECURITY_NEGOTIATION); + memcpy(bhslr2->bhslr_isid, + bhslr->bhslr_isid, sizeof(bhslr2->bhslr_isid)); + 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); +} + +static void +login_send_error(struct pdu *request, char class, char detail) +{ + struct pdu *response; + struct iscsi_bhs_login_response *bhslr2; + + log_debugx("sending Login Response PDU with failure class 0x%x/0x%x; " + "see next line for reason", class, detail); + response = login_new_response(request); + bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; + bhslr2->bhslr_status_class = class; + bhslr2->bhslr_status_detail = detail; + + pdu_send(response); + pdu_delete(response); +} + +static int +login_list_contains(const char *list, const char *what) +{ + char *tofree, *str, *token; + + tofree = str = checked_strdup(list); + + while ((token = strsep(&str, ",")) != NULL) { + if (strcmp(token, what) == 0) { + free(tofree); + return (1); + } + } + free(tofree); + return (0); +} + +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"); +} + +#define LOGIN_CHALLENGE_LEN 1024 + +static struct pdu * +login_receive_chap_a(struct connection *conn) +{ + struct pdu *request; + struct keys *request_keys; + const char *chap_a; + + request = login_receive(conn, false); + request_keys = keys_new(); + keys_load(request_keys, request); + + chap_a = keys_find(request_keys, "CHAP_A"); + if (chap_a == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received CHAP Login PDU without CHAP_A"); + } + if (login_list_contains(chap_a, "5") == 0) { + login_send_error(request, 0x02, 0x01); + log_errx(1, "received CHAP Login PDU with unsupported CHAP_A " + "\"%s\"", chap_a); + } + keys_delete(request_keys); + + return (request); +} + +static void +login_send_chap_c(struct pdu *request, const unsigned char id, + const void *challenge, const size_t challenge_len) +{ + struct pdu *response; + struct keys *response_keys; + char *chap_c, chap_i[4]; + + chap_c = login_bin2hex(challenge, challenge_len); + snprintf(chap_i, sizeof(chap_i), "%d", id); + + response = login_new_response(request); + response_keys = keys_new(); + keys_add(response_keys, "CHAP_A", "5"); + keys_add(response_keys, "CHAP_I", chap_i); + keys_add(response_keys, "CHAP_C", chap_c); + free(chap_c); + keys_save(response_keys, response); + keys_delete(response_keys); + pdu_send(response); +} + +static struct pdu * +login_receive_chap_r(struct connection *conn, + struct auth_group *ag, const unsigned char id, const void *challenge, + const size_t challenge_len, const struct auth **cap) +{ + struct pdu *request; + struct keys *request_keys; + const char *chap_n, *chap_r; + char *response_bin, expected_response_bin[MD5_DIGEST_LENGTH]; + size_t response_bin_len; + const struct auth *auth; + int error; + + request = login_receive(conn, false); + request_keys = keys_new(); + keys_load(request_keys, request); + + chap_n = keys_find(request_keys, "CHAP_N"); + if (chap_n == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received CHAP Login PDU without CHAP_N"); + } + chap_r = keys_find(request_keys, "CHAP_R"); + if (chap_r == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received CHAP Login PDU without CHAP_R"); + } + error = login_hex2bin(chap_r, &response_bin, &response_bin_len); + if (error != 0) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received CHAP Login PDU with malformed CHAP_R"); + } + + /* + * Verify the response. + */ + assert(ag->ag_type == AG_TYPE_CHAP || + ag->ag_type == AG_TYPE_CHAP_MUTUAL); + auth = auth_find(ag, chap_n); + if (auth == NULL) { + login_send_error(request, 0x02, 0x01); + log_errx(1, "received CHAP Login with invalid user \"%s\"", + chap_n); + } + + assert(auth->a_secret != NULL); + assert(strlen(auth->a_secret) > 0); + login_compute_md5(id, auth->a_secret, challenge, + challenge_len, expected_response_bin, + sizeof(expected_response_bin)); + + if (memcmp(response_bin, expected_response_bin, + sizeof(expected_response_bin)) != 0) { + login_send_error(request, 0x02, 0x01); + log_errx(1, "CHAP authentication failed for user \"%s\"", + auth->a_user); + } + + keys_delete(request_keys); + free(response_bin); + + *cap = auth; + return (request); +} + +static void +login_send_chap_success(struct pdu *request, + const struct auth *auth) +{ + struct pdu *response; + struct keys *request_keys, *response_keys; + struct iscsi_bhs_login_response *bhslr2; + const char *chap_i, *chap_c; + char *chap_r, *challenge, response_bin[MD5_DIGEST_LENGTH]; + size_t challenge_len; + unsigned char id; + int error; + + response = login_new_response(request); + bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; + bhslr2->bhslr_flags |= BHSLR_FLAGS_TRANSIT; + login_set_nsg(response, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); + + /* + * Actually, one more thing: mutual authentication. + */ + request_keys = keys_new(); + keys_load(request_keys, request); + chap_i = keys_find(request_keys, "CHAP_I"); + chap_c = keys_find(request_keys, "CHAP_C"); + if (chap_i != NULL || chap_c != NULL) { + if (chap_i == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "initiator requested target " + "authentication, but didn't send CHAP_I"); + } + if (chap_c == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "initiator requested target " + "authentication, but didn't send CHAP_C"); + } + if (auth->a_auth_group->ag_type != AG_TYPE_CHAP_MUTUAL) { + login_send_error(request, 0x02, 0x01); + log_errx(1, "initiator requests target authentication " + "for user \"%s\", but mutual user/secret " + "is not set", auth->a_user); + } + + id = strtoul(chap_i, NULL, 10); + error = login_hex2bin(chap_c, &challenge, &challenge_len); + if (error != 0) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received CHAP Login PDU with malformed " + "CHAP_C"); + } + + log_debugx("performing mutual authentication as user \"%s\"", + auth->a_mutual_user); + login_compute_md5(id, auth->a_mutual_secret, challenge, + challenge_len, response_bin, sizeof(response_bin)); + + chap_r = login_bin2hex(response_bin, + sizeof(response_bin)); + response_keys = keys_new(); + keys_add(response_keys, "CHAP_N", auth->a_mutual_user); + keys_add(response_keys, "CHAP_R", chap_r); + free(chap_r); + keys_save(response_keys, response); + keys_delete(response_keys); + } else { + log_debugx("initiator did not request target authentication"); + } + + keys_delete(request_keys); + pdu_send(response); +} + +static void +login_chap(struct connection *conn, struct auth_group *ag) +{ + const struct auth *auth; + struct pdu *request; + char challenge_bin[LOGIN_CHALLENGE_LEN]; + unsigned char id; + int rv; + + /* + * Receive CHAP_A PDU. + */ + log_debugx("beginning CHAP authentication; waiting for CHAP_A"); + request = login_receive_chap_a(conn); + + /* + * Generate the challenge. + */ + rv = RAND_bytes(challenge_bin, sizeof(challenge_bin)); + if (rv != 1) { + login_send_error(request, 0x03, 0x02); + log_errx(1, "RAND_bytes failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + rv = RAND_bytes(&id, sizeof(id)); + if (rv != 1) { + login_send_error(request, 0x03, 0x02); + log_errx(1, "RAND_bytes failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + + /* + * Send the challenge. + */ + log_debugx("sending CHAP_C, binary challenge size is %zd bytes", + sizeof(challenge_bin)); + login_send_chap_c(request, id, challenge_bin, + sizeof(challenge_bin)); + pdu_delete(request); + + /* + * Receive CHAP_N/CHAP_R PDU and authenticate. + */ + log_debugx("waiting for CHAP_N/CHAP_R"); + request = login_receive_chap_r(conn, ag, id, challenge_bin, + sizeof(challenge_bin), &auth); + + /* + * Yay, authentication succeeded! + */ + log_debugx("authentication succeeded for user \"%s\"; " + "transitioning to Negotiation phase", auth->a_user); + login_send_chap_success(request, auth); + pdu_delete(request); +} + +static void +login_negotiate_key(struct pdu *request, const char *name, + const char *value, bool skipped_security, struct keys *response_keys) +{ + int which, tmp; + struct connection *conn; + + conn = request->pdu_connection; + + if (strcmp(name, "InitiatorName") == 0) { + if (!skipped_security) + log_errx(1, "initiator resent InitiatorName"); + } else if (strcmp(name, "SessionType") == 0) { + if (!skipped_security) + log_errx(1, "initiator resent SessionType"); + } else if (strcmp(name, "TargetName") == 0) { + if (!skipped_security) + log_errx(1, "initiator resent TargetName"); + } else if (strcmp(name, "InitiatorAlias") == 0) { + if (conn->conn_initiator_alias != NULL) + free(conn->conn_initiator_alias); + conn->conn_initiator_alias = checked_strdup(value); + } else if (strcmp(value, "Irrelevant") == 0) { + /* Ignore. */ + } else if (strcmp(name, "HeaderDigest") == 0) { + /* + * We don't handle digests for discovery sessions. + */ + if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) { + log_debugx("discovery session; digests disabled"); + keys_add(response_keys, name, "None"); + return; + } + + which = login_list_prefers(value, "CRC32C", "None"); + switch (which) { + case 1: + log_debugx("initiator prefers CRC32C " + "for header digest; we'll use it"); + conn->conn_header_digest = CONN_DIGEST_CRC32C; + keys_add(response_keys, name, "CRC32C"); + break; + case 2: + log_debugx("initiator prefers not to do " + "header digest; we'll comply"); + keys_add(response_keys, name, "None"); + break; + default: + log_warnx("initiator sent unrecognized " + "HeaderDigest value \"%s\"; will use None", value); + keys_add(response_keys, name, "None"); + break; + } + } else if (strcmp(name, "DataDigest") == 0) { + if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) { + log_debugx("discovery session; digests disabled"); + keys_add(response_keys, name, "None"); + return; + } + + which = login_list_prefers(value, "CRC32C", "None"); + switch (which) { + case 1: + log_debugx("initiator prefers CRC32C " + "for data digest; we'll use it"); + conn->conn_data_digest = CONN_DIGEST_CRC32C; + keys_add(response_keys, name, "CRC32C"); + break; + case 2: + log_debugx("initiator prefers not to do " + "data digest; we'll comply"); + keys_add(response_keys, name, "None"); + break; + default: + log_warnx("initiator sent unrecognized " + "DataDigest value \"%s\"; will use None", value); + keys_add(response_keys, name, "None"); + break; + } + } else if (strcmp(name, "MaxConnections") == 0) { + keys_add(response_keys, name, "1"); + } else if (strcmp(name, "InitialR2T") == 0) { + keys_add(response_keys, name, "Yes"); + } else if (strcmp(name, "ImmediateData") == 0) { + if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) { + log_debugx("discovery session; ImmediateData irrelevant"); + keys_add(response_keys, name, "Irrelevant"); + } else { + if (strcmp(value, "Yes") == 0) { + conn->conn_immediate_data = true; + keys_add(response_keys, name, "Yes"); + } else { + conn->conn_immediate_data = false; + keys_add(response_keys, name, "No"); + } + } + } else if (strcmp(name, "MaxRecvDataSegmentLength") == 0) { + tmp = strtoul(value, NULL, 10); + if (tmp <= 0) { + login_send_error(request, 0x02, 0x00); + log_errx(1, "received invalid " + "MaxRecvDataSegmentLength"); + } + if (tmp > MAX_DATA_SEGMENT_LENGTH) { + log_debugx("capping MaxDataSegmentLength from %d to %d", + tmp, MAX_DATA_SEGMENT_LENGTH); + tmp = MAX_DATA_SEGMENT_LENGTH; + } + conn->conn_max_data_segment_length = tmp; + keys_add_int(response_keys, name, tmp); + } else if (strcmp(name, "MaxBurstLength") == 0) { + tmp = strtoul(value, NULL, 10); + if (tmp <= 0) { + login_send_error(request, 0x02, 0x00); + log_errx(1, "received invalid MaxBurstLength"); + } + if (tmp > MAX_BURST_LENGTH) { + log_debugx("capping MaxBurstLength from %d to %d", + tmp, MAX_BURST_LENGTH); + tmp = MAX_BURST_LENGTH; + } + conn->conn_max_burst_length = tmp; + keys_add(response_keys, name, value); + } else if (strcmp(name, "FirstBurstLength") == 0) { + tmp = strtoul(value, NULL, 10); + if (tmp <= 0) { + login_send_error(request, 0x02, 0x00); + log_errx(1, "received invalid " + "FirstBurstLength"); + } + if (tmp > MAX_DATA_SEGMENT_LENGTH) { + log_debugx("capping FirstBurstLength from %d to %d", + tmp, MAX_DATA_SEGMENT_LENGTH); + tmp = MAX_DATA_SEGMENT_LENGTH; + } + /* + * We don't pass the value to the kernel; it only enforces + * hardcoded limit anyway. + */ + keys_add_int(response_keys, name, tmp); + } else if (strcmp(name, "DefaultTime2Wait") == 0) { + keys_add(response_keys, name, value); + } else if (strcmp(name, "DefaultTime2Retain") == 0) { + keys_add(response_keys, name, "0"); + } else if (strcmp(name, "MaxOutstandingR2T") == 0) { + keys_add(response_keys, name, "1"); + } else if (strcmp(name, "DataPDUInOrder") == 0) { + keys_add(response_keys, name, "Yes"); + } else if (strcmp(name, "DataSequenceInOrder") == 0) { + keys_add(response_keys, name, "Yes"); + } else if (strcmp(name, "ErrorRecoveryLevel") == 0) { + keys_add(response_keys, name, "0"); + } else if (strcmp(name, "OFMarker") == 0) { + keys_add(response_keys, name, "No"); + } else if (strcmp(name, "IFMarker") == 0) { + keys_add(response_keys, name, "No"); + } else { + log_debugx("unknown key \"%s\"; responding " + "with NotUnderstood", name); + keys_add(response_keys, name, "NotUnderstood"); + } +} + +static void +login_negotiate(struct connection *conn, struct pdu *request) +{ + struct pdu *response; + struct iscsi_bhs_login_response *bhslr2; + struct keys *request_keys, *response_keys; + int i; + bool skipped_security; + + if (request == NULL) { + log_debugx("beginning parameter negotiation; " + "waiting for Login PDU"); + request = login_receive(conn, false); + skipped_security = false; + } else + skipped_security = true; + + request_keys = keys_new(); + keys_load(request_keys, request); + + response = login_new_response(request); + bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; + bhslr2->bhslr_flags |= BHSLR_FLAGS_TRANSIT; + bhslr2->bhslr_tsih = htons(0xbadd); + login_set_csg(response, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); + login_set_nsg(response, BHSLR_STAGE_FULL_FEATURE_PHASE); + response_keys = keys_new(); + for (i = 0; i < KEYS_MAX; i++) { + if (request_keys->keys_names[i] == NULL) + break; + + login_negotiate_key(request, request_keys->keys_names[i], + request_keys->keys_values[i], skipped_security, + response_keys); + } + + log_debugx("parameter negotiation done; " + "transitioning to Full Feature phase"); + + keys_save(response_keys, response); + pdu_send(response); + pdu_delete(response); + keys_delete(response_keys); + pdu_delete(request); + keys_delete(request_keys); +} + +void +login(struct connection *conn) +{ + struct pdu *request, *response; + struct iscsi_bhs_login_request *bhslr; + struct iscsi_bhs_login_response *bhslr2; + struct keys *request_keys, *response_keys; + struct auth_group *ag; + const char *initiator_name, *initiator_alias, *session_type, + *target_name, *auth_method; + char *portal_group_tag; + int rv; + + /* + * Handle the initial Login Request - figure out required authentication + * method and either transition to the next phase, if no authentication + * is required, or call appropriate authentication code. + */ + log_debugx("beginning Login phase; waiting for Login PDU"); + request = login_receive(conn, true); + bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; + if (bhslr->bhslr_tsih != 0) { + login_send_error(request, 0x02, 0x0a); + log_errx(1, "received Login PDU with non-zero TSIH"); + } + + /* + * XXX: Implement the C flag some day. + */ + request_keys = keys_new(); + keys_load(request_keys, request); + + assert(conn->conn_initiator_name == NULL); + initiator_name = keys_find(request_keys, "InitiatorName"); + if (initiator_name == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received Login PDU without InitiatorName"); + } + if (valid_iscsi_name(initiator_name) == false) { + login_send_error(request, 0x02, 0x00); + log_errx(1, "received Login PDU with invalid InitiatorName"); + } + conn->conn_initiator_name = checked_strdup(initiator_name); + log_set_peer_name(conn->conn_initiator_name); + /* + * XXX: This doesn't work (does nothing) because of Capsicum. + */ + setproctitle("%s (%s)", conn->conn_initiator_addr, conn->conn_initiator_name); + + initiator_alias = keys_find(request_keys, "InitiatorAlias"); + if (initiator_alias != NULL) + conn->conn_initiator_alias = checked_strdup(initiator_alias); + + assert(conn->conn_session_type == CONN_SESSION_TYPE_NONE); + session_type = keys_find(request_keys, "SessionType"); + if (session_type != NULL) { + if (strcmp(session_type, "Normal") == 0) { + conn->conn_session_type = CONN_SESSION_TYPE_NORMAL; + } else if (strcmp(session_type, "Discovery") == 0) { + conn->conn_session_type = CONN_SESSION_TYPE_DISCOVERY; + } else { + login_send_error(request, 0x02, 0x00); + log_errx(1, "received Login PDU with invalid " + "SessionType \"%s\"", session_type); + } + } else + conn->conn_session_type = CONN_SESSION_TYPE_NORMAL; + + assert(conn->conn_target == NULL); + if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { + target_name = keys_find(request_keys, "TargetName"); + if (target_name == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received Login PDU without TargetName"); + } + + conn->conn_target = + target_find(conn->conn_portal->p_portal_group->pg_conf, + target_name); + if (conn->conn_target == NULL) { + login_send_error(request, 0x02, 0x03); + log_errx(1, "requested target \"%s\" not found", + target_name); + } + } + + /* + * At this point we know what kind of authentication we need. + */ + if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { + ag = conn->conn_target->t_auth_group; + if (ag->ag_name != NULL) { + log_debugx("initiator requests to connect " + "to target \"%s\"; auth-group \"%s\"", + conn->conn_target->t_iqn, + conn->conn_target->t_auth_group->ag_name); + } else { + log_debugx("initiator requests to connect " + "to target \"%s\"", conn->conn_target->t_iqn); + } + } else { + assert(conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY); + ag = conn->conn_portal->p_portal_group->pg_discovery_auth_group; + if (ag->ag_name != NULL) { + log_debugx("initiator requests " + "discovery session; auth-group \"%s\"", ag->ag_name); + } else { + log_debugx("initiator requests discovery session"); + } + } + + /* + * Let's see if the initiator intends to do any kind of authentication + * at all. + */ + if (login_csg(request) == BHSLR_STAGE_OPERATIONAL_NEGOTIATION) { + if (ag->ag_type != AG_TYPE_NO_AUTHENTICATION) { + login_send_error(request, 0x02, 0x01); + log_errx(1, "initiator skipped the authentication " + "phase, but authentication is required"); + } + + keys_delete(request_keys); + + log_debugx("initiator skipped the authentication phase, " + "and we don't need it; proceeding with negotiation"); + login_negotiate(conn, request); + return; + } + + if (ag->ag_type == AG_TYPE_NO_AUTHENTICATION) { + /* + * Initiator might want to to authenticate, + * but we don't need it. + */ + log_debugx("authentication not required; " + "transitioning to Negotiation phase"); + + if ((bhslr->bhslr_flags & BHSLR_FLAGS_TRANSIT) == 0) + log_warnx("initiator did not set the \"T\" flag; " + "transitioning anyway"); + + response = login_new_response(request); + bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; + bhslr2->bhslr_flags |= BHSLR_FLAGS_TRANSIT; + login_set_nsg(response, + BHSLR_STAGE_OPERATIONAL_NEGOTIATION); + response_keys = keys_new(); + /* + * Required by Linux initiator. + */ + auth_method = keys_find(request_keys, "AuthMethod"); + if (auth_method != NULL && + login_list_contains(auth_method, "None")) + keys_add(response_keys, "AuthMethod", "None"); + + if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { + if (conn->conn_target->t_alias != NULL) + keys_add(response_keys, + "TargetAlias", conn->conn_target->t_alias); + rv = asprintf(&portal_group_tag, "%d", + conn->conn_portal->p_portal_group->pg_tag); + if (rv <= 0) + log_err(1, "asprintf"); + keys_add(response_keys, + "TargetPortalGroupTag", portal_group_tag); + free(portal_group_tag); + } + keys_save(response_keys, response); + pdu_send(response); + pdu_delete(response); + keys_delete(response_keys); + pdu_delete(request); + keys_delete(request_keys); + + login_negotiate(conn, NULL); + return; + } + + log_debugx("CHAP authentication required"); + + auth_method = keys_find(request_keys, "AuthMethod"); + if (auth_method == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received Login PDU without AuthMethod"); + } + /* + * XXX: This should be Reject, not just a login failure (5.3.2). + */ + if (login_list_contains(auth_method, "CHAP") == 0) { + login_send_error(request, 0x02, 0x01); + log_errx(1, "initiator requests unsupported AuthMethod \"%s\" " + "instead of \"CHAP\"", auth_method); + } + + response = login_new_response(request); + + response_keys = keys_new(); + keys_add(response_keys, "AuthMethod", "CHAP"); + if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { + rv = asprintf(&portal_group_tag, "%d", + conn->conn_portal->p_portal_group->pg_tag); + if (rv <= 0) + log_err(1, "asprintf"); + keys_add(response_keys, + "TargetPortalGroupTag", portal_group_tag); + free(portal_group_tag); + if (conn->conn_target->t_alias != NULL) + keys_add(response_keys, + "TargetAlias", conn->conn_target->t_alias); + } + keys_save(response_keys, response); + + pdu_send(response); + pdu_delete(response); + keys_delete(response_keys); + pdu_delete(request); + keys_delete(request_keys); + + login_chap(conn, ag); + + login_negotiate(conn, NULL); +} diff -urN p4/freebsd/src/usr.sbin/ctld/parse.y p4/iscsi/usr.sbin/ctld/parse.y --- p4/freebsd/src/usr.sbin/ctld/parse.y 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.sbin/ctld/parse.y 2013-07-24 12:25:00.000000000 +0200 @@ -0,0 +1,604 @@ +%{ +/*- + * 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 +#include +#include +#include +#include +#include +#include +#include + +#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 NUM +%token OPENING_BRACKET OPTION PATH PIDFILE PORTAL_GROUP SERIAL SIZE STR TARGET + +%union +{ + uint64_t num; + char *str; +} + +%token NUM +%token STR + +%% + +statements: + | + statements statement + ; + +statement: + debug_statement + | + pidfile_statement + | + auth_group_definition + | + portal_group_definition + | + target_statement + ; + +debug_statement: DEBUG NUM + { + conf->conf_debug = $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); +} diff -urN p4/freebsd/src/usr.sbin/ctld/pdu.c p4/iscsi/usr.sbin/ctld/pdu.c --- p4/freebsd/src/usr.sbin/ctld/pdu.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.sbin/ctld/pdu.c 2013-07-23 23:48:06.000000000 +0200 @@ -0,0 +1,241 @@ +/*- + * 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 +#include +#include +#include +#include +#include +#include + +#include "ctld.h" +#include "iscsi.h" + +#ifdef ICL_KERNEL_PROXY +#include +#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) + 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) + 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); +} diff -urN p4/freebsd/src/usr.sbin/ctld/token.l p4/iscsi/usr.sbin/ctld/token.l --- p4/freebsd/src/usr.sbin/ctld/token.l 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.sbin/ctld/token.l 2013-07-24 12:25:00.000000000 +0200 @@ -0,0 +1,83 @@ +%{ +/*- + * 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 +#include +#include + +#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; } +option { return OPTION; } +path { return PATH; } +pidfile { return PIDFILE; } +portal-group { return PORTAL_GROUP; } +serial { return SERIAL; } +size { return SIZE; } +target { return TARGET; } +[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 */; +%% diff -urN p4/freebsd/src/usr.sbin/iscsid/Makefile p4/iscsi/usr.sbin/iscsid/Makefile --- p4/freebsd/src/usr.sbin/iscsid/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.sbin/iscsid/Makefile 2013-08-19 12:30:04.000000000 +0200 @@ -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 diff -urN p4/freebsd/src/usr.sbin/iscsid/discovery.c p4/iscsi/usr.sbin/iscsid/discovery.c --- p4/freebsd/src/usr.sbin/iscsid/discovery.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.sbin/iscsid/discovery.c 2013-07-23 23:48:07.000000000 +0200 @@ -0,0 +1,226 @@ +/*- + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "iscsid.h" +#include "ctl/iscsi.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_desc, &conn->conn_desc, sizeof(isa)); + strlcpy(isa.isa_desc.isd_target, target, + sizeof(isa.isa_desc.isd_target)); + isa.isa_desc.isd_discovery = 0; + error = ioctl(conn->conn_iscsi_fd, ISCSISADD, &isa); + if (error != 0) + log_warn("failed to add %s: ISCSISADD", target); + + /* + * XXX: Perhaps we should have a mechanism to retry it? + */ +} + +static void +kernel_remove(const struct connection *conn) +{ + struct iscsi_session_remove isr; + int error; + + memset(&isr, 0, sizeof(isr)); + isr.isr_desc.isd_id = conn->conn_desc.isd_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("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("removing temporary discovery session"); + kernel_remove(conn); + + log_debugx("discovery session done"); +} diff -urN p4/freebsd/src/usr.sbin/iscsid/iscsid.8 p4/iscsi/usr.sbin/iscsid/iscsid.8 --- p4/freebsd/src/usr.sbin/iscsid/iscsid.8 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.sbin/iscsid/iscsid.8 2013-07-23 23:48:07.000000000 +0200 @@ -0,0 +1,96 @@ +.\" 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 d +.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 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 P Ar pidfile +Specify alternative location of a file where main process PID will be stored. +The default location is /var/run/iscsid.pid. +.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. diff -urN p4/freebsd/src/usr.sbin/iscsid/iscsid.c p4/iscsi/usr.sbin/iscsid/iscsid.c --- p4/freebsd/src/usr.sbin/iscsid/iscsid.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.sbin/iscsid/iscsid.c 2013-08-18 13:07:26.000000000 +0200 @@ -0,0 +1,449 @@ +/*- + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "iscsid.h" + +static void +usage(void) +{ + + fprintf(stderr, "usage: iscsid [-d][-P pidfile]\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(const struct iscsi_session_desc *desc, 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; + + /* + * XXX: Should we sanitize this somehow? + */ + memcpy(&conn->conn_desc, desc, sizeof(conn->conn_desc)); + + from_addr = conn->conn_desc.isd_initiator_addr; + to_addr = conn->conn_desc.isd_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_desc.isd_id; + if (conn->conn_desc.isd_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) + log_err(1, "failed to connect to %s using ICL kernel proxy", + to_addr); + +#else /* !ICL_KERNEL_PROXY */ + + if (conn->conn_desc.isd_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) + 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_desc.isd_id; +#ifndef ICL_KERNEL_PROXY + idh->idh_socket = conn->conn_socket; +#endif + strlcpy(idh->idh_target_alias, conn->conn_desc.isd_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"); +} + +/* + * XXX: I CANT INTO LATIN + */ +static void +capsicate(struct connection *conn) +{ + int error; +#ifdef ICL_KERNEL_PROXY + const unsigned long cmds[] = { ISCSIDCONNECT, ISCSIDSEND, ISCSIDRECEIVE, + ISCSIDHANDOFF, ISCSISADD, ISCSISREMOVE }; +#else + const unsigned long cmds[] = { ISCSIDHANDOFF, ISCSISADD, ISCSISREMOVE }; +#endif + + error = cap_rights_limit(conn->conn_iscsi_fd, CAP_IOCTL); + 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"); +} + +static void +handle_request(int iscsi_fd, struct iscsi_daemon_request *request) +{ + struct connection *conn; + + log_set_peer_addr(request->idr_desc.isd_target_addr); + if (request->idr_desc.isd_target[0] != '\0') { + log_set_peer_name(request->idr_desc.isd_target); + setproctitle("%s (%s)", request->idr_desc.isd_target_addr, request->idr_desc.isd_target); + } else { + setproctitle("%s", request->idr_desc.isd_target_addr); + } + +#if 0 + log_debugx("got request 0x%x, id %d, from %s (%s) to %s (%s), " + "auth %s %s %s %s, fd %d", request->idr_cmd, + request->idr_desc.isd_id, request->idr_desc.isd_initiator, + request->idr_desc.isd_initiator_addr, request->idr_desc.isd_target, + request->idr_desc.isd_target_addr, request->idr_desc.isd_user, + request->idr_desc.isd_secret, request->idr_desc.isd_mutual_user, + request->idr_desc.isd_mutual_secret, iscsi_fd); +#endif + + if (request->idr_cmd != ISCSI_DAEMON_CMD_LOGIN) + log_errx(1, "received unsupported cmd 0x%x", request->idr_cmd); + + conn = connection_new(&request->idr_desc, iscsi_fd); + capsicate(conn); + login(conn); + if (conn->conn_desc.isd_discovery != 0) + discovery(conn); + else + handoff(conn); + + log_debugx("nothing more to do; exiting"); + exit (0); +} + +static void +register_signals(void) +{ + struct sigaction sa; + int error; + + bzero(&sa, sizeof(sa)); + sigfillset(&sa.sa_mask); + sa.sa_handler = SIG_IGN; + error = sigaction(SIGCHLD, &sa, NULL); + if (error != 0) + log_err(1, "sigaction"); +} + +int +main(int argc, char **argv) +{ + int debug = 0, error, ch, iscsi_fd, retval, saved_errno; + 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, "dP:")) != -1) { + switch (ch) { + case 'd': + dont_daemonize = true; + debug++; + break; + case 'P': + pidfile_path = 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); + } + } + + register_signals(); + 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) + log_err(1, "ISCSIDWAIT"); + + log_debugx("got request from kernel; cmd = 0x%x", + request->idr_cmd); + + if (dont_daemonize) { + log_debugx("not forking due to -d flag; " + "will exit after servicing a single request"); + } else { + pid = fork(); + if (pid < 0) + log_err(1, "fork"); + if (pid > 0) + continue; + } + + pidfile_close(pidfh); + handle_request(iscsi_fd, request); + } + + return (0); +} diff -urN p4/freebsd/src/usr.sbin/iscsid/iscsid.h p4/iscsi/usr.sbin/iscsid/iscsid.h --- p4/freebsd/src/usr.sbin/iscsid/iscsid.h 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.sbin/iscsid/iscsid.h 2013-08-18 13:07:26.000000000 +0200 @@ -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 ISCSID_H +#define ISCSID_H + +#include +#include + +#include + +#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 + struct iscsi_session_desc conn_desc; + 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 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); + +void login(struct connection *ic); + +void discovery(struct connection *ic); + +char *checked_strdup(const char *); + +#endif /* !ISCSID_H */ diff -urN p4/freebsd/src/usr.sbin/iscsid/keys.c p4/iscsi/usr.sbin/iscsid/keys.c --- p4/freebsd/src/usr.sbin/iscsid/keys.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.sbin/iscsid/keys.c 2013-07-23 23:48:07.000000000 +0200 @@ -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 +#include +#include +#include +#include + +#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); +} diff -urN p4/freebsd/src/usr.sbin/iscsid/log.c p4/iscsi/usr.sbin/iscsid/log.c --- p4/freebsd/src/usr.sbin/iscsid/log.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.sbin/iscsid/log.c 2013-07-23 23:48:07.000000000 +0200 @@ -0,0 +1,181 @@ +/*- + * 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 +#include +#include +#include +#include +#include + +#include "iscsid.h" + +static int log_level = 0; +static char *peer_name = NULL; +static char *peer_addr = NULL; + +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) +{ + int ret; + char *msg; + + ret = vasprintf(&msg, fmt, ap); + if (ret == -1) { + /* + * XXX: vasprintf(3) doesn't set errno, right? + */ + fprintf(stderr, "%s: %s: %s\n", + getprogname(), __func__, strerror(ENOMEM)); + syslog(LOG_CRIT, "%s: %s", __func__, strerror(ENOMEM)); + exit(1); + } + + if (log_errno == -1) { + if (peer_name != NULL) { + fprintf(stderr, "%s: %s (%s): %s\n", getprogname(), peer_addr, peer_name, msg); + syslog(priority, "%s (%s): %s", peer_addr, peer_name, msg); + } else if (peer_addr != NULL) { + fprintf(stderr, "%s: %s: %s\n", getprogname(), peer_addr, msg); + syslog(priority, "%s: %s", peer_addr, msg); + } else { + fprintf(stderr, "%s: %s\n", getprogname(), msg); + syslog(priority, "%s", msg); + } + + } else { + if (peer_name != NULL) { + fprintf(stderr, "%s: %s (%s): %s: %s\n", getprogname(), peer_addr, peer_name, msg, strerror(errno)); + syslog(priority, "%s (%s): %s: %s", peer_addr, peer_name, msg, strerror(errno)); + } else if (peer_addr != NULL) { + fprintf(stderr, "%s: %s: %s: %s\n", getprogname(), peer_addr, msg, strerror(errno)); + syslog(priority, "%s: %s: %s", peer_addr, msg, strerror(errno)); + } else { + fprintf(stderr, "%s: %s: %s\n", getprogname(), msg, strerror(errno)); + syslog(priority, "%s: %s", msg, strerror(errno)); + } + } + + free(msg); +} + +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); +} diff -urN p4/freebsd/src/usr.sbin/iscsid/login.c p4/iscsi/usr.sbin/iscsid/login.c --- p4/freebsd/src/usr.sbin/iscsid/login.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.sbin/iscsid/login.c 2013-08-18 13:07:26.000000000 +0200 @@ -0,0 +1,779 @@ +/*- + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iscsid.h" +#include "ctl/iscsi.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 struct pdu * +login_receive(struct connection *conn, bool initial) +{ + struct pdu *response; + struct iscsi_bhs_login_response *bhslr; + + 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) + log_errx(1, "target returned error: status class 0x%x, " + "detail 0x%x", bhslr->bhslr_status_class, + bhslr->bhslr_status_detail); +#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) { + log_errx(1, "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_desc.isd_target_alias, value, + sizeof(conn->conn_desc.isd_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_desc.isd_discovery == 0) { + if (conn->conn_desc.isd_header_digest != 0) + keys_add(request_keys, "HeaderDigest", "CRC32C"); + if (conn->conn_desc.isd_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_desc.isd_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_desc.isd_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_desc.isd_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_desc.isd_mutual_user) != 0) + log_errx(1, "mutual CHAP authentication failed: wrong user"); + + login_compute_md5(conn->conn_mutual_id, + conn->conn_desc.isd_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) + 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_desc.isd_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_desc.isd_user[0] == '\0') + keys_add(request_keys, "AuthMethod", "None"); + else + keys_add(request_keys, "AuthMethod", "CHAP,None"); + keys_add(request_keys, "InitiatorName", + conn->conn_desc.isd_initiator); + if (conn->conn_desc.isd_initiator_alias[0] != '\0') { + keys_add(request_keys, "InitiatorAlias", + conn->conn_desc.isd_initiator_alias); + } + if (conn->conn_desc.isd_discovery == 0) { + keys_add(request_keys, "SessionType", "Normal"); + keys_add(request_keys, + "TargetName", conn->conn_desc.isd_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) + log_errx(1, "received response " + "with unsupported AuthMethod \"%s\"", auth_method); + + + if (conn->conn_desc.isd_user[0] == '\0' || + conn->conn_desc.isd_secret[0] == '\0') + 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); +} diff -urN p4/freebsd/src/usr.sbin/iscsid/pdu.c p4/iscsi/usr.sbin/iscsid/pdu.c --- p4/freebsd/src/usr.sbin/iscsid/pdu.c 1970-01-01 01:00:00.000000000 +0100 +++ p4/iscsi/usr.sbin/iscsid/pdu.c 2013-07-23 23:48:07.000000000 +0200 @@ -0,0 +1,276 @@ +/*- + * 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 +#include +#include +#include +#include +#include +#include + +#include "iscsid.h" +#include "ctl/iscsi.h" + +#ifdef ICL_KERNEL_PROXY +#include +#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_desc.isd_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_desc.isd_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) + 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) + 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); +}