diff --git a/lib/Makefile b/lib/Makefile index bd28b974c673..43c345daf356 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,241 +1,242 @@ # @(#)Makefile 8.1 (Berkeley) 6/4/93 # $FreeBSD$ .include # The SUBDIR_BOOTSTRAP list is a small set of libraries which are used by many # of the other libraries. These are built first with a .WAIT between them # and the main list to avoid needing a SUBDIR_DEPEND line on every library # naming just these few items. SUBDIR_BOOTSTRAP= \ csu \ .WAIT \ libc \ libc_nonshared \ libcompiler_rt \ ${_libclang_rt} \ ${_libcplusplus} \ ${_libcxxrt} \ libelf \ libssp \ libssp_nonshared \ msun # The main list; please keep these sorted alphabetically. # The only exception is sqlite3: we place it at the start of the list since it # takes a long time to build and starting it first improves parallelism. SUBDIR= ${SUBDIR_BOOTSTRAP} \ .WAIT \ libsqlite3 \ geom \ lib9p \ libalias \ libarchive \ libauditd \ libbegemot \ libblocksruntime \ libbsddialog \ libbsdstat \ libbsm \ libbz2 \ libcalendar \ libcam \ libcapsicum \ libcasper \ libcompat \ libcrypt \ libdevctl \ libdevinfo \ libdevstat \ libdl \ libdwarf \ libedit \ libelftc \ libevent1 \ libexecinfo \ libexpat \ libfetch \ libfigpar \ libgcc_eh \ libgcc_s \ libgeom \ libifconfig \ libipsec \ + libiscsiutil \ libjail \ libkiconv \ libkvm \ liblua \ liblzma \ libmemstat \ libmd \ libmixer \ libmt \ lib80211 \ libnetbsd \ libnetmap \ libnv \ libopenbsd \ libopie \ libpam \ libpathconv \ libpcap \ libpjdlog \ libproc \ libprocstat \ libregex \ librpcsvc \ librss \ librt \ librtld_db \ libsbuf \ libsmb \ libstdbuf \ libstdthreads \ libsysdecode \ libtacplus \ libthr \ libthread_db \ libucl \ libufs \ libugidfw \ libulog \ libutil \ ${_libvgl} \ libwrap \ libxo \ liby \ libz \ libzstd \ ncurses # Inter-library dependencies. When the makefile for a library contains LDADD # libraries, those libraries should be listed as build order dependencies here. SUBDIR_DEPEND_geom= libufs SUBDIR_DEPEND_googletest= libregex SUBDIR_DEPEND_libarchive= libz libbz2 libexpat liblzma libmd libzstd SUBDIR_DEPEND_libauditdm= libbsm SUBDIR_DEPEND_libbsddialog= ncurses SUBDIR_DEPEND_libbsnmp= ${_libnetgraph} SUBDIR_DEPEND_libc++:= libcxxrt # libssp_nonshared doesn't need to be linked into libc on every arch, but it is # small enough to build that this bit of serialization is likely insignificant. SUBDIR_DEPEND_libc= libcompiler_rt libssp_nonshared SUBDIR_DEPEND_libcam= libsbuf SUBDIR_DEPEND_libcasper= libnv SUBDIR_DEPEND_libdevstat= libkvm SUBDIR_DEPEND_libdpv= libfigpar ncurses libutil SUBDIR_DEPEND_libedit= ncurses SUBDIR_DEPEND_libgeom= libexpat libsbuf SUBDIR_DEPEND_librpcsec_gss= libgssapi SUBDIR_DEPEND_libmagic= libz SUBDIR_DEPEND_libmemstat= libkvm SUBDIR_DEPEND_libopie= libmd SUBDIR_DEPEND_libpam= libcrypt libopie ${_libradius} librpcsvc libtacplus libutil ${_libypclnt} ${_libcom_err} SUBDIR_DEPEND_libpjdlog= libutil SUBDIR_DEPEND_libprocstat= libkvm libutil SUBDIR_DEPEND_libradius= libmd SUBDIR_DEPEND_libsmb= libkiconv SUBDIR_DEPEND_libtacplus= libmd SUBDIR_DEPEND_libulog= libmd SUBDIR_DEPEND_libunbound= ${_libldns} SUBDIR_DEPEND_liblzma= libthr .if ${MK_OFED} != "no" SUBDIR_DEPEND_libpcap= ofed .endif .if !defined(COMPAT_32BIT) SUBDIR+= flua SUBDIR_DEPEND_flua= libjail .endif # NB: keep these sorted by MK_* knobs SUBDIR.${MK_ATM}+= libngatm SUBDIR.${MK_BEARSSL}+= libbearssl libsecureboot SUBDIR.${MK_BLACKLIST}+=libblacklist SUBDIR.${MK_BLUETOOTH}+=libbluetooth libsdp SUBDIR.${MK_BSNMP}+= libbsnmp .if !defined(COMPAT_32BIT) && !defined(COMPAT_SOFTFP) .if ${MK_CLANG} != "no" || ${MK_LLD} != "no" || \ ${MK_LLDB} != "no" || ${MK_LLVM_BINUTILS} != "no" SUBDIR+= clang .endif .endif SUBDIR.${MK_CUSE}+= libcuse SUBDIR.${MK_CXX}+= libdevdctl SUBDIR.${MK_TOOLCHAIN}+=libpe SUBDIR.${MK_DIALOG}+= libdpv SUBDIR.${MK_FILE}+= libmagic SUBDIR.${MK_GPIO}+= libgpio SUBDIR.${MK_GSSAPI}+= libgssapi librpcsec_gss SUBDIR.${MK_ICONV}+= libiconv_modules SUBDIR.${MK_KERBEROS_SUPPORT}+= libcom_err SUBDIR.${MK_LDNS}+= libldns SUBDIR.${MK_STATS}+= libstats # The libraries under libclang_rt can only be built by clang and when we enable # C++ support. Furthermore, they can only be built for certain architectures. .if ${COMPILER_TYPE} == "clang" && ${MK_CXX} != "no" && \ (${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" || \ ${MACHINE_CPUARCH} == "arm" || ${MACHINE_CPUARCH} == "i386" || \ ${MACHINE_CPUARCH} == "powerpc") _libclang_rt= libclang_rt .elif (${MK_ASAN} != "no" || ${MK_UBSAN} != "no") && make(all) .error "Requested build with sanitizers but cannot build runtime libraries!" .endif .if ${MK_CXX} != "no" _libcxxrt= libcxxrt _libcplusplus= libc++ _libcplusplus+= libc++experimental .endif SUBDIR.${MK_EFI}+= libefivar SUBDIR.${MK_GOOGLETEST}+= googletest SUBDIR.${MK_NETGRAPH}+= libnetgraph SUBDIR.${MK_NIS}+= libypclnt .if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64" _libvgl= libvgl .endif .if ${MACHINE_CPUARCH} == "aarch64" SUBDIR.${MK_PMC}+= libopencsd .endif .if ${MACHINE_CPUARCH} == "amd64" SUBDIR.${MK_PMC}+= libipt SUBDIR.${MK_BHYVE}+= libvmmapi .endif .if ${MACHINE_ARCH} != "powerpc" SUBDIR.${MK_OPENMP}+= libomp .endif .if !defined(COMPAT_32BIT) && ${MK_USB} != "no" SUBDIR.${MK_OPENSSH}+= libcbor libfido2 .endif SUBDIR.${MK_OPENSSL}+= libmp SUBDIR.${MK_PF}+= libpfctl SUBDIR.${MK_PMC}+= libpmc libpmcstat SUBDIR.${MK_RADIUS_SUPPORT}+= libradius SUBDIR.${MK_SENDMAIL}+= libmilter libsm libsmdb libsmutil SUBDIR.${MK_TELNET}+= libtelnet SUBDIR.${MK_TESTS_SUPPORT}+= atf SUBDIR.${MK_TESTS_SUPPORT}.${MK_CXX}+= liblutok SUBDIR.${MK_TESTS}+= tests SUBDIR.${MK_UNBOUND}+= libunbound SUBDIR.${MK_USB}+= libusbhid libusb SUBDIR.${MK_OFED}+= ofed SUBDIR.${MK_VERIEXEC}+= libveriexec SUBDIR.${MK_ZFS}+= libbe .if !make(install) SUBDIR_PARALLEL= .endif .include diff --git a/lib/libiscsiutil/Makefile b/lib/libiscsiutil/Makefile new file mode 100644 index 000000000000..9ec625970eae --- /dev/null +++ b/lib/libiscsiutil/Makefile @@ -0,0 +1,10 @@ +LIB= iscsiutil +INTERNALLIB= +PACKAGE= iscsi + +INCS= libiscsiutil.h + +SRCS= chap.c connection.c keys.c log.c pdu.c utils.c +CFLAGS+= -I${SRCTOP}/sys/dev/iscsi + +.include diff --git a/usr.sbin/ctld/chap.c b/lib/libiscsiutil/chap.c similarity index 99% rename from usr.sbin/ctld/chap.c rename to lib/libiscsiutil/chap.c index 7b57b7080c97..b33fef220106 100644 --- a/usr.sbin/ctld/chap.c +++ b/lib/libiscsiutil/chap.c @@ -1,423 +1,419 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2014 The FreeBSD Foundation * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #include #include -#include "ctld.h" +#include "libiscsiutil.h" static void chap_compute_md5(const char id, const char *secret, const void *challenge, size_t challenge_len, void *response, size_t response_len) { MD5_CTX ctx; assert(response_len == CHAP_DIGEST_LEN); MD5Init(&ctx); MD5Update(&ctx, &id, sizeof(id)); MD5Update(&ctx, secret, strlen(secret)); MD5Update(&ctx, challenge, challenge_len); MD5Final(response, &ctx); } static int chap_hex2int(const char hex) { switch (hex) { case '0': return (0x00); case '1': return (0x01); case '2': return (0x02); case '3': return (0x03); case '4': return (0x04); case '5': return (0x05); case '6': return (0x06); case '7': return (0x07); case '8': return (0x08); case '9': return (0x09); case 'a': case 'A': return (0x0a); case 'b': case 'B': return (0x0b); case 'c': case 'C': return (0x0c); case 'd': case 'D': return (0x0d); case 'e': case 'E': return (0x0e); case 'f': case 'F': return (0x0f); default: return (-1); } } static int chap_b642bin(const char *b64, void **binp, size_t *bin_lenp) { char *bin; int b64_len, bin_len; b64_len = strlen(b64); bin_len = (b64_len + 3) / 4 * 3; bin = calloc(bin_len, 1); if (bin == NULL) log_err(1, "calloc"); bin_len = b64_pton(b64, bin, bin_len); if (bin_len < 0) { log_warnx("malformed base64 variable"); free(bin); return (-1); } *binp = bin; *bin_lenp = bin_len; return (0); } /* * XXX: Review this _carefully_. */ static int chap_hex2bin(const char *hex, void **binp, size_t *bin_lenp) { int i, hex_len, nibble; bool lo = true; /* As opposed to 'hi'. */ char *bin; size_t bin_off, bin_len; if (strncasecmp(hex, "0b", strlen("0b")) == 0) return (chap_b642bin(hex + 2, binp, bin_lenp)); if (strncasecmp(hex, "0x", strlen("0x")) != 0) { log_warnx("malformed variable, should start with \"0x\"" " or \"0b\""); return (-1); } hex += strlen("0x"); hex_len = strlen(hex); if (hex_len < 1) { log_warnx("malformed variable; doesn't contain anything " "but \"0x\""); return (-1); } bin_len = hex_len / 2 + hex_len % 2; bin = calloc(bin_len, 1); if (bin == NULL) log_err(1, "calloc"); bin_off = bin_len - 1; for (i = hex_len - 1; i >= 0; i--) { nibble = chap_hex2int(hex[i]); if (nibble < 0) { log_warnx("malformed variable, invalid char \"%c\"", hex[i]); free(bin); return (-1); } assert(bin_off < bin_len); if (lo) { bin[bin_off] = nibble; lo = false; } else { bin[bin_off] |= nibble << 4; bin_off--; lo = true; } } *binp = bin; *bin_lenp = bin_len; return (0); } #ifdef USE_BASE64 static char * chap_bin2hex(const char *bin, size_t bin_len) { unsigned char *b64, *tmp; size_t b64_len; b64_len = (bin_len + 2) / 3 * 4 + 3; /* +2 for "0b", +1 for '\0'. */ b64 = malloc(b64_len); if (b64 == NULL) log_err(1, "malloc"); tmp = b64; tmp += sprintf(tmp, "0b"); b64_ntop(bin, bin_len, tmp, b64_len - 2); return (b64); } #else static char * chap_bin2hex(const char *bin, size_t bin_len) { unsigned char *hex, *tmp, ch; size_t hex_len; size_t i; hex_len = bin_len * 2 + 3; /* +2 for "0x", +1 for '\0'. */ hex = malloc(hex_len); if (hex == NULL) log_err(1, "malloc"); tmp = hex; tmp += sprintf(tmp, "0x"); for (i = 0; i < bin_len; i++) { ch = bin[i]; tmp += sprintf(tmp, "%02x", ch); } return (hex); } #endif /* !USE_BASE64 */ struct chap * chap_new(void) { struct chap *chap; chap = calloc(1, sizeof(*chap)); if (chap == NULL) log_err(1, "calloc"); /* * Generate the challenge. */ arc4random_buf(chap->chap_challenge, sizeof(chap->chap_challenge)); arc4random_buf(&chap->chap_id, sizeof(chap->chap_id)); return (chap); } char * chap_get_id(const struct chap *chap) { char *chap_i; int ret; ret = asprintf(&chap_i, "%d", chap->chap_id); if (ret < 0) log_err(1, "asprintf"); return (chap_i); } char * chap_get_challenge(const struct chap *chap) { char *chap_c; chap_c = chap_bin2hex(chap->chap_challenge, sizeof(chap->chap_challenge)); return (chap_c); } static int chap_receive_bin(struct chap *chap, void *response, size_t response_len) { if (response_len != sizeof(chap->chap_response)) { log_debugx("got CHAP response with invalid length; " "got %zd, should be %zd", response_len, sizeof(chap->chap_response)); return (1); } memcpy(chap->chap_response, response, response_len); return (0); } int chap_receive(struct chap *chap, const char *response) { void *response_bin; size_t response_bin_len; int error; error = chap_hex2bin(response, &response_bin, &response_bin_len); if (error != 0) { log_debugx("got incorrectly encoded CHAP response \"%s\"", response); return (1); } error = chap_receive_bin(chap, response_bin, response_bin_len); free(response_bin); return (error); } int chap_authenticate(struct chap *chap, const char *secret) { char expected_response[CHAP_DIGEST_LEN]; chap_compute_md5(chap->chap_id, secret, chap->chap_challenge, sizeof(chap->chap_challenge), expected_response, sizeof(expected_response)); if (memcmp(chap->chap_response, expected_response, sizeof(expected_response)) != 0) { return (-1); } return (0); } void chap_delete(struct chap *chap) { free(chap); } struct rchap * rchap_new(const char *secret) { struct rchap *rchap; rchap = calloc(1, sizeof(*rchap)); if (rchap == NULL) log_err(1, "calloc"); rchap->rchap_secret = checked_strdup(secret); return (rchap); } static void rchap_receive_bin(struct rchap *rchap, const unsigned char id, const void *challenge, size_t challenge_len) { rchap->rchap_id = id; rchap->rchap_challenge = calloc(challenge_len, 1); if (rchap->rchap_challenge == NULL) log_err(1, "calloc"); memcpy(rchap->rchap_challenge, challenge, challenge_len); rchap->rchap_challenge_len = challenge_len; } int rchap_receive(struct rchap *rchap, const char *id, const char *challenge) { unsigned char id_bin; void *challenge_bin; size_t challenge_bin_len; int error; id_bin = strtoul(id, NULL, 10); error = chap_hex2bin(challenge, &challenge_bin, &challenge_bin_len); if (error != 0) { log_debugx("got incorrectly encoded CHAP challenge \"%s\"", challenge); return (1); } rchap_receive_bin(rchap, id_bin, challenge_bin, challenge_bin_len); free(challenge_bin); return (0); } static void rchap_get_response_bin(struct rchap *rchap, void **responsep, size_t *response_lenp) { void *response_bin; size_t response_bin_len = CHAP_DIGEST_LEN; response_bin = calloc(response_bin_len, 1); if (response_bin == NULL) log_err(1, "calloc"); chap_compute_md5(rchap->rchap_id, rchap->rchap_secret, rchap->rchap_challenge, rchap->rchap_challenge_len, response_bin, response_bin_len); *responsep = response_bin; *response_lenp = response_bin_len; } char * rchap_get_response(struct rchap *rchap) { void *response; size_t response_len; char *chap_r; rchap_get_response_bin(rchap, &response, &response_len); chap_r = chap_bin2hex(response, response_len); free(response); return (chap_r); } void rchap_delete(struct rchap *rchap) { free(rchap->rchap_secret); free(rchap->rchap_challenge); free(rchap); } diff --git a/lib/libiscsiutil/connection.c b/lib/libiscsiutil/connection.c new file mode 100644 index 000000000000..7dc50574644c --- /dev/null +++ b/lib/libiscsiutil/connection.c @@ -0,0 +1,53 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2012 The FreeBSD Foundation + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include "libiscsiutil.h" + +void +connection_init(struct connection *conn, const struct connection_ops *ops, + bool use_proxy) +{ + memset(conn, 0, sizeof(*conn)); + conn->conn_ops = ops; + conn->conn_use_proxy = use_proxy; + + /* + * Default values, from RFC 3720, section 12. + */ + conn->conn_header_digest = CONN_DIGEST_NONE; + conn->conn_data_digest = CONN_DIGEST_NONE; + conn->conn_immediate_data = true; + conn->conn_max_recv_data_segment_length = 8192; + conn->conn_max_send_data_segment_length = 8192; + conn->conn_max_burst_length = 262144; + conn->conn_first_burst_length = 65536; +} diff --git a/usr.sbin/ctld/keys.c b/lib/libiscsiutil/keys.c similarity index 98% rename from usr.sbin/ctld/keys.c rename to lib/libiscsiutil/keys.c index f28e333bcd81..8011b0a25329 100644 --- a/usr.sbin/ctld/keys.c +++ b/lib/libiscsiutil/keys.c @@ -1,199 +1,195 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 The FreeBSD Foundation * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include -#include "ctld.h" +#include "libiscsiutil.h" struct keys * keys_new(void) { struct keys *keys; keys = calloc(1, sizeof(*keys)); if (keys == NULL) log_err(1, "calloc"); return (keys); } void keys_delete(struct keys *keys) { free(keys->keys_data); free(keys); } void keys_load(struct keys *keys, const struct pdu *pdu) { int i; char *pair; size_t pair_len; if (pdu->pdu_data_len == 0) return; if (pdu->pdu_data[pdu->pdu_data_len - 1] != '\0') log_errx(1, "protocol error: key not NULL-terminated\n"); assert(keys->keys_data == NULL); keys->keys_data_len = pdu->pdu_data_len; keys->keys_data = malloc(keys->keys_data_len); if (keys->keys_data == NULL) log_err(1, "malloc"); memcpy(keys->keys_data, pdu->pdu_data, keys->keys_data_len); /* * XXX: Review this carefully. */ pair = keys->keys_data; for (i = 0;; i++) { if (i >= KEYS_MAX) log_errx(1, "too many keys received"); pair_len = strlen(pair); keys->keys_values[i] = pair; keys->keys_names[i] = strsep(&keys->keys_values[i], "="); if (keys->keys_names[i] == NULL || keys->keys_values[i] == NULL) log_errx(1, "malformed keys"); log_debugx("key received: \"%s=%s\"", keys->keys_names[i], keys->keys_values[i]); pair += pair_len + 1; /* +1 to skip the terminating '\0'. */ if (pair == keys->keys_data + keys->keys_data_len) break; assert(pair < keys->keys_data + keys->keys_data_len); } } void keys_save(struct keys *keys, struct pdu *pdu) { char *data; size_t len; int i; /* * XXX: Not particularly efficient. */ len = 0; for (i = 0; i < KEYS_MAX; i++) { if (keys->keys_names[i] == NULL) break; /* * +1 for '=', +1 for '\0'. */ len += strlen(keys->keys_names[i]) + strlen(keys->keys_values[i]) + 2; } if (len == 0) return; data = malloc(len); if (data == NULL) log_err(1, "malloc"); pdu->pdu_data = data; pdu->pdu_data_len = len; for (i = 0; i < KEYS_MAX; i++) { if (keys->keys_names[i] == NULL) break; data += sprintf(data, "%s=%s", keys->keys_names[i], keys->keys_values[i]); data += 1; /* for '\0'. */ } } const char * keys_find(struct keys *keys, const char *name) { int i; /* * Note that we don't handle duplicated key names here, * as they are not supposed to happen in requests, and if they do, * it's an initiator error. */ for (i = 0; i < KEYS_MAX; i++) { if (keys->keys_names[i] == NULL) return (NULL); if (strcmp(keys->keys_names[i], name) == 0) return (keys->keys_values[i]); } return (NULL); } void keys_add(struct keys *keys, const char *name, const char *value) { int i; log_debugx("key to send: \"%s=%s\"", name, value); /* * Note that we don't check for duplicates here, as they are perfectly * fine in responses, e.g. the "TargetName" keys in discovery sesion * response. */ for (i = 0; i < KEYS_MAX; i++) { if (keys->keys_names[i] == NULL) { keys->keys_names[i] = checked_strdup(name); keys->keys_values[i] = checked_strdup(value); return; } } log_errx(1, "too many keys"); } void keys_add_int(struct keys *keys, const char *name, int value) { char *str; int ret; ret = asprintf(&str, "%d", value); if (ret <= 0) log_err(1, "asprintf"); keys_add(keys, name, str); free(str); } diff --git a/usr.sbin/iscsid/iscsid.h b/lib/libiscsiutil/libiscsiutil.h similarity index 75% copy from usr.sbin/iscsid/iscsid.h copy to lib/libiscsiutil/libiscsiutil.h index b67df4a8197f..79c79872b2e6 100644 --- a/usr.sbin/iscsid/iscsid.h +++ b/lib/libiscsiutil/libiscsiutil.h @@ -1,152 +1,148 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 The FreeBSD Foundation * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ -#ifndef ISCSID_H -#define ISCSID_H +#ifndef __LIBISCSIUTIL_H__ +#define __LIBISCSIUTIL_H__ +#include #include -#include - -#include -#define DEFAULT_PIDFILE "/var/run/iscsid.pid" +struct connection_ops; #define CONN_DIGEST_NONE 0 #define CONN_DIGEST_CRC32C 1 -#define CONN_MUTUAL_CHALLENGE_LEN 1024 -#define SOCKBUF_SIZE 1048576 - struct connection { - int conn_iscsi_fd; - int conn_socket; - unsigned int conn_session_id; - struct iscsi_session_conf conn_conf; - struct iscsi_session_limits conn_limits; - char conn_target_alias[ISCSI_ADDR_LEN]; - uint8_t conn_isid[6]; - uint16_t conn_tsih; - uint32_t conn_statsn; - int conn_protocol_level; - int conn_header_digest; - int conn_data_digest; - bool conn_initial_r2t; - bool conn_immediate_data; - int conn_max_recv_data_segment_length; - int conn_max_send_data_segment_length; - int conn_max_burst_length; - int conn_first_burst_length; - struct chap *conn_mutual_chap; + const struct connection_ops *conn_ops; + int conn_socket; + uint8_t conn_isid[6]; + uint16_t conn_tsih; + uint32_t conn_cmdsn; + uint32_t conn_statsn; + int conn_header_digest; + int conn_data_digest; + bool conn_immediate_data; + bool conn_use_proxy; + int conn_max_recv_data_segment_length; + int conn_max_send_data_segment_length; + int conn_max_burst_length; + int conn_first_burst_length; }; struct pdu { - struct connection *pdu_connection; - struct iscsi_bhs *pdu_bhs; - char *pdu_data; - size_t pdu_data_len; + struct connection *pdu_connection; + struct iscsi_bhs *pdu_bhs; + char *pdu_data; + size_t pdu_data_len; +}; + +struct connection_ops { + bool (*timed_out)(void); + void (*pdu_receive_proxy)(struct pdu *); + void (*pdu_send_proxy)(struct pdu *); + void (*fail)(const struct connection *, const char *); }; #define KEYS_MAX 1024 struct keys { - char *keys_names[KEYS_MAX]; - char *keys_values[KEYS_MAX]; - char *keys_data; - size_t keys_data_len; + char *keys_names[KEYS_MAX]; + char *keys_values[KEYS_MAX]; + char *keys_data; + size_t keys_data_len; }; #define CHAP_CHALLENGE_LEN 1024 #define CHAP_DIGEST_LEN 16 /* Equal to MD5 digest size. */ struct chap { unsigned char chap_id; char chap_challenge[CHAP_CHALLENGE_LEN]; char chap_response[CHAP_DIGEST_LEN]; }; struct rchap { char *rchap_secret; unsigned char rchap_id; void *rchap_challenge; size_t rchap_challenge_len; }; struct chap *chap_new(void); char *chap_get_id(const struct chap *chap); char *chap_get_challenge(const struct chap *chap); int chap_receive(struct chap *chap, const char *response); int chap_authenticate(struct chap *chap, const char *secret); void chap_delete(struct chap *chap); struct rchap *rchap_new(const char *secret); int rchap_receive(struct rchap *rchap, const char *id, const char *challenge); char *rchap_get_response(struct rchap *rchap); void rchap_delete(struct rchap *rchap); struct keys *keys_new(void); void keys_delete(struct keys *key); void keys_load(struct keys *keys, const struct pdu *pdu); void keys_save(struct keys *keys, struct pdu *pdu); const char *keys_find(struct keys *keys, const char *name); void keys_add(struct keys *keys, const char *name, const char *value); void keys_add_int(struct keys *keys, const char *name, int value); struct pdu *pdu_new(struct connection *ic); struct pdu *pdu_new_response(struct pdu *request); +int pdu_ahs_length(const struct pdu *pdu); +int pdu_data_segment_length(const struct pdu *pdu); +void pdu_set_data_segment_length(struct pdu *pdu, + uint32_t len); void pdu_receive(struct pdu *request); void pdu_send(struct pdu *response); void pdu_delete(struct pdu *ip); -void login(struct connection *ic); - -void discovery(struct connection *ic); +void connection_init(struct connection *conn, + const struct connection_ops *ops, bool use_proxy); void log_init(int level); void log_set_peer_name(const char *name); void log_set_peer_addr(const char *addr); void log_err(int, const char *, ...) __dead2 __printflike(2, 3); void log_errx(int, const char *, ...) __dead2 __printflike(2, 3); void log_warn(const char *, ...) __printflike(1, 2); void log_warnx(const char *, ...) __printflike(1, 2); void log_debugx(const char *, ...) __printflike(1, 2); char *checked_strdup(const char *); -bool timed_out(void); -void fail(const struct connection *, const char *); -#endif /* !ISCSID_H */ +#endif /* !__LIBISCSIUTIL_H__ */ diff --git a/usr.sbin/ctld/log.c b/lib/libiscsiutil/log.c similarity index 98% rename from usr.sbin/ctld/log.c rename to lib/libiscsiutil/log.c index 94e014293a61..7d7a13bb11f2 100644 --- a/usr.sbin/ctld/log.c +++ b/lib/libiscsiutil/log.c @@ -1,202 +1,198 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 The FreeBSD Foundation * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #include #include #include -#include "ctld.h" +#include "libiscsiutil.h" static int log_level = 0; static char *peer_name = NULL; static char *peer_addr = NULL; #define MSGBUF_LEN 1024 void log_init(int level) { log_level = level; openlog(getprogname(), LOG_NDELAY | LOG_PID, LOG_DAEMON); } void log_set_peer_name(const char *name) { /* * XXX: Turn it into assertion? */ if (peer_name != NULL) log_errx(1, "%s called twice", __func__); if (peer_addr == NULL) log_errx(1, "%s called before log_set_peer_addr", __func__); peer_name = checked_strdup(name); } void log_set_peer_addr(const char *addr) { /* * XXX: Turn it into assertion? */ if (peer_addr != NULL) log_errx(1, "%s called twice", __func__); peer_addr = checked_strdup(addr); } static void log_common(int priority, int log_errno, const char *fmt, va_list ap) { static char msgbuf[MSGBUF_LEN]; static char msgbuf_strvised[MSGBUF_LEN * 4 + 1]; char *errstr; int ret; ret = vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); if (ret < 0) { fprintf(stderr, "%s: snprintf failed", getprogname()); syslog(LOG_CRIT, "snprintf failed"); exit(1); } ret = strnvis(msgbuf_strvised, sizeof(msgbuf_strvised), msgbuf, VIS_NL); if (ret < 0) { fprintf(stderr, "%s: strnvis failed", getprogname()); syslog(LOG_CRIT, "strnvis failed"); exit(1); } if (log_errno == -1) { if (peer_name != NULL) { fprintf(stderr, "%s: %s (%s): %s\n", getprogname(), peer_addr, peer_name, msgbuf_strvised); syslog(priority, "%s (%s): %s", peer_addr, peer_name, msgbuf_strvised); } else if (peer_addr != NULL) { fprintf(stderr, "%s: %s: %s\n", getprogname(), peer_addr, msgbuf_strvised); syslog(priority, "%s: %s", peer_addr, msgbuf_strvised); } else { fprintf(stderr, "%s: %s\n", getprogname(), msgbuf_strvised); syslog(priority, "%s", msgbuf_strvised); } } else { errstr = strerror(log_errno); if (peer_name != NULL) { fprintf(stderr, "%s: %s (%s): %s: %s\n", getprogname(), peer_addr, peer_name, msgbuf_strvised, errstr); syslog(priority, "%s (%s): %s: %s", peer_addr, peer_name, msgbuf_strvised, errstr); } else if (peer_addr != NULL) { fprintf(stderr, "%s: %s: %s: %s\n", getprogname(), peer_addr, msgbuf_strvised, errstr); syslog(priority, "%s: %s: %s", peer_addr, msgbuf_strvised, errstr); } else { fprintf(stderr, "%s: %s: %s\n", getprogname(), msgbuf_strvised, errstr); syslog(priority, "%s: %s", msgbuf_strvised, errstr); } } } void log_err(int eval, const char *fmt, ...) { va_list ap; va_start(ap, fmt); log_common(LOG_CRIT, errno, fmt, ap); va_end(ap); exit(eval); } void log_errx(int eval, const char *fmt, ...) { va_list ap; va_start(ap, fmt); log_common(LOG_CRIT, -1, fmt, ap); va_end(ap); exit(eval); } void log_warn(const char *fmt, ...) { va_list ap; va_start(ap, fmt); log_common(LOG_WARNING, errno, fmt, ap); va_end(ap); } void log_warnx(const char *fmt, ...) { va_list ap; va_start(ap, fmt); log_common(LOG_WARNING, -1, fmt, ap); va_end(ap); } void log_debugx(const char *fmt, ...) { va_list ap; if (log_level == 0) return; va_start(ap, fmt); log_common(LOG_DEBUG, -1, fmt, ap); va_end(ap); } diff --git a/usr.sbin/ctld/pdu.c b/lib/libiscsiutil/pdu.c similarity index 77% rename from usr.sbin/ctld/pdu.c rename to lib/libiscsiutil/pdu.c index 04691556b997..ed5ee5b71766 100644 --- a/usr.sbin/ctld/pdu.c +++ b/lib/libiscsiutil/pdu.c @@ -1,268 +1,228 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 The FreeBSD Foundation * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include +#include #include +#include #include -#include "ctld.h" -#include "iscsi_proto.h" +#include +#include "libiscsiutil.h" -#ifdef ICL_KERNEL_PROXY -#include -#endif - -extern bool proxy_mode; - -static int +int pdu_ahs_length(const struct pdu *pdu) { return (pdu->pdu_bhs->bhs_total_ahs_len * 4); } -static int +int pdu_data_segment_length(const struct pdu *pdu) { uint32_t len = 0; 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 +void pdu_set_data_segment_length(struct pdu *pdu, uint32_t len) { pdu->pdu_bhs->bhs_data_segment_len[2] = len; pdu->pdu_bhs->bhs_data_segment_len[1] = len >> 8; pdu->pdu_bhs->bhs_data_segment_len[0] = len >> 16; } struct pdu * pdu_new(struct connection *conn) { struct pdu *pdu; pdu = calloc(1, sizeof(*pdu)); if (pdu == NULL) log_err(1, "calloc"); pdu->pdu_bhs = calloc(1, sizeof(*pdu->pdu_bhs)); if (pdu->pdu_bhs == NULL) log_err(1, "calloc"); pdu->pdu_connection = conn; return (pdu); } struct pdu * pdu_new_response(struct pdu *request) { return (pdu_new(request->pdu_connection)); } -#ifdef ICL_KERNEL_PROXY - -static void -pdu_receive_proxy(struct pdu *pdu) -{ - struct connection *conn; - size_t len; - - assert(proxy_mode); - conn = pdu->pdu_connection; - - kernel_receive(pdu); - - len = pdu_ahs_length(pdu); - if (len > 0) - log_errx(1, "protocol error: non-empty AHS"); - - len = pdu_data_segment_length(pdu); - assert(len <= (size_t)conn->conn_max_recv_data_segment_length); - pdu->pdu_data_len = len; -} - -static void -pdu_send_proxy(struct pdu *pdu) -{ - - assert(proxy_mode); - - pdu_set_data_segment_length(pdu, pdu->pdu_data_len); - kernel_send(pdu); -} - -#endif /* ICL_KERNEL_PROXY */ - static size_t pdu_padding(const struct pdu *pdu) { 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) +pdu_read(const struct connection *conn, char *data, size_t len) { ssize_t ret; while (len > 0) { - ret = read(fd, data, len); + ret = read(conn->conn_socket, data, len); if (ret < 0) { - if (timed_out()) + if (conn->conn_ops->timed_out()) { + conn->conn_ops->fail(conn, + "Login Phase timeout"); log_errx(1, "exiting due to timeout"); + } + conn->conn_ops->fail(conn, strerror(errno)); log_err(1, "read"); - } else if (ret == 0) + } else if (ret == 0) { + conn->conn_ops->fail(conn, "connection lost"); log_errx(1, "read: connection lost"); + } len -= ret; data += ret; } } void pdu_receive(struct pdu *pdu) { struct connection *conn; size_t len, padding; char dummy[4]; -#ifdef ICL_KERNEL_PROXY - if (proxy_mode) - return (pdu_receive_proxy(pdu)); -#endif - - assert(proxy_mode == false); conn = pdu->pdu_connection; + if (conn->conn_use_proxy) + return (conn->conn_ops->pdu_receive_proxy(pdu)); - pdu_read(conn->conn_socket, (char *)pdu->pdu_bhs, - sizeof(*pdu->pdu_bhs)); + pdu_read(conn, (char *)pdu->pdu_bhs, sizeof(*pdu->pdu_bhs)); len = pdu_ahs_length(pdu); if (len > 0) log_errx(1, "protocol error: non-empty AHS"); len = pdu_data_segment_length(pdu); if (len > 0) { if (len > (size_t)conn->conn_max_recv_data_segment_length) { log_errx(1, "protocol error: received PDU " "with DataSegmentLength exceeding %d", conn->conn_max_recv_data_segment_length); } pdu->pdu_data_len = len; pdu->pdu_data = malloc(len); if (pdu->pdu_data == NULL) log_err(1, "malloc"); - pdu_read(conn->conn_socket, (char *)pdu->pdu_data, - pdu->pdu_data_len); + pdu_read(conn, (char *)pdu->pdu_data, pdu->pdu_data_len); padding = pdu_padding(pdu); if (padding != 0) { assert(padding < sizeof(dummy)); - pdu_read(conn->conn_socket, (char *)dummy, padding); + pdu_read(conn, (char *)dummy, padding); } } } void pdu_send(struct pdu *pdu) { + struct connection *conn; ssize_t ret, total_len; size_t padding; uint32_t zero = 0; struct iovec iov[3]; int iovcnt; -#ifdef ICL_KERNEL_PROXY - if (proxy_mode) - return (pdu_send_proxy(pdu)); -#endif - - assert(proxy_mode == false); + conn = pdu->pdu_connection; + if (conn->conn_use_proxy) + return (conn->conn_ops->pdu_send_proxy(pdu)); pdu_set_data_segment_length(pdu, pdu->pdu_data_len); iov[0].iov_base = pdu->pdu_bhs; 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); + ret = writev(conn->conn_socket, iov, iovcnt); if (ret < 0) { - if (timed_out()) + if (conn->conn_ops->timed_out()) log_errx(1, "exiting due to timeout"); log_err(1, "writev"); } if (ret != total_len) log_errx(1, "short write"); } void pdu_delete(struct pdu *pdu) { free(pdu->pdu_data); free(pdu->pdu_bhs); free(pdu); } diff --git a/lib/libiscsiutil/utils.c b/lib/libiscsiutil/utils.c new file mode 100644 index 000000000000..1e04490c477c --- /dev/null +++ b/lib/libiscsiutil/utils.c @@ -0,0 +1,44 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2012 The FreeBSD Foundation + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include "libiscsiutil.h" + +char * +checked_strdup(const char *s) +{ + char *c; + + c = strdup(s); + if (c == NULL) + log_err(1, "strdup"); + return (c); +} diff --git a/rescue/rescue/Makefile b/rescue/rescue/Makefile index 90aaea3cb709..783b5bbd7721 100644 --- a/rescue/rescue/Makefile +++ b/rescue/rescue/Makefile @@ -1,242 +1,245 @@ #$FreeBSD$ # @(#)Makefile 8.1 (Berkeley) 6/2/93 .include PACKAGE=rescue MAN= MK_SSP= no # Static-PIE is not supported so we should not be linking against _pie.a libs. # This is also needed to avoid linking against sanitizer-instrumented libraries # since MK_ASAN/MK_UBSAN will instrument the .pieo object files. MK_PIE= no NO_SHARED= yes CRUNCH_BUILDOPTS+= MK_PIE=no NO_SHARED=yes PROG= rescue BINDIR?=/rescue # Shell scripts need #! line to be edited from /bin/sh to /rescue/sh SCRIPTS= nextboot_FIXED SCRIPTSNAME_nextboot_FIXED= nextboot nextboot_FIXED: ../../sbin/reboot/nextboot.sh sed '1s/\/bin\//\/rescue\//' ${.ALLSRC} > ${.TARGET} CLEANFILES+= nextboot_FIXED SCRIPTS+= dhclient_FIXED SCRIPTSNAME_dhclient_FIXED= dhclient-script dhclient_FIXED: ../../sbin/dhclient/dhclient-script sed '1s/\/bin\//\/rescue\//' ${.ALLSRC} > ${.TARGET} CLEANFILES+= dhclient_FIXED # The help which used to be here is now in mk/bsd.crunchgen.mk # Define Makefile variable RESCUE CRUNCH_BUILDOPTS+= -DRESCUE # Define compile-time RESCUE symbol when compiling components CRUNCH_BUILDOPTS+= CRUNCH_CFLAGS=-DRESCUE # An experiment that failed: try overriding bsd.lib.mk and bsd.prog.mk # rather than incorporating rescue-specific logic into standard files. #MAKEFLAGS= -m ${.CURDIR} ${.MAKEFLAGS} # Hackery: 'librescue' exists merely as a tool for appropriately # recompiling specific library entries. We _know_ they're needed, and # regular archive searching creates ugly library ordering problems. # Easiest fix: tell the linker to include them into the executable # first, so they are guaranteed to override the regular lib entries. # Note that if 'librescue' hasn't been compiled, we'll just get the # regular lib entries from libc and friends. CRUNCH_LIBS+= ${.OBJDIR}/../librescue/*.o ################################################################### # Programs from stock /bin # # WARNING: Changing this list may require adjusting # /usr/include/paths.h as well! You were warned! # CRUNCH_SRCDIRS+= bin CRUNCH_PROGS_bin= cat chflags chio chmod cp date dd df echo \ ed expr getfacl hostname kenv kill ln ls mkdir mv \ pkill ps pwd realpath rm rmdir setfacl sh sleep stty \ sync test CRUNCH_LIBS+= -lcrypt -ledit -ljail -lkvm -lelf -ltermcapw -lutil -lxo CRUNCH_BUILDTOOLS+= bin/sh # Additional options for specific programs CRUNCH_ALIAS_test= [ CRUNCH_ALIAS_sh= -sh # The -sh alias shouldn't appear in /rescue as a hard link CRUNCH_SUPPRESS_LINK_-sh= 1 CRUNCH_ALIAS_ln= link CRUNCH_ALIAS_rm= unlink CRUNCH_ALIAS_ed= red CRUNCH_ALIAS_pkill= pgrep .if ${MK_TCSH} != "no" CRUNCH_PROGS_bin+= csh CRUNCH_ALIAS_csh= -csh tcsh -tcsh CRUNCH_BUILDTOOLS+= bin/csh CRUNCH_SUPPRESS_LINK_-csh= 1 CRUNCH_SUPPRESS_LINK_-tcsh= 1 .endif ################################################################### # Programs from standard /sbin # # WARNING: Changing this list may require adjusting # /usr/include/paths.h as well! You were warned! # # Note that mdmfs have their own private 'pathnames.h' # headers in addition to the standard 'paths.h' header. # CRUNCH_SRCDIRS+= sbin CRUNCH_PROGS_sbin= \ camcontrol clri devfs dmesg dump \ dumpfs dumpon fsck fsck_ffs fsck_msdosfs fsdb \ fsirand gbde geom ifconfig init \ kldconfig kldload kldstat kldunload ldconfig \ md5 mdconfig mdmfs mknod mount mount_cd9660 \ mount_msdosfs mount_nfs mount_nullfs \ mount_udf mount_unionfs newfs \ newfs_msdos nos-tun ping reboot \ restore rcorder route savecore \ shutdown swapon sysctl tunefs umount .if ${MK_CCD} != "no" CRUNCH_PROGS_sbin+= ccdconfig .endif .if ${MK_INET6_SUPPORT} != "no" CRUNCH_ALIAS_ping= ping6 CRUNCH_PROGS_sbin+= rtsol .endif .if ${MK_IPFILTER} != "no" CRUNCH_PROGS_sbin+= ipf CRUNCH_LIBS_ipf+= ${LIBIPF} .endif .if ${MK_ROUTED} != "no" CRUNCH_PROGS_sbin+= routed rtquery .endif .if ${MK_ZFS} != "no" CRUNCH_PROGS_sbin+= bectl CRUNCH_PROGS_sbin+= zfs CRUNCH_PROGS_sbin+= zpool CRUNCH_PROGS_usr.sbin+= zdb .endif # crunchgen does not like C++ programs; this should be fixed someday # CRUNCH_PROGS+= devd CRUNCH_LIBS+= -l80211 -lalias -lcam -lncursesw_real -ldevstat -lipsec -llzma .if ${MK_ZFS} != "no" CRUNCH_LIBS+= -lavl -lzpool -lzfs_core -lzfs -lnvpair -lpthread -luutil -lumem CRUNCH_LIBS+= -lbe -lzfsbootenv -lzutil -ltpool -lspl -licp_rescue .else # liblzma needs pthread CRUNCH_LIBS+= -lpthread .endif CRUNCH_LIBS+= -lgeom -lbsdxml -lkiconv .if ${MK_OPENSSL} == "no" CRUNCH_LIBS+= -lmd .endif CRUNCH_LIBS+= -lmt -lsbuf -lufs -lz .if ${MACHINE_CPUARCH} == "i386" CRUNCH_PROGS_sbin+= bsdlabel sconfig fdisk CRUNCH_ALIAS_bsdlabel= disklabel #CRUNCH_PROGS+= mount_smbfs #CRUNCH_LIBS+= -lsmb .endif .if ${MACHINE_CPUARCH} == "amd64" CRUNCH_PROGS_sbin+= bsdlabel fdisk CRUNCH_ALIAS_bsdlabel= disklabel .endif CRUNCH_SRCDIR_rtquery= ${SRCTOP}/sbin/routed/rtquery CRUNCH_SRCDIR_ipf= ${SRCTOP}/sbin/ipf/ipf .if ${MK_ZFS} != "no" CRUNCH_SRCDIR_zfs= ${SRCTOP}/cddl/sbin/zfs CRUNCH_SRCDIR_zpool= ${SRCTOP}/cddl/sbin/zpool CRUNCH_SRCDIR_zdb= ${SRCTOP}/cddl/usr.sbin/zdb .endif CRUNCH_ALIAS_reboot= fastboot halt fasthalt CRUNCH_ALIAS_restore= rrestore CRUNCH_ALIAS_dump= rdump CRUNCH_ALIAS_fsck_ffs= fsck_4.2bsd fsck_ufs CRUNCH_ALIAS_geom= glabel gpart CRUNCH_ALIAS_shutdown= poweroff # dhclient has historically been troublesome... CRUNCH_PROGS_sbin+= dhclient ################################################################## # Programs from stock /usr/bin # CRUNCH_SRCDIRS+= usr.bin CRUNCH_PROGS_usr.bin= head mt sed tail tee CRUNCH_PROGS_usr.bin+= gzip CRUNCH_ALIAS_gzip= gunzip gzcat zcat CRUNCH_PROGS_usr.bin+= bzip2 CRUNCH_ALIAS_bzip2= bunzip2 bzcat CRUNCH_LIBS+= -lbz2 CRUNCH_PROGS_usr.bin+= less CRUNCH_ALIAS_less= more CRUNCH_PROGS_usr.bin+= xz CRUNCH_ALIAS_xz= unxz lzma unlzma xzcat lzcat CRUNCH_PROGS_usr.bin+= zstd CRUNCH_ALIAS_zstd= unzstd zstdcat zstdmt CRUNCH_LIBS+= ${LDADD_zstd} CRUNCH_PROGS_usr.bin+= tar CRUNCH_LIBS+= -larchive .if ${MK_OPENSSL} != "no" CRUNCH_LIBS+= -lcrypto .endif CRUNCH_LIBS+= -lmd .if ${MK_NETCAT} != "no" CRUNCH_PROGS_usr.bin+= nc .endif .if ${MK_VI} != "no" CRUNCH_PROGS_usr.bin+= vi CRUNCH_ALIAS_vi= ex .endif CRUNCH_PROGS_usr.bin+= id CRUNCH_ALIAS_id= groups whoami ################################################################## # Programs from stock /usr/sbin # CRUNCH_SRCDIRS+= usr.sbin CRUNCH_PROGS_usr.sbin+= chroot CRUNCH_PROGS_usr.sbin+= chown CRUNCH_ALIAS_chown= chgrp ################################################################## CRUNCH_LIBS+= ${OBJTOP}/lib/libifconfig/libifconfig.a CRUNCH_BUILDOPTS+= CRUNCH_CFLAGS+=-I${OBJTOP}/lib/libifconfig CRUNCH_LIBS+= -lm .if ${MK_ISCSI} != "no" CRUNCH_PROGS_usr.bin+= iscsictl CRUNCH_PROGS_usr.sbin+= iscsid + +CRUNCH_LIBS+= ${OBJTOP}/lib/libiscsiutil/libiscsiutil.a +CRUNCH_BUILDOPTS+= CRUNCH_CFLAGS+=-I${OBJTOP}/lib/libiscsiutil .endif .include .include diff --git a/share/mk/bsd.libnames.mk b/share/mk/bsd.libnames.mk index f71664bf5858..45710b203ddc 100644 --- a/share/mk/bsd.libnames.mk +++ b/share/mk/bsd.libnames.mk @@ -1,220 +1,221 @@ # $FreeBSD$ # The include file define library names. # Other include files (e.g. bsd.prog.mk, bsd.lib.mk) include this # file where necessary. .if !target(____) .error bsd.libnames.mk cannot be included directly. .endif LIBDESTDIR?= ${SYSROOT:U${DESTDIR}} .sinclude # Src directory locations are also defined in src.libnames.mk. LIBCRT0?= ${LIBDESTDIR}${LIBDIR_BASE}/crt0.o LIB80211?= ${LIBDESTDIR}${LIBDIR_BASE}/lib80211.a LIB9P?= ${LIBDESTDIR}${LIBDIR_BASE}/lib9p.a LIBALIAS?= ${LIBDESTDIR}${LIBDIR_BASE}/libalias.a LIBARCHIVE?= ${LIBDESTDIR}${LIBDIR_BASE}/libarchive.a LIBASN1?= ${LIBDESTDIR}${LIBDIR_BASE}/libasn1.a LIBATM?= ${LIBDESTDIR}${LIBDIR_BASE}/libatm.a LIBAUDITD?= ${LIBDESTDIR}${LIBDIR_BASE}/libauditd.a LIBAVL?= ${LIBDESTDIR}${LIBDIR_BASE}/libavl.a LIBBE?= ${LIBDESTDIR}${LIBDIR_BASE}/libbe.a LIBBEGEMOT?= ${LIBDESTDIR}${LIBDIR_BASE}/libbegemot.a LIBBLACKLIST?= ${LIBDESTDIR}${LIBDIR_BASE}/libblacklist.a LIBBLUETOOTH?= ${LIBDESTDIR}${LIBDIR_BASE}/libbluetooth.a LIBBSDXML?= ${LIBDESTDIR}${LIBDIR_BASE}/libbsdxml.a LIBBSM?= ${LIBDESTDIR}${LIBDIR_BASE}/libbsm.a LIBBSNMP?= ${LIBDESTDIR}${LIBDIR_BASE}/libbsnmp.a LIBBZ2?= ${LIBDESTDIR}${LIBDIR_BASE}/libbz2.a LIBC?= ${LIBDESTDIR}${LIBDIR_BASE}/libc.a LIBCALENDAR?= ${LIBDESTDIR}${LIBDIR_BASE}/libcalendar.a LIBCAM?= ${LIBDESTDIR}${LIBDIR_BASE}/libcam.a LIBCOMPAT?= ${LIBDESTDIR}${LIBDIR_BASE}/libcompat.a LIBCOMPILER_RT?=${LIBDESTDIR}${LIBDIR_BASE}/libcompiler_rt.a LIBCOM_ERR?= ${LIBDESTDIR}${LIBDIR_BASE}/libcom_err.a LIBCPLUSPLUS?= ${LIBDESTDIR}${LIBDIR_BASE}/libc++.a LIBCRYPT?= ${LIBDESTDIR}${LIBDIR_BASE}/libcrypt.a LIBCRYPTO?= ${LIBDESTDIR}${LIBDIR_BASE}/libcrypto.a LIBCTF?= ${LIBDESTDIR}${LIBDIR_BASE}/libctf.a LIBCURSES?= ${LIBDESTDIR}${LIBDIR_BASE}/libcurses.a LIBCUSE?= ${LIBDESTDIR}${LIBDIR_BASE}/libcuse.a LIBCXGB4?= ${LIBDESTDIR}${LIBDIR_BASE}/libcxgb4.a LIBCXXRT?= ${LIBDESTDIR}${LIBDIR_BASE}/libcxxrt.a LIBC_PIC?= ${LIBDESTDIR}${LIBDIR_BASE}/libc_pic.a LIBDEVCTL?= ${LIBDESTDIR}${LIBDIR_BASE}/libdevctl.a LIBDEVDCTL?= ${LIBDESTDIR}${LIBDIR_BASE}/libdevdctl.a LIBDEVINFO?= ${LIBDESTDIR}${LIBDIR_BASE}/libdevinfo.a LIBDEVSTAT?= ${LIBDESTDIR}${LIBDIR_BASE}/libdevstat.a LIBDIALOG?= ${LIBDESTDIR}${LIBDIR_BASE}/libdialog.a LIBDL?= ${LIBDESTDIR}${LIBDIR_BASE}/libdl.a LIBDNS?= ${LIBDESTDIR}${LIBDIR_BASE}/libdns.a LIBDPV?= ${LIBDESTDIR}${LIBDIR_BASE}/libdpv.a LIBDTRACE?= ${LIBDESTDIR}${LIBDIR_BASE}/libdtrace.a LIBDWARF?= ${LIBDESTDIR}${LIBDIR_BASE}/libdwarf.a LIBEDIT?= ${LIBDESTDIR}${LIBDIR_BASE}/libedit.a LIBEFIVAR?= ${LIBDESTDIR}${LIBDIR_BASE}/libefivar.a LIBELF?= ${LIBDESTDIR}${LIBDIR_BASE}/libelf.a LIBEXECINFO?= ${LIBDESTDIR}${LIBDIR_BASE}/libexecinfo.a LIBFETCH?= ${LIBDESTDIR}${LIBDIR_BASE}/libfetch.a LIBFIGPAR?= ${LIBDESTDIR}${LIBDIR_BASE}/libfigpar.a LIBFL?= "don't use LIBFL, use LIBL" LIBFORMW?= ${LIBDESTDIR}${LIBDIR_BASE}/libformw.a LIBG2C?= ${LIBDESTDIR}${LIBDIR_BASE}/libg2c.a LIBGEOM?= ${LIBDESTDIR}${LIBDIR_BASE}/libgeom.a LIBGPIO?= ${LIBDESTDIR}${LIBDIR_BASE}/libgpio.a LIBGSSAPI?= ${LIBDESTDIR}${LIBDIR_BASE}/libgssapi.a LIBGSSAPI_KRB5?= ${LIBDESTDIR}${LIBDIR_BASE}/libgssapi_krb5.a LIBHDB?= ${LIBDESTDIR}${LIBDIR_BASE}/libhdb.a LIBHEIMBASE?= ${LIBDESTDIR}${LIBDIR_BASE}/libheimbase.a LIBHEIMNTLM?= ${LIBDESTDIR}${LIBDIR_BASE}/libheimntlm.a LIBHEIMSQLITE?= ${LIBDESTDIR}${LIBDIR_BASE}/libheimsqlite.a LIBHX509?= ${LIBDESTDIR}${LIBDIR_BASE}/libhx509.a LIBIBCM?= ${LIBDESTDIR}${LIBDIR_BASE}/libibcm.a LIBIBMAD?= ${LIBDESTDIR}${LIBDIR_BASE}/libibmad.a LIBIBNETDISC?= ${LIBDESTDIR}${LIBDIR_BASE}/libibnetdisc.a LIBIBUMAD?= ${LIBDESTDIR}${LIBDIR_BASE}/libibumad.a LIBIBVERBS?= ${LIBDESTDIR}${LIBDIR_BASE}/libibverbs.a LIBICP?= ${LIBDESTDIR}${LIBDIR_BASE}/libicp.a LIBIPSEC?= ${LIBDESTDIR}${LIBDIR_BASE}/libipsec.a LIBIPT?= ${LIBDESTDIR}${LIBDIR_BASE}/libipt.a +LIBISCSIUTIL?= ${LIBDESTDIR}${LIBDIR_BASE}/libiscsiutil.a LIBJAIL?= ${LIBDESTDIR}${LIBDIR_BASE}/libjail.a LIBKADM5CLNT?= ${LIBDESTDIR}${LIBDIR_BASE}/libkadm5clnt.a LIBKADM5SRV?= ${LIBDESTDIR}${LIBDIR_BASE}/libkadm5srv.a LIBKAFS5?= ${LIBDESTDIR}${LIBDIR_BASE}/libkafs5.a LIBKDC?= ${LIBDESTDIR}${LIBDIR_BASE}/libkdc.a LIBKEYCAP?= ${LIBDESTDIR}${LIBDIR_BASE}/libkeycap.a LIBKICONV?= ${LIBDESTDIR}${LIBDIR_BASE}/libkiconv.a LIBKRB5?= ${LIBDESTDIR}${LIBDIR_BASE}/libkrb5.a LIBKVM?= ${LIBDESTDIR}${LIBDIR_BASE}/libkvm.a LIBL?= ${LIBDESTDIR}${LIBDIR_BASE}/libl.a LIBLN?= "don't use LIBLN, use LIBL" LIBLZMA?= ${LIBDESTDIR}${LIBDIR_BASE}/liblzma.a LIBM?= ${LIBDESTDIR}${LIBDIR_BASE}/libm.a LIBMAGIC?= ${LIBDESTDIR}${LIBDIR_BASE}/libmagic.a LIBMD?= ${LIBDESTDIR}${LIBDIR_BASE}/libmd.a LIBMEMSTAT?= ${LIBDESTDIR}${LIBDIR_BASE}/libmemstat.a LIBMENU?= ${LIBDESTDIR}${LIBDIR_BASE}/libmenu.a LIBMILTER?= ${LIBDESTDIR}${LIBDIR_BASE}/libmilter.a LIBMLX4?= ${LIBDESTDIR}${LIBDIR_BASE}/libmlx4.a LIBMLX5?= ${LIBDESTDIR}${LIBDIR_BASE}/libmlx5.a LIBMP?= ${LIBDESTDIR}${LIBDIR_BASE}/libmp.a LIBMT?= ${LIBDESTDIR}${LIBDIR_BASE}/libmt.a LIBNCURSES?= ${LIBDESTDIR}${LIBDIR_BASE}/libncurses.a LIBNCURSESW?= ${LIBDESTDIR}${LIBDIR_BASE}/libncursesw.a LIBNETGRAPH?= ${LIBDESTDIR}${LIBDIR_BASE}/libnetgraph.a LIBNETMAP?= ${LIBDESTDIR}${LIBDIR_BASE}/libnetmap.a LIBNGATM?= ${LIBDESTDIR}${LIBDIR_BASE}/libngatm.a LIBNV?= ${LIBDESTDIR}${LIBDIR_BASE}/libnv.a LIBNVPAIR?= ${LIBDESTDIR}${LIBDIR_BASE}/libnvpair.a LIBOPENCSD?= ${LIBDESTDIR}${LIBDIR_BASE}/libopencsd.a LIBOPENSM?= ${LIBDESTDIR}${LIBDIR_BASE}/libopensm.a LIBOPIE?= ${LIBDESTDIR}${LIBDIR_BASE}/libopie.a LIBOSMCOMP?= ${LIBDESTDIR}${LIBDIR_BASE}/libosmcomp.a LIBOSMVENDOR?= ${LIBDESTDIR}${LIBDIR_BASE}/libosmvendor.a LIBPAM?= ${LIBDESTDIR}${LIBDIR_BASE}/libpam.a LIBPANEL?= ${LIBDESTDIR}${LIBDIR_BASE}/libpanel.a LIBPANELW?= ${LIBDESTDIR}${LIBDIR_BASE}/libpanelw.a LIBPCAP?= ${LIBDESTDIR}${LIBDIR_BASE}/libpcap.a LIBPJDLOG?= ${LIBDESTDIR}${LIBDIR_BASE}/libpjdlog.a LIBPMC?= ${LIBDESTDIR}${LIBDIR_BASE}/libpmc.a LIBPROC?= ${LIBDESTDIR}${LIBDIR_BASE}/libproc.a LIBPROCSTAT?= ${LIBDESTDIR}${LIBDIR_BASE}/libprocstat.a LIBPTHREAD?= ${LIBDESTDIR}${LIBDIR_BASE}/libpthread.a LIBRADIUS?= ${LIBDESTDIR}${LIBDIR_BASE}/libradius.a LIBRDMACM?= ${LIBDESTDIR}${LIBDIR_BASE}/librdmacm.a LIBREGEX?= ${LIBDESTDIR}${LIBDIR_BASE}/libregex.a LIBROKEN?= ${LIBDESTDIR}${LIBDIR_BASE}/libroken.a LIBRPCSEC_GSS?= ${LIBDESTDIR}${LIBDIR_BASE}/librpcsec_gss.a LIBRPCSVC?= ${LIBDESTDIR}${LIBDIR_BASE}/librpcsvc.a LIBRT?= ${LIBDESTDIR}${LIBDIR_BASE}/librt.a LIBRTLD_DB?= ${LIBDESTDIR}${LIBDIR_BASE}/librtld_db.a LIBSBUF?= ${LIBDESTDIR}${LIBDIR_BASE}/libsbuf.a LIBSDP?= ${LIBDESTDIR}${LIBDIR_BASE}/libsdp.a LIBSMB?= ${LIBDESTDIR}${LIBDIR_BASE}/libsmb.a LIBSPL?= ${LIBDESTDIR}${LIBDIR_BASE}/libspl.a LIBSSL?= ${LIBDESTDIR}${LIBDIR_BASE}/libssl.a LIBSSP_NONSHARED?= ${LIBDESTDIR}${LIBDIR_BASE}/libssp_nonshared.a LIBSTATS?= ${LIBDESTDIR}${LIBDIR_BASE}/libstats.a LIBSTDTHREADS?= ${LIBDESTDIR}${LIBDIR_BASE}/libstdthreads.a LIBSYSDECODE?= ${LIBDESTDIR}${LIBDIR_BASE}/libsysdecode.a LIBTACPLUS?= ${LIBDESTDIR}${LIBDIR_BASE}/libtacplus.a LIBTERMCAP?= ${LIBDESTDIR}${LIBDIR_BASE}/libtermcap.a LIBTERMCAPW?= ${LIBDESTDIR}${LIBDIR_BASE}/libtermcapw.a LIBTERMLIB?= "don't use LIBTERMLIB, use LIBTERMCAP" LIBTINFOW= ${LIBDESTDIR}${LIBDIR_BASE}/libtinfow.a LIBTPOOL?= ${LIBDESTDIR}${LIBDIR_BASE}/libtpool.a LIBUFS?= ${LIBDESTDIR}${LIBDIR_BASE}/libufs.a LIBUGIDFW?= ${LIBDESTDIR}${LIBDIR_BASE}/libugidfw.a LIBULOG?= ${LIBDESTDIR}${LIBDIR_BASE}/libulog.a LIBUMEM?= ${LIBDESTDIR}${LIBDIR_BASE}/libumem.a LIBUSB?= ${LIBDESTDIR}${LIBDIR_BASE}/libusb.a LIBUSBHID?= ${LIBDESTDIR}${LIBDIR_BASE}/libusbhid.a LIBUTIL?= ${LIBDESTDIR}${LIBDIR_BASE}/libutil.a LIBUUTIL?= ${LIBDESTDIR}${LIBDIR_BASE}/libuutil.a LIBVGL?= ${LIBDESTDIR}${LIBDIR_BASE}/libvgl.a LIBVMMAPI?= ${LIBDESTDIR}${LIBDIR_BASE}/libvmmapi.a LIBWIND?= ${LIBDESTDIR}${LIBDIR_BASE}/libwind.a LIBWRAP?= ${LIBDESTDIR}${LIBDIR_BASE}/libwrap.a LIBXO?= ${LIBDESTDIR}${LIBDIR_BASE}/libxo.a LIBXPG4?= ${LIBDESTDIR}${LIBDIR_BASE}/libxpg4.a LIBY?= ${LIBDESTDIR}${LIBDIR_BASE}/liby.a LIBYPCLNT?= ${LIBDESTDIR}${LIBDIR_BASE}/libypclnt.a LIBZ?= ${LIBDESTDIR}${LIBDIR_BASE}/libz.a LIBZFS?= ${LIBDESTDIR}${LIBDIR_BASE}/libzfs.a LIBZFS_CORE?= ${LIBDESTDIR}${LIBDIR_BASE}/libzfs_core.a LIBZFSBOOTENV?= ${LIBDESTDIR}${LIBDIR_BASE}/libzfsbootenv.a LIBZPOOL?= ${LIBDESTDIR}${LIBDIR_BASE}/libzpool.a LIBZUTIL?= ${LIBDESTDIR}${LIBDIR_BASE}/libzutil.a # enforce the 2 -lpthread and -lc to always be the last in that exact order .if defined(LDADD) .if ${LDADD:M-lpthread} LDADD:= ${LDADD:N-lpthread} -lpthread .endif .if ${LDADD:M-lc} LDADD:= ${LDADD:N-lc} -lc .endif .endif # Only do this for src builds. .if defined(SRCTOP) .if defined(_LIBRARIES) && defined(LIB) && \ ${_LIBRARIES:M${LIB}} != "" .if !defined(LIB${LIB:tu}) .error ${.CURDIR}: Missing value for LIB${LIB:tu} in ${_this:T}. Likely should be: LIB${LIB:tu}?= $${LIBDESTDIR}$${LIBDIR_BASE}/lib${LIB}.a .endif .endif # Derive LIB*SRCDIR from LIB*DIR .for lib in ${_LIBRARIES} LIB${lib:tu}SRCDIR?= ${SRCTOP}/${LIB${lib:tu}DIR:S,^${OBJTOP}/,,} .endfor .else # Out of tree builds # There are LIBADD defined in an out-of-tree build. Are they *all* # in-tree libraries? If so convert them to LDADD to support # partial checkouts. .if !empty(LIBADD) _convert_libadd= 1 .for l in ${LIBADD} .if empty(LIB${l:tu}) _convert_libadd= 0 .endif .endfor .if ${_convert_libadd} == 1 .warning Converting out-of-tree build LIBADDs into LDADD. This is not fully supported. .for l in ${LIBADD} LDADD+= -l${l} .endfor .endif .endif .endif # defined(SRCTOP) diff --git a/share/mk/src.libnames.mk b/share/mk/src.libnames.mk index 385e8616a82d..cf6c41887791 100644 --- a/share/mk/src.libnames.mk +++ b/share/mk/src.libnames.mk @@ -1,762 +1,766 @@ # $FreeBSD$ # # The include file define library names suitable # for INTERNALLIB and PRIVATELIB definition .if !target(____) .error src.libnames.mk cannot be included directly. .endif .if !target(____) ____: .include _PRIVATELIBS= \ atf_c \ atf_cxx \ auditd \ bsddialog \ bsdstat \ cbor \ devdctl \ event1 \ fido2 \ gmock \ gtest \ gmock_main \ gtest_main \ heimipcc \ heimipcs \ ldns \ sqlite3 \ ssh \ ucl \ unbound \ zstd _INTERNALLIBS= \ amu \ bsnmptools \ c_nossp_pic \ cron \ elftc \ fifolog \ ifconfig \ ipf \ + iscsiutil \ lpr \ lua \ lutok \ netbsd \ ntp \ ntpevent \ openbsd \ opts \ parse \ pe \ pfctl \ pmcstat \ sl \ sm \ smdb \ smutil \ telnet \ vers \ wpaap \ wpacommon \ wpacrypto \ wpadrivers \ wpaeap_common \ wpaeap_peer \ wpaeap_server \ wpaeapol_auth \ wpaeapol_supp \ wpal2_packet \ wparadius \ wparsn_supp \ wpatls \ wpautils \ wpawps _LIBRARIES= \ ${_PRIVATELIBS} \ ${_INTERNALLIBS} \ ${LOCAL_LIBRARIES} \ 80211 \ 9p \ alias \ archive \ asn1 \ avl \ be \ begemot \ bluetooth \ bsdxml \ bsm \ bsnmp \ bz2 \ c \ c_pic \ calendar \ cam \ casper \ cap_dns \ cap_fileargs \ cap_grp \ cap_net \ cap_netdb \ cap_pwd \ cap_sysctl \ cap_syslog \ com_err \ compiler_rt \ crypt \ crypto \ ctf \ cuse \ cxxrt \ devctl \ devdctl \ devinfo \ devstat \ dialog \ dl \ dpv \ dtrace \ dwarf \ edit \ efivar \ elf \ execinfo \ fetch \ figpar \ formw \ geom \ gpio \ gssapi \ gssapi_krb5 \ hdb \ heimbase \ heimntlm \ heimsqlite \ hx509 \ icp \ ipsec \ ipt \ jail \ kadm5clnt \ kadm5srv \ kafs5 \ kdc \ kiconv \ krb5 \ kvm \ l \ lzma \ m \ magic \ md \ memstat \ mp \ mt \ ncursesw \ netgraph \ netmap \ ngatm \ nv \ nvpair \ opencsd \ opie \ pam \ panel \ panelw \ pcap \ pcsclite \ pjdlog \ pmc \ proc \ procstat \ pthread \ radius \ regex \ roken \ rpcsec_gss \ rpcsvc \ rt \ rtld_db \ sbuf \ sdp \ sm \ smb \ spl \ ssl \ ssp_nonshared \ stats \ stdthreads \ supcplusplus \ sysdecode \ tacplus \ termcapw \ tinfow \ tpool \ ufs \ ugidfw \ ulog \ umem \ usb \ usbhid \ util \ uutil \ vmmapi \ wind \ wrap \ xo \ y \ ypclnt \ z \ zfs_core \ zfs \ zfsbootenv \ zpool \ zutil .if ${MK_BLACKLIST} != "no" _LIBRARIES+= \ blacklist \ .endif .if ${MK_OFED} != "no" _LIBRARIES+= \ cxgb4 \ ibcm \ ibmad \ ibnetdisc \ ibumad \ ibverbs \ mlx4 \ mlx5 \ rdmacm \ osmcomp \ opensm \ osmvendor .endif .if ${MK_BEARSSL} == "yes" _LIBRARIES+= \ bearssl \ secureboot \ LIBBEARSSL?= ${LIBBEARSSLDIR}/libbearssl.a LIBSECUREBOOT?= ${LIBSECUREBOOTDIR}/libsecureboot.a .endif .if ${MK_VERIEXEC} == "yes" _LIBRARIES+= veriexec LIBVERIEXEC?= ${LIBVERIEXECDIR}/libveriexec.a .endif # Each library's LIBADD needs to be duplicated here for static linkage of # 2nd+ order consumers. Auto-generating this would be better. _DP_80211= sbuf bsdxml _DP_9p= sbuf # XXX: Not bootstrapped so uses host version on non-FreeBSD, so don't use a # FreeBSD-specific dependency list .if ${.MAKE.OS} == "FreeBSD" || !defined(BOOTSTRAPPING) _DP_archive= z bz2 lzma bsdxml zstd .endif _DP_avl= spl _DP_bsddialog= formw ncursesw tinfow _DP_zstd= pthread .if ${MK_BLACKLIST} != "no" _DP_blacklist+= pthread .endif _DP_crypto= pthread # See comment by _DP_archive above .if ${.MAKE.OS} == "FreeBSD" || !defined(BOOTSTRAPPING) .if ${MK_OPENSSL} != "no" _DP_archive+= crypto .else _DP_archive+= md .endif .endif _DP_sqlite3= pthread _DP_ssl= crypto _DP_ssh= crypto crypt z .if ${MK_LDNS} != "no" _DP_ssh+= ldns .endif _DP_edit= tinfow .if ${MK_OPENSSL} != "no" _DP_bsnmp= crypto .endif _DP_geom= bsdxml sbuf _DP_cam= sbuf _DP_kvm= elf _DP_casper= nv _DP_cap_dns= nv _DP_cap_fileargs= nv _DP_cap_grp= nv _DP_cap_pwd= nv _DP_cap_sysctl= nv _DP_cap_syslog= nv .if ${MK_OFED} != "no" _DP_pcap= ibverbs mlx5 .endif _DP_pjdlog= util _DP_opie= md _DP_usb= pthread _DP_unbound= ssl crypto pthread _DP_rt= pthread .if ${MK_OPENSSL} == "no" _DP_radius= md .else _DP_radius= crypto .endif _DP_rtld_db= elf procstat _DP_procstat= kvm util elf .if ${MK_CXX} == "yes" _DP_proc= cxxrt .endif .if ${MK_CDDL} != "no" _DP_proc+= ctf .endif _DP_proc+= elf procstat rtld_db util _DP_mp= crypto _DP_memstat= kvm _DP_magic= z _DP_mt= sbuf bsdxml _DP_ldns= ssl crypto _DP_lua= m _DP_lutok= lua .if ${MK_OPENSSL} != "no" _DP_fetch= ssl crypto .else _DP_fetch= md .endif _DP_execinfo= elf _DP_dwarf= elf z _DP_dpv= dialog figpar util tinfow ncursesw _DP_dialog= tinfow ncursesw m _DP_cuse= pthread _DP_atf_cxx= atf_c _DP_gtest= pthread regex _DP_gmock= gtest _DP_gmock_main= gmock _DP_gtest_main= gtest _DP_devstat= kvm _DP_pam= radius tacplus opie md util .if ${MK_KERBEROS} != "no" _DP_pam+= krb5 .endif .if ${MK_OPENSSH} != "no" _DP_fido2+= crypto z _DP_pam+= ssh .endif .if ${MK_NIS} != "no" _DP_pam+= ypclnt .endif _DP_roken= crypt _DP_kadm5clnt= com_err krb5 roken _DP_kadm5srv= com_err hdb krb5 roken _DP_heimntlm= crypto com_err krb5 roken _DP_hx509= asn1 com_err crypto roken wind _DP_hdb= asn1 com_err krb5 roken sqlite3 _DP_asn1= com_err roken _DP_kdc= roken hdb hx509 krb5 heimntlm asn1 crypto _DP_wind= com_err roken _DP_heimbase= pthread _DP_heimipcc= heimbase roken pthread _DP_heimipcs= heimbase roken pthread _DP_kafs5= asn1 krb5 roken _DP_krb5+= asn1 com_err crypt crypto hx509 roken wind heimbase heimipcc _DP_gssapi_krb5+= gssapi krb5 crypto roken asn1 com_err _DP_lzma= md pthread _DP_ucl= m _DP_vmmapi= util _DP_opencsd= cxxrt _DP_ctf= spl z _DP_dtrace= ctf elf proc pthread rtld_db _DP_xo= util _DP_ztest= geom m nvpair umem zpool pthread avl zfs_core spl zutil zfs uutil icp # The libc dependencies are not strictly needed but are defined to make the # assert happy. _DP_c= compiler_rt .if ${MK_SSP} != "no" && \ (${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH:Mpower*} != "") _DP_c+= ssp_nonshared .endif _DP_stats= sbuf pthread _DP_stdthreads= pthread _DP_tacplus= md _DP_ncursesw= tinfow _DP_formw= ncursesw _DP_nvpair= spl _DP_panelw= ncursesw _DP_rpcsec_gss= gssapi _DP_smb= kiconv _DP_ulog= md _DP_fifolog= z _DP_ipf= kvm _DP_tpool= spl _DP_uutil= avl spl _DP_zfs= md pthread umem util uutil m avl bsdxml crypto geom nvpair \ z zfs_core zutil _DP_zfsbootenv= zfs nvpair _DP_zfs_core= nvpair spl zutil _DP_zpool= md pthread z icp spl nvpair avl umem _DP_zutil= avl geom m tpool _DP_be= zfs spl nvpair zfsbootenv _DP_netmap= _DP_ifconfig= m _DP_pfctl= nv # OFED support .if ${MK_OFED} != "no" _DP_cxgb4= ibverbs pthread _DP_ibcm= ibverbs _DP_ibmad= ibumad _DP_ibnetdisc= osmcomp ibmad ibumad _DP_ibumad= _DP_ibverbs= _DP_mlx4= ibverbs pthread _DP_mlx5= ibverbs pthread _DP_rdmacm= ibverbs _DP_osmcomp= pthread _DP_opensm= pthread _DP_osmvendor= ibumad pthread .endif # Define special cases LDADD_supcplusplus= -lsupc++ LIBATF_C= ${LIBDESTDIR}${LIBDIR_BASE}/libprivateatf-c.a LIBATF_CXX= ${LIBDESTDIR}${LIBDIR_BASE}/libprivateatf-c++.a LDADD_atf_c= -lprivateatf-c LDADD_atf_cxx= -lprivateatf-c++ LIBGMOCK= ${LIBDESTDIR}${LIBDIR_BASE}/libprivategmock.a LIBGMOCK_MAIN= ${LIBDESTDIR}${LIBDIR_BASE}/libprivategmock_main.a LIBGTEST= ${LIBDESTDIR}${LIBDIR_BASE}/libprivategtest.a LIBGTEST_MAIN= ${LIBDESTDIR}${LIBDIR_BASE}/libprivategtest_main.a LDADD_gmock= -lprivategmock LDADD_gtest= -lprivategtest LDADD_gmock_main= -lprivategmock_main LDADD_gtest_main= -lprivategtest_main .for _l in ${_PRIVATELIBS} LIB${_l:tu}?= ${LIBDESTDIR}${LIBDIR_BASE}/libprivate${_l}.a .endfor .if ${MK_PIE} != "no" PIE_SUFFIX= _pie .endif .for _l in ${_LIBRARIES} .if ${_INTERNALLIBS:M${_l}} || !defined(SYSROOT) LDADD_${_l}_L+= -L${LIB${_l:tu}DIR} .endif DPADD_${_l}?= ${LIB${_l:tu}} .if ${_PRIVATELIBS:M${_l}} LDADD_${_l}?= -lprivate${_l} .elif ${_INTERNALLIBS:M${_l}} LDADD_${_l}?= ${LDADD_${_l}_L} -l${_l:S/${PIE_SUFFIX}//}${PIE_SUFFIX} .else LDADD_${_l}?= ${LDADD_${_l}_L} -l${_l} .endif # Add in all dependencies for static linkage. # Bootstrapping from non-FreeBSD needs special handling, since it overrides # NO_SHARED back to yes despite only building static versions of bootstrap # libraries (see tools/build/mk/Makefile.boot.pre). .if defined(_DP_${_l}) && (${_INTERNALLIBS:M${_l}} || \ (defined(NO_SHARED) && ${NO_SHARED:tl} != "no") || \ (defined(BOOTSTRAPPING) && ${.MAKE.OS} != "FreeBSD")) .for _d in ${_DP_${_l}} DPADD_${_l}+= ${DPADD_${_d}} LDADD_${_l}+= ${LDADD_${_d}} .endfor .endif .endfor # These are special cases where the library is broken and anything that uses # it needs to add more dependencies. Broken usually means that it has a # cyclic dependency and cannot link its own dependencies. This is bad, please # fix the library instead. # Unless the library itself is broken then the proper place to define # dependencies is _DP_* above. # libatf-c++ exposes libatf-c abi hence we need to explicit link to atf_c for # atf_cxx DPADD_atf_cxx+= ${DPADD_atf_c} LDADD_atf_cxx+= ${LDADD_atf_c} DPADD_gmock+= ${DPADD_gtest} LDADD_gmock+= ${LDADD_gtest} DPADD_gmock_main+= ${DPADD_gmock} LDADD_gmock_main+= ${LDADD_gmock} DPADD_gtest_main+= ${DPADD_gtest} LDADD_gtest_main+= ${LDADD_gtest} # Detect LDADD/DPADD that should be LIBADD, before modifying LDADD here. _BADLDADD= .for _l in ${LDADD:M-l*:N-l*/*:C,^-l,,} .if ${_LIBRARIES:M${_l}} && !${_PRIVATELIBS:M${_l}} _BADLDADD+= ${_l} .endif .endfor .if !empty(_BADLDADD) .error ${.CURDIR}: These libraries should be LIBADD+=foo rather than DPADD/LDADD+=-lfoo: ${_BADLDADD} .endif .for _l in ${LIBADD} DPADD+= ${DPADD_${_l}} LDADD+= ${LDADD_${_l}} .endfor _LIB_OBJTOP?= ${OBJTOP} # INTERNALLIB definitions. LIBELFTCDIR= ${_LIB_OBJTOP}/lib/libelftc LIBELFTC?= ${LIBELFTCDIR}/libelftc${PIE_SUFFIX}.a LIBLUADIR= ${_LIB_OBJTOP}/lib/liblua LIBLUA?= ${LIBLUADIR}/liblua${PIE_SUFFIX}.a LIBLUTOKDIR= ${_LIB_OBJTOP}/lib/liblutok LIBLUTOK?= ${LIBLUTOKDIR}/liblutok${PIE_SUFFIX}.a LIBPEDIR= ${_LIB_OBJTOP}/lib/libpe LIBPE?= ${LIBPEDIR}/libpe${PIE_SUFFIX}.a LIBOPENBSDDIR= ${_LIB_OBJTOP}/lib/libopenbsd LIBOPENBSD?= ${LIBOPENBSDDIR}/libopenbsd${PIE_SUFFIX}.a LIBSMDIR= ${_LIB_OBJTOP}/lib/libsm LIBSM?= ${LIBSMDIR}/libsm${PIE_SUFFIX}.a LIBSMDBDIR= ${_LIB_OBJTOP}/lib/libsmdb LIBSMDB?= ${LIBSMDBDIR}/libsmdb${PIE_SUFFIX}.a LIBSMUTILDIR= ${_LIB_OBJTOP}/lib/libsmutil LIBSMUTIL?= ${LIBSMUTILDIR}/libsmutil${PIE_SUFFIX}.a LIBNETBSDDIR?= ${_LIB_OBJTOP}/lib/libnetbsd LIBNETBSD?= ${LIBNETBSDDIR}/libnetbsd${PIE_SUFFIX}.a LIBVERSDIR?= ${_LIB_OBJTOP}/kerberos5/lib/libvers LIBVERS?= ${LIBVERSDIR}/libvers${PIE_SUFFIX}.a LIBSLDIR= ${_LIB_OBJTOP}/kerberos5/lib/libsl LIBSL?= ${LIBSLDIR}/libsl${PIE_SUFFIX}.a LIBIFCONFIGDIR= ${_LIB_OBJTOP}/lib/libifconfig LIBIFCONFIG?= ${LIBIFCONFIGDIR}/libifconfig${PIE_SUFFIX}.a LIBIPFDIR= ${_LIB_OBJTOP}/sbin/ipf/libipf LIBIPF?= ${LIBIPFDIR}/libipf${PIE_SUFFIX}.a +LIBISCSIUTILDIR= ${_LIB_OBJTOP}/lib/libiscsiutil +LIBISCSIUTIL?= ${LIBISCSIUTILDIR}/libiscsiutil${PIE_SUFFIX}.a + LIBTELNETDIR= ${_LIB_OBJTOP}/lib/libtelnet LIBTELNET?= ${LIBTELNETDIR}/libtelnet${PIE_SUFFIX}.a LIBCRONDIR= ${_LIB_OBJTOP}/usr.sbin/cron/lib LIBCRON?= ${LIBCRONDIR}/libcron${PIE_SUFFIX}.a LIBNTPDIR= ${_LIB_OBJTOP}/usr.sbin/ntp/libntp LIBNTP?= ${LIBNTPDIR}/libntp${PIE_SUFFIX}.a LIBNTPEVENTDIR= ${_LIB_OBJTOP}/usr.sbin/ntp/libntpevent LIBNTPEVENT?= ${LIBNTPEVENTDIR}/libntpevent${PIE_SUFFIX}.a LIBOPTSDIR= ${_LIB_OBJTOP}/usr.sbin/ntp/libopts LIBOPTS?= ${LIBOPTSDIR}/libopts${PIE_SUFFIX}.a LIBPARSEDIR= ${_LIB_OBJTOP}/usr.sbin/ntp/libparse LIBPARSE?= ${LIBPARSEDIR}/libparse${PIE_SUFFIX}.a LIBPFCTL= ${_LIB_OBJTOP}/lib/libpfctl LIBPFCTL?= ${LIBPFCTLDIR}/libpfctl${PIE_SUFFIX}.a LIBLPRDIR= ${_LIB_OBJTOP}/usr.sbin/lpr/common_source LIBLPR?= ${LIBLPRDIR}/liblpr${PIE_SUFFIX}.a LIBFIFOLOGDIR= ${_LIB_OBJTOP}/usr.sbin/fifolog/lib LIBFIFOLOG?= ${LIBFIFOLOGDIR}/libfifolog${PIE_SUFFIX}.a LIBBSNMPTOOLSDIR= ${_LIB_OBJTOP}/usr.sbin/bsnmpd/tools/libbsnmptools LIBBSNMPTOOLS?= ${LIBBSNMPTOOLSDIR}/libbsnmptools${PIE_SUFFIX}.a LIBBE?= ${LIBBEDIR}/libbe${PIE_SUFFIX}.a LIBPMCSTATDIR= ${_LIB_OBJTOP}/lib/libpmcstat LIBPMCSTAT?= ${LIBPMCSTATDIR}/libpmcstat${PIE_SUFFIX}.a LIBWPAAPDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/ap LIBWPAAP?= ${LIBWPAAPDIR}/libwpaap${PIE_SUFFIX}.a LIBWPACOMMONDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/common LIBWPACOMMON?= ${LIBWPACOMMONDIR}/libwpacommon${PIE_SUFFIX}.a LIBWPACRYPTODIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/crypto LIBWPACRYPTO?= ${LIBWPACRYPTODIR}/libwpacrypto${PIE_SUFFIX}.a LIBWPADRIVERSDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/drivers LIBWPADRIVERS?= ${LIBWPADRIVERSDIR}/libwpadrivers${PIE_SUFFIX}.a LIBWPAEAP_COMMONDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/eap_common LIBWPAEAP_COMMON?= ${LIBWPAEAP_COMMONDIR}/libwpaeap_common${PIE_SUFFIX}.a LIBWPAEAP_PEERDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/eap_peer LIBWPAEAP_PEER?= ${LIBWPAEAP_PEERDIR}/libwpaeap_peer${PIE_SUFFIX}.a LIBWPAEAP_SERVERDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/eap_server LIBWPAEAP_SERVER?= ${LIBWPAEAP_SERVERDIR}/libwpaeap_server${PIE_SUFFIX}.a LIBWPAEAPOL_AUTHDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/eapol_auth LIBWPAEAPOL_AUTH?= ${LIBWPAEAPOL_AUTHDIR}/libwpaeapol_auth${PIE_SUFFIX}.a LIBWPAEAPOL_SUPPDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/eapol_supp LIBWPAEAPOL_SUPP?= ${LIBWPAEAPOL_SUPPDIR}/libwpaeapol_supp${PIE_SUFFIX}.a LIBWPAL2_PACKETDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/l2_packet LIBWPAL2_PACKET?= ${LIBWPAL2_PACKETDIR}/libwpal2_packet${PIE_SUFFIX}.a LIBWPARADIUSDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/radius LIBWPARADIUS?= ${LIBWPARADIUSDIR}/libwparadius${PIE_SUFFIX}.a LIBWPARSN_SUPPDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/rsn_supp LIBWPARSN_SUPP?= ${LIBWPARSN_SUPPDIR}/libwparsn_supp${PIE_SUFFIX}.a LIBWPATLSDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/tls LIBWPATLS?= ${LIBWPATLSDIR}/libwpatls${PIE_SUFFIX}.a LIBWPAUTILSDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/utils LIBWPAUTILS?= ${LIBWPAUTILSDIR}/libwpautils${PIE_SUFFIX}.a LIBWPAWPSDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/wps LIBWPAWPS?= ${LIBWPAWPSDIR}/libwpawps${PIE_SUFFIX}.a LIBC_NOSSP_PICDIR= ${_LIB_OBJTOP}/lib/libc LIBC_NOSSP_PIC?= ${LIBC_NOSSP_PICDIR}/libc_nossp_pic.a # Define a directory for each library. This is useful for adding -L in when # not using a --sysroot or for meta mode bootstrapping when there is no # Makefile.depend. These are sorted by directory. LIBAVLDIR= ${OBJTOP}/cddl/lib/libavl LIBCTFDIR= ${OBJTOP}/cddl/lib/libctf LIBDTRACEDIR= ${OBJTOP}/cddl/lib/libdtrace LIBICPDIR= ${OBJTOP}/cddl/lib/libicp LIBNVPAIRDIR= ${OBJTOP}/cddl/lib/libnvpair LIBUMEMDIR= ${OBJTOP}/cddl/lib/libumem LIBUUTILDIR= ${OBJTOP}/cddl/lib/libuutil LIBZFSDIR= ${OBJTOP}/cddl/lib/libzfs LIBZFS_COREDIR= ${OBJTOP}/cddl/lib/libzfs_core LIBZFSBOOTENVDIR= ${OBJTOP}/cddl/lib/libzfsbootenv LIBZPOOLDIR= ${OBJTOP}/cddl/lib/libzpool LIBZUTILDIR= ${OBJTOP}/cddl/lib/libzutil LIBTPOOLDIR= ${OBJTOP}/cddl/lib/libtpool # OFED support LIBCXGB4DIR= ${OBJTOP}/lib/ofed/libcxgb4 LIBIBCMDIR= ${OBJTOP}/lib/ofed/libibcm LIBIBMADDIR= ${OBJTOP}/lib/ofed/libibmad LIBIBNETDISCDIR=${OBJTOP}/lib/ofed/libibnetdisc LIBIBUMADDIR= ${OBJTOP}/lib/ofed/libibumad LIBIBVERBSDIR= ${OBJTOP}/lib/ofed/libibverbs LIBMLX4DIR= ${OBJTOP}/lib/ofed/libmlx4 LIBMLX5DIR= ${OBJTOP}/lib/ofed/libmlx5 LIBRDMACMDIR= ${OBJTOP}/lib/ofed/librdmacm LIBOSMCOMPDIR= ${OBJTOP}/lib/ofed/complib LIBOPENSMDIR= ${OBJTOP}/lib/ofed/libopensm LIBOSMVENDORDIR=${OBJTOP}/lib/ofed/libvendor LIBDIALOGDIR= ${OBJTOP}/gnu/lib/libdialog LIBSSPDIR= ${OBJTOP}/lib/libssp LIBSSP_NONSHAREDDIR= ${OBJTOP}/lib/libssp_nonshared LIBASN1DIR= ${OBJTOP}/kerberos5/lib/libasn1 LIBGSSAPI_KRB5DIR= ${OBJTOP}/kerberos5/lib/libgssapi_krb5 LIBGSSAPI_NTLMDIR= ${OBJTOP}/kerberos5/lib/libgssapi_ntlm LIBGSSAPI_SPNEGODIR= ${OBJTOP}/kerberos5/lib/libgssapi_spnego LIBHDBDIR= ${OBJTOP}/kerberos5/lib/libhdb LIBHEIMBASEDIR= ${OBJTOP}/kerberos5/lib/libheimbase LIBHEIMIPCCDIR= ${OBJTOP}/kerberos5/lib/libheimipcc LIBHEIMIPCSDIR= ${OBJTOP}/kerberos5/lib/libheimipcs LIBHEIMNTLMDIR= ${OBJTOP}/kerberos5/lib/libheimntlm LIBHX509DIR= ${OBJTOP}/kerberos5/lib/libhx509 LIBKADM5CLNTDIR= ${OBJTOP}/kerberos5/lib/libkadm5clnt LIBKADM5SRVDIR= ${OBJTOP}/kerberos5/lib/libkadm5srv LIBKAFS5DIR= ${OBJTOP}/kerberos5/lib/libkafs5 LIBKDCDIR= ${OBJTOP}/kerberos5/lib/libkdc LIBKRB5DIR= ${OBJTOP}/kerberos5/lib/libkrb5 LIBROKENDIR= ${OBJTOP}/kerberos5/lib/libroken LIBWINDDIR= ${OBJTOP}/kerberos5/lib/libwind LIBATF_CDIR= ${OBJTOP}/lib/atf/libatf-c LIBATF_CXXDIR= ${OBJTOP}/lib/atf/libatf-c++ LIBGMOCKDIR= ${OBJTOP}/lib/googletest/gmock LIBGMOCK_MAINDIR= ${OBJTOP}/lib/googletest/gmock_main LIBGTESTDIR= ${OBJTOP}/lib/googletest/gtest LIBGTEST_MAINDIR= ${OBJTOP}/lib/googletest/gtest_main LIBALIASDIR= ${OBJTOP}/lib/libalias/libalias LIBBLACKLISTDIR= ${OBJTOP}/lib/libblacklist LIBBLOCKSRUNTIMEDIR= ${OBJTOP}/lib/libblocksruntime LIBBSNMPDIR= ${OBJTOP}/lib/libbsnmp/libbsnmp LIBCASPERDIR= ${OBJTOP}/lib/libcasper/libcasper LIBCAP_DNSDIR= ${OBJTOP}/lib/libcasper/services/cap_dns LIBCAP_GRPDIR= ${OBJTOP}/lib/libcasper/services/cap_grp LIBCAP_NETDIR= ${OBJTOP}/lib/libcasper/services/cap_net LIBCAP_PWDDIR= ${OBJTOP}/lib/libcasper/services/cap_pwd LIBCAP_SYSCTLDIR= ${OBJTOP}/lib/libcasper/services/cap_sysctl LIBCAP_SYSLOGDIR= ${OBJTOP}/lib/libcasper/services/cap_syslog LIBCBORDIR= ${OBJTOP}/lib/libcbor LIBBSDXMLDIR= ${OBJTOP}/lib/libexpat LIBFIDO2DIR= ${OBJTOP}/lib/libfido2 LIBKVMDIR= ${OBJTOP}/lib/libkvm LIBPTHREADDIR= ${OBJTOP}/lib/libthr LIBMDIR= ${OBJTOP}/lib/msun LIBFORMWDIR= ${OBJTOP}/lib/ncurses/form LIBMENUWDIR= ${OBJTOP}/lib/ncurses/menu LIBNCURSESWDIR= ${OBJTOP}/lib/ncurses/ncurses LIBTINFOWDIR= ${OBJTOP}/lib/ncurses/tinfo LIBPANELWDIR= ${OBJTOP}/lib/ncurses/panel LIBCRYPTODIR= ${OBJTOP}/secure/lib/libcrypto LIBSPLDIR= ${OBJTOP}/cddl/lib/libspl LIBSSHDIR= ${OBJTOP}/secure/lib/libssh LIBSSLDIR= ${OBJTOP}/secure/lib/libssl LIBTEKENDIR= ${OBJTOP}/sys/teken/libteken LIBEGACYDIR= ${OBJTOP}/tools/build LIBLNDIR= ${OBJTOP}/usr.bin/lex/lib LIBTERMCAPWDIR= ${LIBTINFOWDIR} # Default other library directories to lib/libNAME. .for lib in ${_LIBRARIES} LIB${lib:tu}DIR?= ${OBJTOP}/lib/lib${lib} .endfor # Validate that listed LIBADD are valid. .for _l in ${LIBADD} .if empty(_LIBRARIES:M${_l}) _BADLIBADD+= ${_l} .endif .endfor .if !empty(_BADLIBADD) .error ${.CURDIR}: Invalid LIBADD used which may need to be added to ${_this:T}: ${_BADLIBADD} .endif # Sanity check that libraries are defined here properly when building them. .if defined(LIB) && ${_LIBRARIES:M${LIB}} != "" .if !empty(LIBADD) && \ (!defined(_DP_${LIB}) || ${LIBADD:O:u} != ${_DP_${LIB}:O:u}) .error ${.CURDIR}: Missing or incorrect _DP_${LIB} entry in ${_this:T}. Should match LIBADD for ${LIB} ('${LIBADD}' vs '${_DP_${LIB}}') .endif # Note that OBJTOP is not yet defined here but for the purpose of the check # it is fine as it resolves to the SRC directory. .if !defined(LIB${LIB:tu}DIR) || !exists(${SRCTOP}/${LIB${LIB:tu}DIR:S,^${OBJTOP}/,,}) .error ${.CURDIR}: Missing or incorrect value for LIB${LIB:tu}DIR in ${_this:T}: ${LIB${LIB:tu}DIR:S,^${OBJTOP}/,,} .endif .if ${_INTERNALLIBS:M${LIB}} != "" && !defined(LIB${LIB:tu}) .error ${.CURDIR}: Missing value for LIB${LIB:tu} in ${_this:T}. Likely should be: LIB${LIB:tu}?= $${LIB${LIB:tu}DIR}/lib${LIB}.a .endif .endif .endif # !target(____) diff --git a/usr.sbin/ctld/Makefile b/usr.sbin/ctld/Makefile index ec207f024ab1..5f80ba026204 100644 --- a/usr.sbin/ctld/Makefile +++ b/usr.sbin/ctld/Makefile @@ -1,30 +1,31 @@ # $FreeBSD$ .include CFLAGS+=-I${SRCTOP}/contrib/libucl/include .PATH: ${SRCTOP}/contrib/libucl/include PACKAGE= iscsi PROG= ctld -SRCS= chap.c ctld.c discovery.c isns.c kernel.c keys.c log.c -SRCS+= login.c parse.y pdu.c token.l y.tab.h uclparse.c +SRCS= ctld.c discovery.c isns.c kernel.c +SRCS+= login.c parse.y token.l y.tab.h uclparse.c CFLAGS+= -I${.CURDIR} CFLAGS+= -I${SRCTOP}/sys CFLAGS+= -I${SRCTOP}/sys/cam/ctl CFLAGS+= -I${SRCTOP}/sys/dev/iscsi +CFLAGS+= -I${SRCTOP}/lib/libiscsiutil #CFLAGS+= -DICL_KERNEL_PROXY MAN= ctld.8 ctl.conf.5 -LIBADD= bsdxml md sbuf util ucl m nv +LIBADD= bsdxml iscsiutil md sbuf util ucl m nv YFLAGS+= -v CLEANFILES= y.tab.c y.tab.h y.output NO_WMISSING_VARIABLE_DECLARATIONS= .if ${MK_ISCSI} != "no" CFLAGS+= -DWANT_ISCSI .endif .include diff --git a/usr.sbin/ctld/ctld.c b/usr.sbin/ctld/ctld.c index c37181ff00d0..3f4bca632512 100644 --- a/usr.sbin/ctld/ctld.c +++ b/usr.sbin/ctld/ctld.c @@ -1,2862 +1,2898 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 The FreeBSD Foundation * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ctld.h" #include "isns.h" +static bool timed_out(void); +#ifdef ICL_KERNEL_PROXY +static void pdu_receive_proxy(struct pdu *pdu); +static void pdu_send_proxy(struct pdu *pdu); +#endif /* ICL_KERNEL_PROXY */ +static void pdu_fail(const struct connection *conn, const char *reason); + bool proxy_mode = false; static volatile bool sighup_received = false; static volatile bool sigterm_received = false; static volatile bool sigalrm_received = false; static int nchildren = 0; static uint16_t last_portal_group_tag = 0xff; +static struct connection_ops conn_ops = { + .timed_out = timed_out, +#ifdef ICL_KERNEL_PROXY + .pdu_receive_proxy = pdu_receive_proxy, + .pdu_send_proxy = pdu_send_proxy, +#endif + .fail = pdu_fail, +}; + static void usage(void) { fprintf(stderr, "usage: ctld [-d][-u][-f config-file]\n"); fprintf(stderr, " ctld -t [-u][-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_luns); TAILQ_INIT(&conf->conf_targets); TAILQ_INIT(&conf->conf_auth_groups); TAILQ_INIT(&conf->conf_ports); TAILQ_INIT(&conf->conf_portal_groups); TAILQ_INIT(&conf->conf_pports); TAILQ_INIT(&conf->conf_isns); conf->conf_isns_period = 900; conf->conf_isns_timeout = 5; conf->conf_debug = 0; conf->conf_timeout = 60; conf->conf_maxproc = 30; return (conf); } void conf_delete(struct conf *conf) { struct lun *lun, *ltmp; struct target *targ, *tmp; struct auth_group *ag, *cagtmp; struct portal_group *pg, *cpgtmp; struct pport *pp, *pptmp; struct isns *is, *istmp; assert(conf->conf_pidfh == NULL); TAILQ_FOREACH_SAFE(lun, &conf->conf_luns, l_next, ltmp) lun_delete(lun); 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); TAILQ_FOREACH_SAFE(pp, &conf->conf_pports, pp_next, pptmp) pport_delete(pp); TAILQ_FOREACH_SAFE(is, &conf->conf_isns, i_next, istmp) isns_delete(is); assert(TAILQ_EMPTY(&conf->conf_ports)); 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(const 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); } 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_name); } 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 12 characters " "long", auth->a_user, auth->a_auth_group->ag_target->t_name); } if (auth->a_mutual_secret != NULL) { len = strlen(auth->a_mutual_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_name); } 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 12 characters long", auth->a_user, auth->a_auth_group->ag_target->t_name); } } } 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_name); 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_name); 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); } const struct auth_name * auth_name_new(struct auth_group *ag, const char *name) { struct auth_name *an; an = calloc(1, sizeof(*an)); if (an == NULL) log_err(1, "calloc"); an->an_auth_group = ag; an->an_initiator_name = checked_strdup(name); TAILQ_INSERT_TAIL(&ag->ag_names, an, an_next); return (an); } static void auth_name_delete(struct auth_name *an) { TAILQ_REMOVE(&an->an_auth_group->ag_names, an, an_next); free(an->an_initiator_name); free(an); } bool auth_name_defined(const struct auth_group *ag) { if (TAILQ_EMPTY(&ag->ag_names)) return (false); return (true); } const struct auth_name * auth_name_find(const struct auth_group *ag, const char *name) { const struct auth_name *auth_name; TAILQ_FOREACH(auth_name, &ag->ag_names, an_next) { if (strcmp(auth_name->an_initiator_name, name) == 0) return (auth_name); } return (NULL); } int auth_name_check(const struct auth_group *ag, const char *initiator_name) { if (!auth_name_defined(ag)) return (0); if (auth_name_find(ag, initiator_name) == NULL) return (1); return (0); } const struct auth_portal * auth_portal_new(struct auth_group *ag, const char *portal) { struct auth_portal *ap; char *net, *mask, *str, *tmp; int len, dm, m; ap = calloc(1, sizeof(*ap)); if (ap == NULL) log_err(1, "calloc"); ap->ap_auth_group = ag; ap->ap_initiator_portal = checked_strdup(portal); mask = str = checked_strdup(portal); net = strsep(&mask, "/"); if (net[0] == '[') net++; len = strlen(net); if (len == 0) goto error; if (net[len - 1] == ']') net[len - 1] = 0; if (strchr(net, ':') != NULL) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ap->ap_sa; sin6->sin6_len = sizeof(*sin6); sin6->sin6_family = AF_INET6; if (inet_pton(AF_INET6, net, &sin6->sin6_addr) <= 0) goto error; dm = 128; } else { struct sockaddr_in *sin = (struct sockaddr_in *)&ap->ap_sa; sin->sin_len = sizeof(*sin); sin->sin_family = AF_INET; if (inet_pton(AF_INET, net, &sin->sin_addr) <= 0) goto error; dm = 32; } if (mask != NULL) { m = strtol(mask, &tmp, 0); if (m < 0 || m > dm || tmp[0] != 0) goto error; } else m = dm; ap->ap_mask = m; free(str); TAILQ_INSERT_TAIL(&ag->ag_portals, ap, ap_next); return (ap); error: free(str); free(ap); log_warnx("incorrect initiator portal \"%s\"", portal); return (NULL); } static void auth_portal_delete(struct auth_portal *ap) { TAILQ_REMOVE(&ap->ap_auth_group->ag_portals, ap, ap_next); free(ap->ap_initiator_portal); free(ap); } bool auth_portal_defined(const struct auth_group *ag) { if (TAILQ_EMPTY(&ag->ag_portals)) return (false); return (true); } const struct auth_portal * auth_portal_find(const struct auth_group *ag, const struct sockaddr_storage *ss) { const struct auth_portal *ap; const uint8_t *a, *b; int i; uint8_t bmask; TAILQ_FOREACH(ap, &ag->ag_portals, ap_next) { if (ap->ap_sa.ss_family != ss->ss_family) continue; if (ss->ss_family == AF_INET) { a = (const uint8_t *) &((const struct sockaddr_in *)ss)->sin_addr; b = (const uint8_t *) &((const struct sockaddr_in *)&ap->ap_sa)->sin_addr; } else { a = (const uint8_t *) &((const struct sockaddr_in6 *)ss)->sin6_addr; b = (const uint8_t *) &((const struct sockaddr_in6 *)&ap->ap_sa)->sin6_addr; } for (i = 0; i < ap->ap_mask / 8; i++) { if (a[i] != b[i]) goto next; } if (ap->ap_mask % 8) { bmask = 0xff << (8 - (ap->ap_mask % 8)); if ((a[i] & bmask) != (b[i] & bmask)) goto next; } return (ap); next: ; } return (NULL); } int auth_portal_check(const struct auth_group *ag, const struct sockaddr_storage *sa) { if (!auth_portal_defined(ag)) return (0); if (auth_portal_find(ag, sa) == NULL) return (1); return (0); } 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); TAILQ_INIT(&ag->ag_names); TAILQ_INIT(&ag->ag_portals); 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, *auth_tmp; struct auth_name *auth_name, *auth_name_tmp; struct auth_portal *auth_portal, *auth_portal_tmp; TAILQ_REMOVE(&ag->ag_conf->conf_auth_groups, ag, ag_next); TAILQ_FOREACH_SAFE(auth, &ag->ag_auths, a_next, auth_tmp) auth_delete(auth); TAILQ_FOREACH_SAFE(auth_name, &ag->ag_names, an_next, auth_name_tmp) auth_name_delete(auth_name); TAILQ_FOREACH_SAFE(auth_portal, &ag->ag_portals, ap_next, auth_portal_tmp) auth_portal_delete(auth_portal); free(ag->ag_name); free(ag); } struct auth_group * auth_group_find(const 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); } int auth_group_set_type(struct auth_group *ag, const char *str) { int type; if (strcmp(str, "none") == 0) { type = AG_TYPE_NO_AUTHENTICATION; } else if (strcmp(str, "deny") == 0) { type = AG_TYPE_DENY; } else if (strcmp(str, "chap") == 0) { type = AG_TYPE_CHAP; } else if (strcmp(str, "chap-mutual") == 0) { type = AG_TYPE_CHAP_MUTUAL; } else { if (ag->ag_name != NULL) log_warnx("invalid auth-type \"%s\" for auth-group " "\"%s\"", str, ag->ag_name); else log_warnx("invalid auth-type \"%s\" for target " "\"%s\"", str, ag->ag_target->t_name); return (1); } if (ag->ag_type != AG_TYPE_UNKNOWN && ag->ag_type != type) { if (ag->ag_name != NULL) { log_warnx("cannot set auth-type to \"%s\" for " "auth-group \"%s\"; already has a different " "type", str, ag->ag_name); } else { log_warnx("cannot set auth-type to \"%s\" for target " "\"%s\"; already has a different type", str, ag->ag_target->t_name); } return (1); } ag->ag_type = type; return (0); } 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); if (portal->p_ai != NULL) 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; 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_options); TAILQ_INIT(&pg->pg_portals); TAILQ_INIT(&pg->pg_ports); pg->pg_conf = conf; pg->pg_tag = 0; /* Assigned later in conf_apply(). */ pg->pg_dscp = -1; pg->pg_pcp = -1; TAILQ_INSERT_TAIL(&conf->conf_portal_groups, pg, pg_next); return (pg); } void portal_group_delete(struct portal_group *pg) { struct portal *portal, *tmp; struct port *port, *tport; struct option *o, *otmp; TAILQ_FOREACH_SAFE(port, &pg->pg_ports, p_pgs, tport) port_delete(port); TAILQ_REMOVE(&pg->pg_conf->conf_portal_groups, pg, pg_next); TAILQ_FOREACH_SAFE(portal, &pg->pg_portals, p_next, tmp) portal_delete(portal); TAILQ_FOREACH_SAFE(o, &pg->pg_options, o_next, otmp) option_delete(&pg->pg_options, o); free(pg->pg_name); free(pg->pg_offload); free(pg->pg_redirection); free(pg); } struct portal_group * portal_group_find(const 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); } static int parse_addr_port(char *arg, const char *def_port, struct addrinfo **ai) { struct addrinfo hints; char *str, *addr, *ch; const char *port; int error, colons = 0; str = arg = strdup(arg); if (arg[0] == '[') { /* * IPv6 address in square brackets, perhaps with port. */ arg++; addr = strsep(&arg, "]"); if (arg == NULL) { free(str); return (1); } if (arg[0] == '\0') { port = def_port; } else if (arg[0] == ':') { port = arg + 1; } else { free(str); 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 = def_port; } else { addr = strsep(&arg, ":"); if (arg == NULL) port = def_port; 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); free(str); return ((error != 0) ? 1 : 0); } int portal_group_add_listen(struct portal_group *pg, const char *value, bool iser) { struct portal *portal; portal = portal_new(pg); portal->p_listen = checked_strdup(value); portal->p_iser = iser; if (parse_addr_port(portal->p_listen, "3260", &portal->p_ai)) { log_warnx("invalid listen address %s", portal->p_listen); portal_delete(portal); return (1); } /* * XXX: getaddrinfo(3) may return multiple addresses; we should turn * those into multiple portals. */ return (0); } int isns_new(struct conf *conf, const char *addr) { struct isns *isns; isns = calloc(1, sizeof(*isns)); if (isns == NULL) log_err(1, "calloc"); isns->i_conf = conf; TAILQ_INSERT_TAIL(&conf->conf_isns, isns, i_next); isns->i_addr = checked_strdup(addr); if (parse_addr_port(isns->i_addr, "3205", &isns->i_ai)) { log_warnx("invalid iSNS address %s", isns->i_addr); isns_delete(isns); return (1); } /* * XXX: getaddrinfo(3) may return multiple addresses; we should turn * those into multiple servers. */ return (0); } void isns_delete(struct isns *isns) { TAILQ_REMOVE(&isns->i_conf->conf_isns, isns, i_next); free(isns->i_addr); if (isns->i_ai != NULL) freeaddrinfo(isns->i_ai); free(isns); } static int isns_do_connect(struct isns *isns) { int s; s = socket(isns->i_ai->ai_family, isns->i_ai->ai_socktype, isns->i_ai->ai_protocol); if (s < 0) { log_warn("socket(2) failed for %s", isns->i_addr); return (-1); } if (connect(s, isns->i_ai->ai_addr, isns->i_ai->ai_addrlen)) { log_warn("connect(2) failed for %s", isns->i_addr); close(s); return (-1); } return(s); } static int isns_do_register(struct isns *isns, int s, const char *hostname) { struct conf *conf = isns->i_conf; struct target *target; struct portal *portal; struct portal_group *pg; struct port *port; struct isns_req *req; int res = 0; uint32_t error; req = isns_req_create(ISNS_FUNC_DEVATTRREG, ISNS_FLAG_CLIENT); isns_req_add_str(req, 32, TAILQ_FIRST(&conf->conf_targets)->t_name); isns_req_add_delim(req); isns_req_add_str(req, 1, hostname); isns_req_add_32(req, 2, 2); /* 2 -- iSCSI */ isns_req_add_32(req, 6, conf->conf_isns_period); TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { if (pg->pg_unassigned) continue; TAILQ_FOREACH(portal, &pg->pg_portals, p_next) { isns_req_add_addr(req, 16, portal->p_ai); isns_req_add_port(req, 17, portal->p_ai); } } TAILQ_FOREACH(target, &conf->conf_targets, t_next) { isns_req_add_str(req, 32, target->t_name); isns_req_add_32(req, 33, 1); /* 1 -- Target*/ if (target->t_alias != NULL) isns_req_add_str(req, 34, target->t_alias); TAILQ_FOREACH(port, &target->t_ports, p_ts) { if ((pg = port->p_portal_group) == NULL) continue; isns_req_add_32(req, 51, pg->pg_tag); TAILQ_FOREACH(portal, &pg->pg_portals, p_next) { isns_req_add_addr(req, 49, portal->p_ai); isns_req_add_port(req, 50, portal->p_ai); } } } res = isns_req_send(s, req); if (res < 0) { log_warn("send(2) failed for %s", isns->i_addr); goto quit; } res = isns_req_receive(s, req); if (res < 0) { log_warn("receive(2) failed for %s", isns->i_addr); goto quit; } error = isns_req_get_status(req); if (error != 0) { log_warnx("iSNS register error %d for %s", error, isns->i_addr); res = -1; } quit: isns_req_free(req); return (res); } static int isns_do_check(struct isns *isns, int s, const char *hostname) { struct conf *conf = isns->i_conf; struct isns_req *req; int res = 0; uint32_t error; req = isns_req_create(ISNS_FUNC_DEVATTRQRY, ISNS_FLAG_CLIENT); isns_req_add_str(req, 32, TAILQ_FIRST(&conf->conf_targets)->t_name); isns_req_add_str(req, 1, hostname); isns_req_add_delim(req); isns_req_add(req, 2, 0, NULL); res = isns_req_send(s, req); if (res < 0) { log_warn("send(2) failed for %s", isns->i_addr); goto quit; } res = isns_req_receive(s, req); if (res < 0) { log_warn("receive(2) failed for %s", isns->i_addr); goto quit; } error = isns_req_get_status(req); if (error != 0) { log_warnx("iSNS check error %d for %s", error, isns->i_addr); res = -1; } quit: isns_req_free(req); return (res); } static int isns_do_deregister(struct isns *isns, int s, const char *hostname) { struct conf *conf = isns->i_conf; struct isns_req *req; int res = 0; uint32_t error; req = isns_req_create(ISNS_FUNC_DEVDEREG, ISNS_FLAG_CLIENT); isns_req_add_str(req, 32, TAILQ_FIRST(&conf->conf_targets)->t_name); isns_req_add_delim(req); isns_req_add_str(req, 1, hostname); res = isns_req_send(s, req); if (res < 0) { log_warn("send(2) failed for %s", isns->i_addr); goto quit; } res = isns_req_receive(s, req); if (res < 0) { log_warn("receive(2) failed for %s", isns->i_addr); goto quit; } error = isns_req_get_status(req); if (error != 0) { log_warnx("iSNS deregister error %d for %s", error, isns->i_addr); res = -1; } quit: isns_req_free(req); return (res); } void isns_register(struct isns *isns, struct isns *oldisns) { struct conf *conf = isns->i_conf; int error, s; char hostname[256]; if (TAILQ_EMPTY(&conf->conf_targets) || TAILQ_EMPTY(&conf->conf_portal_groups)) return; set_timeout(conf->conf_isns_timeout, false); s = isns_do_connect(isns); if (s < 0) { set_timeout(0, false); return; } error = gethostname(hostname, sizeof(hostname)); if (error != 0) log_err(1, "gethostname"); if (oldisns == NULL || TAILQ_EMPTY(&oldisns->i_conf->conf_targets)) oldisns = isns; isns_do_deregister(oldisns, s, hostname); isns_do_register(isns, s, hostname); close(s); set_timeout(0, false); } void isns_check(struct isns *isns) { struct conf *conf = isns->i_conf; int error, s, res; char hostname[256]; if (TAILQ_EMPTY(&conf->conf_targets) || TAILQ_EMPTY(&conf->conf_portal_groups)) return; set_timeout(conf->conf_isns_timeout, false); s = isns_do_connect(isns); if (s < 0) { set_timeout(0, false); return; } error = gethostname(hostname, sizeof(hostname)); if (error != 0) log_err(1, "gethostname"); res = isns_do_check(isns, s, hostname); if (res < 0) { isns_do_deregister(isns, s, hostname); isns_do_register(isns, s, hostname); } close(s); set_timeout(0, false); } void isns_deregister(struct isns *isns) { struct conf *conf = isns->i_conf; int error, s; char hostname[256]; if (TAILQ_EMPTY(&conf->conf_targets) || TAILQ_EMPTY(&conf->conf_portal_groups)) return; set_timeout(conf->conf_isns_timeout, false); s = isns_do_connect(isns); if (s < 0) return; error = gethostname(hostname, sizeof(hostname)); if (error != 0) log_err(1, "gethostname"); isns_do_deregister(isns, s, hostname); close(s); set_timeout(0, false); } int portal_group_set_filter(struct portal_group *pg, const char *str) { int filter; if (strcmp(str, "none") == 0) { filter = PG_FILTER_NONE; } else if (strcmp(str, "portal") == 0) { filter = PG_FILTER_PORTAL; } else if (strcmp(str, "portal-name") == 0) { filter = PG_FILTER_PORTAL_NAME; } else if (strcmp(str, "portal-name-auth") == 0) { filter = PG_FILTER_PORTAL_NAME_AUTH; } else { log_warnx("invalid discovery-filter \"%s\" for portal-group " "\"%s\"; valid values are \"none\", \"portal\", " "\"portal-name\", and \"portal-name-auth\"", str, pg->pg_name); return (1); } if (pg->pg_discovery_filter != PG_FILTER_UNKNOWN && pg->pg_discovery_filter != filter) { log_warnx("cannot set discovery-filter to \"%s\" for " "portal-group \"%s\"; already has a different " "value", str, pg->pg_name); return (1); } pg->pg_discovery_filter = filter; return (0); } int portal_group_set_offload(struct portal_group *pg, const char *offload) { if (pg->pg_offload != NULL) { log_warnx("cannot set offload to \"%s\" for " "portal-group \"%s\"; already defined", offload, pg->pg_name); return (1); } pg->pg_offload = checked_strdup(offload); return (0); } int portal_group_set_redirection(struct portal_group *pg, const char *addr) { if (pg->pg_redirection != NULL) { log_warnx("cannot set redirection to \"%s\" for " "portal-group \"%s\"; already defined", addr, pg->pg_name); return (1); } pg->pg_redirection = checked_strdup(addr); 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 pport * pport_new(struct conf *conf, const char *name, uint32_t ctl_port) { struct pport *pp; pp = calloc(1, sizeof(*pp)); if (pp == NULL) log_err(1, "calloc"); pp->pp_conf = conf; pp->pp_name = checked_strdup(name); pp->pp_ctl_port = ctl_port; TAILQ_INIT(&pp->pp_ports); TAILQ_INSERT_TAIL(&conf->conf_pports, pp, pp_next); return (pp); } struct pport * pport_find(const struct conf *conf, const char *name) { struct pport *pp; TAILQ_FOREACH(pp, &conf->conf_pports, pp_next) { if (strcasecmp(pp->pp_name, name) == 0) return (pp); } return (NULL); } struct pport * pport_copy(struct pport *pp, struct conf *conf) { struct pport *ppnew; ppnew = pport_new(conf, pp->pp_name, pp->pp_ctl_port); return (ppnew); } void pport_delete(struct pport *pp) { struct port *port, *tport; TAILQ_FOREACH_SAFE(port, &pp->pp_ports, p_ts, tport) port_delete(port); TAILQ_REMOVE(&pp->pp_conf->conf_pports, pp, pp_next); free(pp->pp_name); free(pp); } struct port * port_new(struct conf *conf, struct target *target, struct portal_group *pg) { struct port *port; char *name; int ret; ret = asprintf(&name, "%s-%s", pg->pg_name, target->t_name); if (ret <= 0) log_err(1, "asprintf"); if (port_find(conf, name) != NULL) { log_warnx("duplicate port \"%s\"", name); free(name); return (NULL); } port = calloc(1, sizeof(*port)); if (port == NULL) log_err(1, "calloc"); port->p_conf = conf; port->p_name = name; port->p_ioctl_port = 0; TAILQ_INSERT_TAIL(&conf->conf_ports, port, p_next); TAILQ_INSERT_TAIL(&target->t_ports, port, p_ts); port->p_target = target; TAILQ_INSERT_TAIL(&pg->pg_ports, port, p_pgs); port->p_portal_group = pg; return (port); } struct port * port_new_ioctl(struct conf *conf, struct target *target, int pp, int vp) { struct pport *pport; struct port *port; char *pname; char *name; int ret; ret = asprintf(&pname, "ioctl/%d/%d", pp, vp); if (ret <= 0) { log_err(1, "asprintf"); return (NULL); } pport = pport_find(conf, pname); if (pport != NULL) { free(pname); return (port_new_pp(conf, target, pport)); } ret = asprintf(&name, "%s-%s", pname, target->t_name); free(pname); if (ret <= 0) log_err(1, "asprintf"); if (port_find(conf, name) != NULL) { log_warnx("duplicate port \"%s\"", name); free(name); return (NULL); } port = calloc(1, sizeof(*port)); if (port == NULL) log_err(1, "calloc"); port->p_conf = conf; port->p_name = name; port->p_ioctl_port = 1; port->p_ioctl_pp = pp; port->p_ioctl_vp = vp; TAILQ_INSERT_TAIL(&conf->conf_ports, port, p_next); TAILQ_INSERT_TAIL(&target->t_ports, port, p_ts); port->p_target = target; return (port); } struct port * port_new_pp(struct conf *conf, struct target *target, struct pport *pp) { struct port *port; char *name; int ret; ret = asprintf(&name, "%s-%s", pp->pp_name, target->t_name); if (ret <= 0) log_err(1, "asprintf"); if (port_find(conf, name) != NULL) { log_warnx("duplicate port \"%s\"", name); free(name); return (NULL); } port = calloc(1, sizeof(*port)); if (port == NULL) log_err(1, "calloc"); port->p_conf = conf; port->p_name = name; TAILQ_INSERT_TAIL(&conf->conf_ports, port, p_next); TAILQ_INSERT_TAIL(&target->t_ports, port, p_ts); port->p_target = target; TAILQ_INSERT_TAIL(&pp->pp_ports, port, p_pps); port->p_pport = pp; return (port); } struct port * port_find(const struct conf *conf, const char *name) { struct port *port; TAILQ_FOREACH(port, &conf->conf_ports, p_next) { if (strcasecmp(port->p_name, name) == 0) return (port); } return (NULL); } struct port * port_find_in_pg(const struct portal_group *pg, const char *target) { struct port *port; TAILQ_FOREACH(port, &pg->pg_ports, p_pgs) { if (strcasecmp(port->p_target->t_name, target) == 0) return (port); } return (NULL); } void port_delete(struct port *port) { if (port->p_portal_group) TAILQ_REMOVE(&port->p_portal_group->pg_ports, port, p_pgs); if (port->p_pport) TAILQ_REMOVE(&port->p_pport->pp_ports, port, p_pps); if (port->p_target) TAILQ_REMOVE(&port->p_target->t_ports, port, p_ts); TAILQ_REMOVE(&port->p_conf->conf_ports, port, p_next); free(port->p_name); free(port); } int port_is_dummy(struct port *port) { if (port->p_portal_group) { if (port->p_portal_group->pg_foreign) return (1); if (TAILQ_EMPTY(&port->p_portal_group->pg_portals)) return (1); } return (0); } struct target * target_new(struct conf *conf, const char *name) { struct target *targ; int i, len; targ = target_find(conf, name); if (targ != NULL) { log_warnx("duplicated target \"%s\"", name); return (NULL); } if (valid_iscsi_name(name) == false) { log_warnx("target name \"%s\" is invalid", name); return (NULL); } targ = calloc(1, sizeof(*targ)); if (targ == NULL) log_err(1, "calloc"); targ->t_name = checked_strdup(name); /* * RFC 3722 requires us to normalize the name to lowercase. */ len = strlen(name); for (i = 0; i < len; i++) targ->t_name[i] = tolower(targ->t_name[i]); targ->t_conf = conf; TAILQ_INIT(&targ->t_ports); TAILQ_INSERT_TAIL(&conf->conf_targets, targ, t_next); return (targ); } void target_delete(struct target *targ) { struct port *port, *tport; TAILQ_FOREACH_SAFE(port, &targ->t_ports, p_ts, tport) port_delete(port); TAILQ_REMOVE(&targ->t_conf->conf_targets, targ, t_next); free(targ->t_name); free(targ->t_redirection); free(targ); } struct target * target_find(struct conf *conf, const char *name) { struct target *targ; TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { if (strcasecmp(targ->t_name, name) == 0) return (targ); } return (NULL); } int target_set_redirection(struct target *target, const char *addr) { if (target->t_redirection != NULL) { log_warnx("cannot set redirection to \"%s\" for " "target \"%s\"; already defined", addr, target->t_name); return (1); } target->t_redirection = checked_strdup(addr); return (0); } struct lun * lun_new(struct conf *conf, const char *name) { struct lun *lun; lun = lun_find(conf, name); if (lun != NULL) { log_warnx("duplicated lun \"%s\"", name); return (NULL); } lun = calloc(1, sizeof(*lun)); if (lun == NULL) log_err(1, "calloc"); lun->l_conf = conf; lun->l_name = checked_strdup(name); TAILQ_INIT(&lun->l_options); TAILQ_INSERT_TAIL(&conf->conf_luns, lun, l_next); lun->l_ctl_lun = -1; return (lun); } void lun_delete(struct lun *lun) { struct target *targ; struct option *o, *tmp; int i; TAILQ_FOREACH(targ, &lun->l_conf->conf_targets, t_next) { for (i = 0; i < MAX_LUNS; i++) { if (targ->t_luns[i] == lun) targ->t_luns[i] = NULL; } } TAILQ_REMOVE(&lun->l_conf->conf_luns, lun, l_next); TAILQ_FOREACH_SAFE(o, &lun->l_options, o_next, tmp) option_delete(&lun->l_options, o); free(lun->l_name); free(lun->l_backend); free(lun->l_device_id); free(lun->l_path); free(lun->l_scsiname); free(lun->l_serial); free(lun); } struct lun * lun_find(const struct conf *conf, const char *name) { struct lun *lun; TAILQ_FOREACH(lun, &conf->conf_luns, l_next) { if (strcmp(lun->l_name, name) == 0) 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_type(struct lun *lun, uint8_t value) { lun->l_device_type = 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_scsiname(struct lun *lun, const char *value) { free(lun->l_scsiname); lun->l_scsiname = 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 option * option_new(struct options *options, const char *name, const char *value) { struct option *o; o = option_find(options, name); if (o != NULL) { log_warnx("duplicated option \"%s\"", name); return (NULL); } o = calloc(1, sizeof(*o)); if (o == NULL) log_err(1, "calloc"); o->o_name = checked_strdup(name); o->o_value = checked_strdup(value); TAILQ_INSERT_TAIL(options, o, o_next); return (o); } void option_delete(struct options *options, struct option *o) { TAILQ_REMOVE(options, o, o_next); free(o->o_name); free(o->o_value); free(o); } struct option * option_find(const struct options *options, const char *name) { struct option *o; TAILQ_FOREACH(o, options, o_next) { if (strcmp(o->o_name, name) == 0) return (o); } return (NULL); } void option_set(struct option *o, const char *value) { free(o->o_value); o->o_value = checked_strdup(value); } -static struct connection * +#ifdef ICL_KERNEL_PROXY + +static void +pdu_receive_proxy(struct pdu *pdu) +{ + struct connection *conn; + size_t len; + + assert(proxy_mode); + conn = pdu->pdu_connection; + + kernel_receive(pdu); + + len = pdu_ahs_length(pdu); + if (len > 0) + log_errx(1, "protocol error: non-empty AHS"); + + len = pdu_data_segment_length(pdu); + assert(len <= (size_t)conn->conn_max_recv_data_segment_length); + pdu->pdu_data_len = len; +} + +static void +pdu_send_proxy(struct pdu *pdu) +{ + + assert(proxy_mode); + + pdu_set_data_segment_length(pdu, pdu->pdu_data_len); + kernel_send(pdu); +} + +#endif /* ICL_KERNEL_PROXY */ + +static void +pdu_fail(const struct connection *conn __unused, const char *reason __unused) +{ +} + +static struct ctld_connection * connection_new(struct portal *portal, int fd, const char *host, const struct sockaddr *client_sa) { - struct connection *conn; + struct ctld_connection *conn; conn = calloc(1, sizeof(*conn)); if (conn == NULL) log_err(1, "calloc"); + connection_init(&conn->conn, &conn_ops, proxy_mode); + conn->conn.conn_socket = fd; conn->conn_portal = portal; - conn->conn_socket = fd; conn->conn_initiator_addr = checked_strdup(host); memcpy(&conn->conn_initiator_sa, client_sa, client_sa->sa_len); - /* - * Default values, from RFC 3720, section 12. - */ - conn->conn_max_recv_data_segment_length = 8192; - conn->conn_max_send_data_segment_length = 8192; - conn->conn_max_burst_length = 262144; - conn->conn_first_burst_length = 65536; - conn->conn_immediate_data = true; - return (conn); } #if 0 static void conf_print(struct conf *conf) { struct auth_group *ag; struct auth *auth; struct auth_name *auth_name; struct auth_portal *auth_portal; struct portal_group *pg; struct portal *portal; struct target *targ; struct lun *lun; struct option *o; 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); TAILQ_FOREACH(auth_name, &ag->ag_names, an_next) fprintf(stderr, "\t initiator-name %s\n", auth_name->an_initiator_name); TAILQ_FOREACH(auth_portal, &ag->ag_portals, ap_next) fprintf(stderr, "\t initiator-portal %s\n", auth_portal->ap_initiator_portal); 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(lun, &conf->conf_luns, l_next) { fprintf(stderr, "\tlun %s {\n", lun->l_name); fprintf(stderr, "\t\tpath %s\n", lun->l_path); TAILQ_FOREACH(o, &lun->l_options, o_next) fprintf(stderr, "\t\toption %s %s\n", o->o_name, o->o_value); fprintf(stderr, "\t}\n"); } TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { fprintf(stderr, "target %s {\n", targ->t_name); if (targ->t_alias != NULL) fprintf(stderr, "\t alias %s\n", targ->t_alias); fprintf(stderr, "}\n"); } } #endif static int conf_verify_lun(struct lun *lun) { const struct lun *lun2; if (lun->l_backend == NULL) lun_set_backend(lun, "block"); if (strcmp(lun->l_backend, "block") == 0) { if (lun->l_path == NULL) { log_warnx("missing path for lun \"%s\"", lun->l_name); return (1); } } else if (strcmp(lun->l_backend, "ramdisk") == 0) { if (lun->l_size == 0) { log_warnx("missing size for ramdisk-backed lun \"%s\"", lun->l_name); return (1); } if (lun->l_path != NULL) { log_warnx("path must not be specified " "for ramdisk-backed lun \"%s\"", lun->l_name); return (1); } } if (lun->l_blocksize == 0) { if (lun->l_device_type == 5) lun_set_blocksize(lun, DEFAULT_CD_BLOCKSIZE); else lun_set_blocksize(lun, DEFAULT_BLOCKSIZE); } else if (lun->l_blocksize < 0) { log_warnx("invalid blocksize for lun \"%s\"; " "must be larger than 0", lun->l_name); return (1); } if (lun->l_size != 0 && lun->l_size % lun->l_blocksize != 0) { log_warnx("invalid size for lun \"%s\"; " "must be multiple of blocksize", lun->l_name); return (1); } TAILQ_FOREACH(lun2, &lun->l_conf->conf_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: path \"%s\" duplicated " "between lun \"%s\", and " "lun \"%s\"", lun->l_path, lun->l_name, lun2->l_name); } } return (0); } int conf_verify(struct conf *conf) { struct auth_group *ag; struct portal_group *pg; struct port *port; struct target *targ; struct lun *lun; bool found; int error, i; if (conf->conf_pidfile_path == NULL) conf->conf_pidfile_path = checked_strdup(DEFAULT_PIDFILE); TAILQ_FOREACH(lun, &conf->conf_luns, l_next) { error = conf_verify_lun(lun); if (error != 0) return (error); } TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { if (targ->t_auth_group == NULL) { targ->t_auth_group = auth_group_find(conf, "default"); assert(targ->t_auth_group != NULL); } if (TAILQ_EMPTY(&targ->t_ports)) { pg = portal_group_find(conf, "default"); assert(pg != NULL); port_new(conf, targ, pg); } found = false; for (i = 0; i < MAX_LUNS; i++) { if (targ->t_luns[i] != NULL) found = true; } if (!found && targ->t_redirection == NULL) { log_warnx("no LUNs defined for target \"%s\"", targ->t_name); } if (found && targ->t_redirection != NULL) { log_debugx("target \"%s\" contains luns, " " but configured for redirection", targ->t_name); } } 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, "default"); assert(pg->pg_discovery_auth_group != NULL); } if (pg->pg_discovery_filter == PG_FILTER_UNKNOWN) pg->pg_discovery_filter = PG_FILTER_NONE; if (pg->pg_redirection != NULL) { if (!TAILQ_EMPTY(&pg->pg_ports)) { log_debugx("portal-group \"%s\" assigned " "to target, but configured " "for redirection", pg->pg_name); } pg->pg_unassigned = false; } else if (!TAILQ_EMPTY(&pg->pg_ports)) { pg->pg_unassigned = false; } else { if (strcmp(pg->pg_name, "default") != 0) log_warnx("portal-group \"%s\" not assigned " "to any target", pg->pg_name); pg->pg_unassigned = true; } } 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); found = false; TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { if (targ->t_auth_group == ag) { found = true; break; } } TAILQ_FOREACH(port, &conf->conf_ports, p_next) { if (port->p_auth_group == ag) { found = true; break; } } TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { if (pg->pg_discovery_auth_group == ag) { found = true; break; } } if (!found && ag->ag_name != NULL && strcmp(ag->ag_name, "default") != 0 && 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 lun *oldlun, *newlun, *tmplun; struct portal_group *oldpg, *newpg; struct portal *oldp, *newp; struct port *oldport, *newport, *tmpport; struct isns *oldns, *newns; pid_t otherpid; int changed, cumulated_error = 0, error, sockbuf; int one = 1; 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); } } /* * Go through the new portal groups, assigning tags or preserving old. */ TAILQ_FOREACH(newpg, &newconf->conf_portal_groups, pg_next) { if (newpg->pg_tag != 0) continue; oldpg = portal_group_find(oldconf, newpg->pg_name); if (oldpg != NULL) newpg->pg_tag = oldpg->pg_tag; else newpg->pg_tag = ++last_portal_group_tag; } /* Deregister on removed iSNS servers. */ TAILQ_FOREACH(oldns, &oldconf->conf_isns, i_next) { TAILQ_FOREACH(newns, &newconf->conf_isns, i_next) { if (strcmp(oldns->i_addr, newns->i_addr) == 0) break; } if (newns == NULL) isns_deregister(oldns); } /* * XXX: If target or lun removal fails, we should somehow "move" * the old lun or target into newconf, so that subsequent * conf_apply() would try to remove them again. That would * be somewhat hairy, though, and lun deletion failures don't * really happen, so leave it as it is for now. */ /* * First, remove any ports present in the old configuration * and missing in the new one. */ TAILQ_FOREACH_SAFE(oldport, &oldconf->conf_ports, p_next, tmpport) { if (port_is_dummy(oldport)) continue; newport = port_find(newconf, oldport->p_name); if (newport != NULL && !port_is_dummy(newport)) continue; log_debugx("removing port \"%s\"", oldport->p_name); error = kernel_port_remove(oldport); if (error != 0) { log_warnx("failed to remove port %s", oldport->p_name); /* * XXX: Uncomment after fixing the root cause. * * cumulated_error++; */ } } /* * Second, remove any LUNs present in the old configuration * and missing in the new one. */ TAILQ_FOREACH_SAFE(oldlun, &oldconf->conf_luns, l_next, tmplun) { newlun = lun_find(newconf, oldlun->l_name); if (newlun == NULL) { log_debugx("lun \"%s\", CTL lun %d " "not found in new configuration; " "removing", oldlun->l_name, oldlun->l_ctl_lun); error = kernel_lun_remove(oldlun); if (error != 0) { log_warnx("failed to remove lun \"%s\", " "CTL lun %d", oldlun->l_name, oldlun->l_ctl_lun); cumulated_error++; } 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 \"%s\", " "CTL lun %d changed; removing", oldlun->l_name, oldlun->l_ctl_lun); changed = 1; } if (oldlun->l_blocksize != newlun->l_blocksize) { log_debugx("blocksize for lun \"%s\", " "CTL lun %d changed; removing", oldlun->l_name, 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 \"%s\", " "CTL lun %d changed; removing", oldlun->l_name, 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 \"%s\", " "CTL lun %d, changed; removing", oldlun->l_name, 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 \"%s\", " "CTL lun %d changed; removing", oldlun->l_name, oldlun->l_ctl_lun); changed = 1; } if (changed) { error = kernel_lun_remove(oldlun); if (error != 0) { log_warnx("failed to remove lun \"%s\", " "CTL lun %d", oldlun->l_name, oldlun->l_ctl_lun); cumulated_error++; } lun_delete(oldlun); continue; } lun_set_ctl_lun(newlun, oldlun->l_ctl_lun); } TAILQ_FOREACH_SAFE(newlun, &newconf->conf_luns, l_next, tmplun) { oldlun = lun_find(oldconf, newlun->l_name); if (oldlun != NULL) { log_debugx("modifying lun \"%s\", CTL lun %d", newlun->l_name, newlun->l_ctl_lun); error = kernel_lun_modify(newlun); if (error != 0) { log_warnx("failed to " "modify lun \"%s\", CTL lun %d", newlun->l_name, newlun->l_ctl_lun); cumulated_error++; } continue; } log_debugx("adding lun \"%s\"", newlun->l_name); error = kernel_lun_add(newlun); if (error != 0) { log_warnx("failed to add lun \"%s\"", newlun->l_name); lun_delete(newlun); cumulated_error++; } } /* * Now add new ports or modify existing ones. */ TAILQ_FOREACH(newport, &newconf->conf_ports, p_next) { if (port_is_dummy(newport)) continue; oldport = port_find(oldconf, newport->p_name); if (oldport == NULL || port_is_dummy(oldport)) { log_debugx("adding port \"%s\"", newport->p_name); error = kernel_port_add(newport); } else { log_debugx("updating port \"%s\"", newport->p_name); newport->p_ctl_port = oldport->p_ctl_port; error = kernel_port_update(newport, oldport); } if (error != 0) { log_warnx("failed to %s port %s", (oldport == NULL) ? "add" : "update", newport->p_name); /* * XXX: Uncomment after fixing the root cause. * * cumulated_error++; */ } } /* * Go through the new portals, opening the sockets as necessary. */ TAILQ_FOREACH(newpg, &newconf->conf_portal_groups, pg_next) { if (newpg->pg_foreign) continue; 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 if (proxy_mode) { newpg->pg_conf->conf_portal_id++; newp->p_id = newpg->pg_conf->conf_portal_id; log_debugx("listening on %s, portal-group " "\"%s\", portal id %d, using ICL proxy", newp->p_listen, newpg->pg_name, newp->p_id); kernel_listen(newp->p_ai, newp->p_iser, newp->p_id); continue; } #endif assert(proxy_mode == false); 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; } sockbuf = SOCKBUF_SIZE; if (setsockopt(newp->p_socket, SOL_SOCKET, SO_RCVBUF, &sockbuf, sizeof(sockbuf)) == -1) log_warn("setsockopt(SO_RCVBUF) failed " "for %s", newp->p_listen); sockbuf = SOCKBUF_SIZE; if (setsockopt(newp->p_socket, SOL_SOCKET, SO_SNDBUF, &sockbuf, sizeof(sockbuf)) == -1) log_warn("setsockopt(SO_SNDBUF) failed " "for %s", newp->p_listen); if (setsockopt(newp->p_socket, SOL_SOCKET, SO_NO_DDP, &one, sizeof(one)) == -1) log_warn("setsockopt(SO_NO_DDP) failed " "for %s", newp->p_listen); 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; } if (newpg->pg_dscp != -1) { struct sockaddr sa; int len = sizeof(sa); getsockname(newp->p_socket, &sa, &len); /* * Only allow the 6-bit DSCP * field to be modified */ int tos = newpg->pg_dscp << 2; if (sa.sa_family == AF_INET) { if (setsockopt(newp->p_socket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1) log_warn("setsockopt(IP_TOS) " "failed for %s", newp->p_listen); } else if (sa.sa_family == AF_INET6) { if (setsockopt(newp->p_socket, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)) == -1) log_warn("setsockopt(IPV6_TCLASS) " "failed for %s", newp->p_listen); } } if (newpg->pg_pcp != -1) { struct sockaddr sa; int len = sizeof(sa); getsockname(newp->p_socket, &sa, &len); /* * Only allow the 6-bit DSCP * field to be modified */ int pcp = newpg->pg_pcp; if (sa.sa_family == AF_INET) { if (setsockopt(newp->p_socket, IPPROTO_IP, IP_VLAN_PCP, &pcp, sizeof(pcp)) == -1) log_warn("setsockopt(IP_VLAN_PCP) " "failed for %s", newp->p_listen); } else if (sa.sa_family == AF_INET6) { if (setsockopt(newp->p_socket, IPPROTO_IPV6, IPV6_VLAN_PCP, &pcp, sizeof(pcp)) == -1) log_warn("setsockopt(IPV6_VLAN_PCP) " "failed for %s", newp->p_listen); } } 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; } } } /* * 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; } } /* (Re-)Register on remaining/new iSNS servers. */ TAILQ_FOREACH(newns, &newconf->conf_isns, i_next) { TAILQ_FOREACH(oldns, &oldconf->conf_isns, i_next) { if (strcmp(oldns->i_addr, newns->i_addr) == 0) break; } isns_register(newns, oldns); } /* Schedule iSNS update */ if (!TAILQ_EMPTY(&newconf->conf_isns)) set_timeout((newconf->conf_isns_period + 2) / 3, false); return (cumulated_error); } -bool +static bool timed_out(void) { return (sigalrm_received); } static void sigalrm_handler_fatal(int dummy __unused) { /* * It would be easiest to just log an error and exit. We can't * do this, though, because log_errx() is not signal safe, since * it calls syslog(3). Instead, set a flag checked by pdu_send() * and pdu_receive(), to call log_errx() there. Should they fail * to notice, we'll exit here one second later. */ if (sigalrm_received) { /* * Oh well. Just give up and quit. */ _exit(2); } sigalrm_received = true; } static void sigalrm_handler(int dummy __unused) { sigalrm_received = true; } void set_timeout(int timeout, int fatal) { struct sigaction sa; struct itimerval itv; int error; if (timeout <= 0) { log_debugx("session timeout disabled"); bzero(&itv, sizeof(itv)); error = setitimer(ITIMER_REAL, &itv, NULL); if (error != 0) log_err(1, "setitimer"); sigalrm_received = false; return; } sigalrm_received = false; bzero(&sa, sizeof(sa)); if (fatal) sa.sa_handler = sigalrm_handler_fatal; else sa.sa_handler = sigalrm_handler; sigfillset(&sa.sa_mask); error = sigaction(SIGALRM, &sa, NULL); if (error != 0) log_err(1, "sigaction"); /* * First SIGALRM will arive after conf_timeout seconds. * If we do nothing, another one will arrive a second later. */ log_debugx("setting session timeout to %d seconds", timeout); bzero(&itv, sizeof(itv)); itv.it_interval.tv_sec = 1; itv.it_value.tv_sec = timeout; error = setitimer(ITIMER_REAL, &itv, NULL); if (error != 0) log_err(1, "setitimer"); } static int wait_for_children(bool block) { pid_t pid; int status; int num = 0; for (;;) { /* * If "block" is true, wait for at least one process. */ if (block && num == 0) pid = wait4(-1, &status, 0, NULL); else pid = wait4(-1, &status, WNOHANG, NULL); if (pid <= 0) break; if (WIFSIGNALED(status)) { log_warnx("child process %d terminated with signal %d", pid, WTERMSIG(status)); } else if (WEXITSTATUS(status) != 0) { log_warnx("child process %d terminated with exit status %d", pid, WEXITSTATUS(status)); } else { log_debugx("child process %d terminated gracefully", pid); } num++; } return (num); } static void handle_connection(struct portal *portal, int fd, const struct sockaddr *client_sa, bool dont_fork) { - struct connection *conn; + struct ctld_connection *conn; int error; pid_t pid; char host[NI_MAXHOST + 1]; struct conf *conf; conf = portal->p_portal_group->pg_conf; if (dont_fork) { log_debugx("incoming connection; not forking due to -d flag"); } else { nchildren -= wait_for_children(false); assert(nchildren >= 0); while (conf->conf_maxproc > 0 && nchildren >= conf->conf_maxproc) { log_debugx("maxproc limit of %d child processes hit; " "waiting for child process to exit", conf->conf_maxproc); nchildren -= wait_for_children(true); assert(nchildren >= 0); } log_debugx("incoming connection; forking child process #%d", nchildren); nchildren++; pid = fork(); if (pid < 0) log_err(1, "fork"); if (pid > 0) { close(fd); return; } } pidfile_close(conf->conf_pidfh); error = getnameinfo(client_sa, client_sa->sa_len, host, sizeof(host), NULL, 0, NI_NUMERICHOST); if (error != 0) log_errx(1, "getnameinfo: %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); conn = connection_new(portal, fd, host, client_sa); set_timeout(conf->conf_timeout, true); 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); } 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); } static void main_loop(struct conf *conf, bool dont_fork) { struct portal_group *pg; struct portal *portal; struct sockaddr_storage client_sa; socklen_t client_salen; #ifdef ICL_KERNEL_PROXY int connection_id; int portal_id; #endif fd_set fdset; int error, nfds, client_fd; pidfile_write(conf->conf_pidfh); for (;;) { if (sighup_received || sigterm_received || timed_out()) return; #ifdef ICL_KERNEL_PROXY if (proxy_mode) { client_salen = sizeof(client_sa); kernel_accept(&connection_id, &portal_id, (struct sockaddr *)&client_sa, &client_salen); assert(client_salen >= client_sa.ss_len); log_debugx("incoming connection, id %d, portal id %d", connection_id, portal_id); TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { TAILQ_FOREACH(portal, &pg->pg_portals, p_next) { if (portal->p_id == portal_id) { goto found; } } } log_errx(1, "kernel returned invalid portal_id %d", portal_id); found: handle_connection(portal, connection_id, (struct sockaddr *)&client_sa, dont_fork); } else { #endif assert(proxy_mode == false); 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_salen = sizeof(client_sa); client_fd = accept(portal->p_socket, (struct sockaddr *)&client_sa, &client_salen); if (client_fd < 0) { if (errno == ECONNABORTED) continue; log_err(1, "accept"); } assert(client_salen >= client_sa.ss_len); handle_connection(portal, client_fd, (struct sockaddr *)&client_sa, dont_fork); break; } } #ifdef ICL_KERNEL_PROXY } #endif } } static void sighup_handler(int dummy __unused) { sighup_received = true; } static void sigterm_handler(int dummy __unused) { sigterm_received = true; } static void sigchld_handler(int dummy __unused) { /* * The only purpose of this handler is to make SIGCHLD * interrupt the ISCSIDWAIT ioctl(2), so we can call * wait_for_children(). */ } 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 = sigchld_handler; error = sigaction(SIGCHLD, &sa, NULL); if (error != 0) log_err(1, "sigaction"); } 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? */ } static struct conf * conf_new_from_file(const char *path, struct conf *oldconf, bool ucl) { struct conf *conf; struct auth_group *ag; struct portal_group *pg; struct pport *pp; int error; log_debugx("obtaining configuration from %s", path); conf = conf_new(); TAILQ_FOREACH(pp, &oldconf->conf_pports, pp_next) pport_copy(pp, conf); ag = auth_group_new(conf, "default"); assert(ag != NULL); ag = auth_group_new(conf, "no-authentication"); assert(ag != NULL); ag->ag_type = AG_TYPE_NO_AUTHENTICATION; ag = auth_group_new(conf, "no-access"); assert(ag != NULL); ag->ag_type = AG_TYPE_DENY; pg = portal_group_new(conf, "default"); assert(pg != NULL); if (ucl) error = uclparse_conf(conf, path); else error = parse_conf(conf, path); if (error != 0) { conf_delete(conf); return (NULL); } check_perms(path); if (conf->conf_default_ag_defined == false) { log_debugx("auth-group \"default\" not defined; " "going with defaults"); ag = auth_group_find(conf, "default"); assert(ag != NULL); ag->ag_type = AG_TYPE_DENY; } if (conf->conf_default_pg_defined == false) { log_debugx("portal-group \"default\" not defined; " "going with defaults"); pg = portal_group_find(conf, "default"); assert(pg != NULL); portal_group_add_listen(pg, "0.0.0.0:3260", false); portal_group_add_listen(pg, "[::]:3260", false); } conf->conf_kernel_port_on = true; error = conf_verify(conf); if (error != 0) { conf_delete(conf); return (NULL); } return (conf); } int main(int argc, char **argv) { struct conf *oldconf, *newconf, *tmpconf; struct isns *newns; const char *config_path = DEFAULT_CONFIG_PATH; int debug = 0, ch, error; bool dont_daemonize = false; bool test_config = false; bool use_ucl = false; while ((ch = getopt(argc, argv, "dtuf:R")) != -1) { switch (ch) { case 'd': dont_daemonize = true; debug++; break; case 't': test_config = true; break; case 'u': use_ucl = true; break; case 'f': config_path = optarg; break; case 'R': #ifndef ICL_KERNEL_PROXY log_errx(1, "ctld(8) compiled without ICL_KERNEL_PROXY " "does not support iSER protocol"); #endif proxy_mode = true; 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, oldconf, use_ucl); if (newconf == NULL) log_errx(1, "configuration error; exiting"); if (test_config) return (0); if (debug > 0) { oldconf->conf_debug = debug; newconf->conf_debug = debug; } error = conf_apply(oldconf, newconf); if (error != 0) log_errx(1, "failed to apply configuration; exiting"); conf_delete(oldconf); oldconf = NULL; register_signals(); if (dont_daemonize == false) { log_debugx("daemonizing"); if (daemon(0, 0) == -1) { log_warn("cannot daemonize"); pidfile_remove(newconf->conf_pidfh); exit(1); } } /* Schedule iSNS update */ if (!TAILQ_EMPTY(&newconf->conf_isns)) set_timeout((newconf->conf_isns_period + 2) / 3, false); 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, newconf, use_ucl); 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 if (sigterm_received) { log_debugx("exiting on signal; " "reloading empty configuration"); log_debugx("removing CTL iSCSI ports " "and terminating all connections"); 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"); conf_delete(oldconf); oldconf = NULL; log_warnx("exiting on signal"); exit(0); } else { nchildren -= wait_for_children(false); assert(nchildren >= 0); if (timed_out()) { set_timeout(0, false); TAILQ_FOREACH(newns, &newconf->conf_isns, i_next) isns_check(newns); /* Schedule iSNS update */ if (!TAILQ_EMPTY(&newconf->conf_isns)) { set_timeout((newconf->conf_isns_period + 2) / 3, false); } } } } /* NOTREACHED */ } diff --git a/usr.sbin/ctld/ctld.h b/usr.sbin/ctld/ctld.h index 4ff8f0e638ac..293f5378592f 100644 --- a/usr.sbin/ctld/ctld.h +++ b/usr.sbin/ctld/ctld.h @@ -1,472 +1,386 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 The FreeBSD Foundation * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef CTLD_H #define CTLD_H #include #ifdef ICL_KERNEL_PROXY #include #endif #include #include +#include #include #define DEFAULT_CONFIG_PATH "/etc/ctl.conf" #define DEFAULT_PIDFILE "/var/run/ctld.pid" #define DEFAULT_BLOCKSIZE 512 #define DEFAULT_CD_BLOCKSIZE 2048 #define MAX_LUNS 1024 #define MAX_NAME_LEN 223 #define MAX_DATA_SEGMENT_LENGTH (128 * 1024) #define SOCKBUF_SIZE 1048576 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; }; struct auth_name { TAILQ_ENTRY(auth_name) an_next; struct auth_group *an_auth_group; char *an_initiator_name; }; struct auth_portal { TAILQ_ENTRY(auth_portal) ap_next; struct auth_group *ap_auth_group; char *ap_initiator_portal; struct sockaddr_storage ap_sa; int ap_mask; }; #define AG_TYPE_UNKNOWN 0 #define AG_TYPE_DENY 1 #define AG_TYPE_NO_AUTHENTICATION 2 #define AG_TYPE_CHAP 3 #define AG_TYPE_CHAP_MUTUAL 4 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; TAILQ_HEAD(, auth_name) ag_names; TAILQ_HEAD(, auth_portal) ag_portals; }; struct portal { TAILQ_ENTRY(portal) p_next; struct portal_group *p_portal_group; bool p_iser; char *p_listen; struct addrinfo *p_ai; #ifdef ICL_KERNEL_PROXY int p_id; #endif TAILQ_HEAD(, target) p_targets; int p_socket; }; TAILQ_HEAD(options, option); #define PG_FILTER_UNKNOWN 0 #define PG_FILTER_NONE 1 #define PG_FILTER_PORTAL 2 #define PG_FILTER_PORTAL_NAME 3 #define PG_FILTER_PORTAL_NAME_AUTH 4 struct portal_group { TAILQ_ENTRY(portal_group) pg_next; struct conf *pg_conf; struct options pg_options; char *pg_name; struct auth_group *pg_discovery_auth_group; int pg_discovery_filter; int pg_foreign; bool pg_unassigned; TAILQ_HEAD(, portal) pg_portals; TAILQ_HEAD(, port) pg_ports; char *pg_offload; char *pg_redirection; int pg_dscp; int pg_pcp; uint16_t pg_tag; }; struct pport { TAILQ_ENTRY(pport) pp_next; TAILQ_HEAD(, port) pp_ports; struct conf *pp_conf; char *pp_name; uint32_t pp_ctl_port; }; struct port { TAILQ_ENTRY(port) p_next; TAILQ_ENTRY(port) p_pgs; TAILQ_ENTRY(port) p_pps; TAILQ_ENTRY(port) p_ts; struct conf *p_conf; char *p_name; struct auth_group *p_auth_group; struct portal_group *p_portal_group; struct pport *p_pport; struct target *p_target; int p_ioctl_port; int p_ioctl_pp; int p_ioctl_vp; uint32_t p_ctl_port; }; struct option { TAILQ_ENTRY(option) o_next; char *o_name; char *o_value; }; struct lun { TAILQ_ENTRY(lun) l_next; struct conf *l_conf; struct options l_options; char *l_name; char *l_backend; uint8_t l_device_type; int l_blocksize; char *l_device_id; char *l_path; char *l_scsiname; char *l_serial; int64_t l_size; int l_ctl_lun; }; struct target { TAILQ_ENTRY(target) t_next; struct conf *t_conf; struct lun *t_luns[MAX_LUNS]; struct auth_group *t_auth_group; TAILQ_HEAD(, port) t_ports; char *t_name; char *t_alias; char *t_redirection; }; struct isns { TAILQ_ENTRY(isns) i_next; struct conf *i_conf; char *i_addr; struct addrinfo *i_ai; }; struct conf { char *conf_pidfile_path; TAILQ_HEAD(, lun) conf_luns; TAILQ_HEAD(, target) conf_targets; TAILQ_HEAD(, auth_group) conf_auth_groups; TAILQ_HEAD(, port) conf_ports; TAILQ_HEAD(, portal_group) conf_portal_groups; TAILQ_HEAD(, pport) conf_pports; TAILQ_HEAD(, isns) conf_isns; int conf_isns_period; int conf_isns_timeout; int conf_debug; int conf_timeout; int conf_maxproc; #ifdef ICL_KERNEL_PROXY int conf_portal_id; #endif struct pidfh *conf_pidfh; bool conf_default_pg_defined; bool conf_default_ag_defined; bool conf_kernel_port_on; }; #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 ctld_connection { + struct connection conn; struct portal *conn_portal; struct port *conn_port; struct target *conn_target; - int conn_socket; int conn_session_type; char *conn_initiator_name; char *conn_initiator_addr; char *conn_initiator_alias; uint8_t conn_initiator_isid[6]; struct sockaddr_storage conn_initiator_sa; - uint32_t conn_cmdsn; - uint32_t conn_statsn; int conn_max_recv_data_segment_limit; int conn_max_send_data_segment_limit; int conn_max_burst_limit; int conn_first_burst_limit; - int conn_max_recv_data_segment_length; - int conn_max_send_data_segment_length; - int conn_max_burst_length; - int conn_first_burst_length; - int conn_immediate_data; - int conn_header_digest; - int conn_data_digest; const char *conn_user; struct chap *conn_chap; }; -struct pdu { - struct connection *pdu_connection; - struct iscsi_bhs *pdu_bhs; - char *pdu_data; - size_t pdu_data_len; -}; - -#define KEYS_MAX 1024 - -struct keys { - char *keys_names[KEYS_MAX]; - char *keys_values[KEYS_MAX]; - char *keys_data; - size_t keys_data_len; -}; - -#define CHAP_CHALLENGE_LEN 1024 -#define CHAP_DIGEST_LEN 16 /* Equal to MD5 digest size. */ - -struct chap { - unsigned char chap_id; - char chap_challenge[CHAP_CHALLENGE_LEN]; - char chap_response[CHAP_DIGEST_LEN]; -}; - -struct rchap { - char *rchap_secret; - unsigned char rchap_id; - void *rchap_challenge; - size_t rchap_challenge_len; -}; - -struct chap *chap_new(void); -char *chap_get_id(const struct chap *chap); -char *chap_get_challenge(const struct chap *chap); -int chap_receive(struct chap *chap, const char *response); -int chap_authenticate(struct chap *chap, - const char *secret); -void chap_delete(struct chap *chap); - -struct rchap *rchap_new(const char *secret); -int rchap_receive(struct rchap *rchap, - const char *id, const char *challenge); -char *rchap_get_response(struct rchap *rchap); -void rchap_delete(struct rchap *rchap); - int parse_conf(struct conf *conf, const char *path); int uclparse_conf(struct conf *conf, const char *path); struct conf *conf_new(void); 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(const struct conf *conf, const char *name); int auth_group_set_type(struct auth_group *ag, const char *type); 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(const struct auth_group *ag, const char *user); const struct auth_name *auth_name_new(struct auth_group *ag, const char *initiator_name); bool auth_name_defined(const struct auth_group *ag); const struct auth_name *auth_name_find(const struct auth_group *ag, const char *initiator_name); int auth_name_check(const struct auth_group *ag, const char *initiator_name); const struct auth_portal *auth_portal_new(struct auth_group *ag, const char *initiator_portal); bool auth_portal_defined(const struct auth_group *ag); const struct auth_portal *auth_portal_find(const struct auth_group *ag, const struct sockaddr_storage *sa); int auth_portal_check(const struct auth_group *ag, const struct sockaddr_storage *sa); 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(const struct conf *conf, const char *name); int portal_group_add_listen(struct portal_group *pg, const char *listen, bool iser); int portal_group_set_filter(struct portal_group *pg, const char *filter); int portal_group_set_offload(struct portal_group *pg, const char *offload); int portal_group_set_redirection(struct portal_group *pg, const char *addr); int isns_new(struct conf *conf, const char *addr); void isns_delete(struct isns *is); void isns_register(struct isns *isns, struct isns *oldisns); void isns_check(struct isns *isns); void isns_deregister(struct isns *isns); struct pport *pport_new(struct conf *conf, const char *name, uint32_t ctl_port); struct pport *pport_find(const struct conf *conf, const char *name); struct pport *pport_copy(struct pport *pport, struct conf *conf); void pport_delete(struct pport *pport); struct port *port_new(struct conf *conf, struct target *target, struct portal_group *pg); struct port *port_new_ioctl(struct conf *conf, struct target *target, int pp, int vp); struct port *port_new_pp(struct conf *conf, struct target *target, struct pport *pp); struct port *port_find(const struct conf *conf, const char *name); struct port *port_find_in_pg(const struct portal_group *pg, const char *target); void port_delete(struct port *port); int port_is_dummy(struct port *port); struct target *target_new(struct conf *conf, const char *name); void target_delete(struct target *target); struct target *target_find(struct conf *conf, const char *name); int target_set_redirection(struct target *target, const char *addr); struct lun *lun_new(struct conf *conf, const char *name); void lun_delete(struct lun *lun); struct lun *lun_find(const struct conf *conf, const char *name); void lun_set_backend(struct lun *lun, const char *value); void lun_set_device_type(struct lun *lun, uint8_t 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_scsiname(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 option *option_new(struct options *os, const char *name, const char *value); void option_delete(struct options *os, struct option *co); struct option *option_find(const struct options *os, const char *name); void option_set(struct option *o, const char *value); void kernel_init(void); int kernel_lun_add(struct lun *lun); int kernel_lun_modify(struct lun *lun); int kernel_lun_remove(struct lun *lun); -void kernel_handoff(struct connection *conn); +void kernel_handoff(struct ctld_connection *conn); void kernel_limits(const char *offload, int *max_recv_data_segment_length, int *max_send_data_segment_length, int *max_burst_length, int *first_burst_length); int kernel_port_add(struct port *port); int kernel_port_update(struct port *port, struct port *old); int kernel_port_remove(struct port *port); void kernel_capsicate(void); #ifdef ICL_KERNEL_PROXY void kernel_listen(struct addrinfo *ai, bool iser, int portal_id); void kernel_accept(int *connection_id, int *portal_id, struct sockaddr *client_sa, socklen_t *client_salen); void kernel_send(struct pdu *pdu); void kernel_receive(struct pdu *pdu); #endif -struct keys *keys_new(void); -void keys_delete(struct keys *keys); -void keys_load(struct keys *keys, const struct pdu *pdu); -void keys_save(struct keys *keys, struct pdu *pdu); -const char *keys_find(struct keys *keys, const char *name); -void keys_add(struct keys *keys, - const char *name, const char *value); -void keys_add_int(struct keys *keys, - const char *name, int value); - -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 __printflike(2, 3); -void log_errx(int, const char *, ...) - __dead2 __printflike(2, 3); -void log_warn(const char *, ...) __printflike(1, 2); -void log_warnx(const char *, ...) __printflike(1, 2); -void log_debugx(const char *, ...) __printflike(1, 2); - -char *checked_strdup(const char *); +void login(struct ctld_connection *conn); + +void discovery(struct ctld_connection *conn); + bool valid_iscsi_name(const char *name); void set_timeout(int timeout, int fatal); -bool timed_out(void); #endif /* !CTLD_H */ diff --git a/usr.sbin/ctld/discovery.c b/usr.sbin/ctld/discovery.c index d054aee4078c..242c5c7bca52 100644 --- a/usr.sbin/ctld/discovery.c +++ b/usr.sbin/ctld/discovery.c @@ -1,337 +1,337 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 The FreeBSD Foundation * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "ctld.h" #include "iscsi_proto.h" static struct pdu * text_receive(struct connection *conn) { struct pdu *request; struct iscsi_bhs_text_request *bhstr; request = pdu_new(conn); pdu_receive(request); if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != ISCSI_BHS_OPCODE_TEXT_REQUEST) log_errx(1, "protocol error: received invalid opcode 0x%x", request->pdu_bhs->bhs_opcode); bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; #if 0 if ((bhstr->bhstr_flags & ISCSI_BHSTR_FLAGS_FINAL) == 0) log_errx(1, "received Text PDU without the \"F\" flag"); #endif /* * XXX: Implement the C flag some day. */ if ((bhstr->bhstr_flags & BHSTR_FLAGS_CONTINUE) != 0) log_errx(1, "received Text PDU with unsupported \"C\" flag"); if (ISCSI_SNLT(ntohl(bhstr->bhstr_cmdsn), conn->conn_cmdsn)) { log_errx(1, "received Text PDU with decreasing CmdSN: " "was %u, is %u", conn->conn_cmdsn, ntohl(bhstr->bhstr_cmdsn)); } if (ntohl(bhstr->bhstr_expstatsn) != conn->conn_statsn) { log_errx(1, "received Text PDU with wrong ExpStatSN: " "is %u, should be %u", ntohl(bhstr->bhstr_expstatsn), conn->conn_statsn); } conn->conn_cmdsn = ntohl(bhstr->bhstr_cmdsn); if ((bhstr->bhstr_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0) conn->conn_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 (ISCSI_SNLT(ntohl(bhslr->bhslr_cmdsn), conn->conn_cmdsn)) { log_errx(1, "received Logout PDU with decreasing CmdSN: " "was %u, is %u", conn->conn_cmdsn, ntohl(bhslr->bhslr_cmdsn)); } if (ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) { log_errx(1, "received Logout PDU with wrong ExpStatSN: " "is %u, should be %u", ntohl(bhslr->bhslr_expstatsn), conn->conn_statsn); } conn->conn_cmdsn = ntohl(bhslr->bhslr_cmdsn); if ((bhslr->bhslr_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0) conn->conn_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); } static void discovery_add_target(struct keys *response_keys, const struct target *targ) { struct port *port; struct portal *portal; char *buf; char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; struct addrinfo *ai; int ret; keys_add(response_keys, "TargetName", targ->t_name); TAILQ_FOREACH(port, &targ->t_ports, p_ts) { if (port->p_portal_group == NULL) continue; TAILQ_FOREACH(portal, &port->p_portal_group->pg_portals, p_next) { ai = portal->p_ai; ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); if (ret != 0) { log_warnx("getnameinfo: %s", gai_strerror(ret)); continue; } switch (ai->ai_addr->sa_family) { case AF_INET: if (strcmp(hbuf, "0.0.0.0") == 0) continue; ret = asprintf(&buf, "%s:%s,%d", hbuf, sbuf, port->p_portal_group->pg_tag); break; case AF_INET6: if (strcmp(hbuf, "::") == 0) continue; ret = asprintf(&buf, "[%s]:%s,%d", hbuf, sbuf, port->p_portal_group->pg_tag); break; default: continue; } if (ret <= 0) log_err(1, "asprintf"); keys_add(response_keys, "TargetAddress", buf); free(buf); } } } static bool -discovery_target_filtered_out(const struct connection *conn, +discovery_target_filtered_out(const struct ctld_connection *conn, const struct port *port) { const struct auth_group *ag; const struct portal_group *pg; const struct target *targ; const struct auth *auth; int error; targ = port->p_target; ag = port->p_auth_group; if (ag == NULL) ag = targ->t_auth_group; pg = conn->conn_portal->p_portal_group; assert(pg->pg_discovery_auth_group != PG_FILTER_UNKNOWN); if (pg->pg_discovery_filter >= PG_FILTER_PORTAL && auth_portal_check(ag, &conn->conn_initiator_sa) != 0) { log_debugx("initiator does not match initiator portals " "allowed for target \"%s\"; skipping", targ->t_name); return (true); } if (pg->pg_discovery_filter >= PG_FILTER_PORTAL_NAME && auth_name_check(ag, conn->conn_initiator_name) != 0) { log_debugx("initiator does not match initiator names " "allowed for target \"%s\"; skipping", targ->t_name); return (true); } if (pg->pg_discovery_filter >= PG_FILTER_PORTAL_NAME_AUTH && ag->ag_type != AG_TYPE_NO_AUTHENTICATION) { if (conn->conn_chap == NULL) { assert(pg->pg_discovery_auth_group->ag_type == AG_TYPE_NO_AUTHENTICATION); log_debugx("initiator didn't authenticate, but target " "\"%s\" requires CHAP; skipping", targ->t_name); return (true); } assert(conn->conn_user != NULL); auth = auth_find(ag, conn->conn_user); if (auth == NULL) { log_debugx("CHAP user \"%s\" doesn't match target " "\"%s\"; skipping", conn->conn_user, targ->t_name); return (true); } error = chap_authenticate(conn->conn_chap, auth->a_secret); if (error != 0) { log_debugx("password for CHAP user \"%s\" doesn't " "match target \"%s\"; skipping", conn->conn_user, targ->t_name); return (true); } } return (false); } void -discovery(struct connection *conn) +discovery(struct ctld_connection *conn) { struct pdu *request, *response; struct keys *request_keys, *response_keys; const struct port *port; const struct portal_group *pg; const char *send_targets; pg = conn->conn_portal->p_portal_group; log_debugx("beginning discovery session; waiting for Text PDU"); - request = text_receive(conn); + request = text_receive(&conn->conn); request_keys = keys_new(); keys_load(request_keys, request); 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(port, &pg->pg_ports, p_pgs) { if (discovery_target_filtered_out(conn, port)) { /* Ignore this target. */ continue; } discovery_add_target(response_keys, port->p_target); } } else { port = port_find_in_pg(pg, send_targets); if (port == NULL) { log_debugx("initiator requested information on unknown " "target \"%s\"; returning nothing", send_targets); } else { if (discovery_target_filtered_out(conn, port)) { /* Ignore this target. */ } else { discovery_add_target(response_keys, port->p_target); } } } 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); + request = logout_receive(&conn->conn); response = logout_new_response(request); pdu_send(response); pdu_delete(response); pdu_delete(request); log_debugx("discovery session done"); } diff --git a/usr.sbin/ctld/kernel.c b/usr.sbin/ctld/kernel.c index cdf2b735b54e..dbbd7c35f2d9 100644 --- a/usr.sbin/ctld/kernel.c +++ b/usr.sbin/ctld/kernel.c @@ -1,1373 +1,1374 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2003, 2004 Silicon Graphics International Corp. * Copyright (c) 1997-2007 Kenneth D. Merry * Copyright (c) 2012 The FreeBSD Foundation * Copyright (c) 2017 Jakub Wojciech Klama * 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. * */ #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 "ctld.h" #ifdef ICL_KERNEL_PROXY #include #endif #define NVLIST_BUFSIZE 1024 extern bool proxy_mode; 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 && errno == ENOENT) { 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); #ifdef WANT_ISCSI else { saved_errno = errno; if (modfind("cfiscsi") == -1 && kldload("cfiscsi") == -1) log_warn("couldn't load cfiscsi"); errno = saved_errno; } #endif } /* * 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; uint8_t device_type; uint64_t size_blocks; uint32_t blocksize; char *serial_number; char *device_id; char *ctld_name; STAILQ_HEAD(,cctl_lun_nv) attr_list; STAILQ_ENTRY(cctl_lun) links; }; struct cctl_port { uint32_t port_id; char *port_frontend; char *port_name; int pp; int vp; int cfiscsi_state; char *cfiscsi_target; uint16_t cfiscsi_portal_group_tag; char *ctld_portal_group_name; STAILQ_HEAD(,cctl_lun_nv) attr_list; STAILQ_ENTRY(cctl_port) links; }; struct cctl_devlist_data { int num_luns; STAILQ_HEAD(,cctl_lun) lun_list; struct cctl_lun *cur_lun; int num_ports; STAILQ_HEAD(,cctl_port) port_list; struct cctl_port *cur_port; 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, "lun_type") == 0) { if (str == NULL) log_errx(1, "%s: %s missing its argument", __func__, name); cur_lun->device_type = strtoull(str, NULL, 0); } else if (strcmp(name, "size") == 0) { if (str == NULL) log_errx(1, "%s: %s missing its argument", __func__, name); cur_lun->size_blocks = strtoull(str, NULL, 0); } else if (strcmp(name, "blocksize") == 0) { if (str == NULL) log_errx(1, "%s: %s missing its argument", __func__, name); 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_name") == 0) { cur_lun->ctld_name = str; str = NULL; } else if (strcmp(name, "lun") == 0) { devlist->cur_lun = NULL; } else if (strcmp(name, "ctllunlist") == 0) { /* Nothing. */ } 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_start_pelement(void *user_data, const char *name, const char **attr) { int i; struct cctl_devlist_data *devlist; struct cctl_port *cur_port; devlist = (struct cctl_devlist_data *)user_data; cur_port = devlist->cur_port; 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, "targ_port") == 0) { if (cur_port != NULL) log_errx(1, "%s: improper port element nesting (%s)", __func__, name); cur_port = calloc(1, sizeof(*cur_port)); if (cur_port == NULL) log_err(1, "%s: cannot allocate %zd bytes", __func__, sizeof(*cur_port)); devlist->num_ports++; devlist->cur_port = cur_port; STAILQ_INIT(&cur_port->attr_list); STAILQ_INSERT_TAIL(&devlist->port_list, cur_port, links); for (i = 0; attr[i] != NULL; i += 2) { if (strcmp(attr[i], "id") == 0) { cur_port->port_id = strtoul(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_pelement(void *user_data, const char *name) { struct cctl_devlist_data *devlist; struct cctl_port *cur_port; char *str; devlist = (struct cctl_devlist_data *)user_data; cur_port = devlist->cur_port; if ((cur_port == NULL) && (strcmp(name, "ctlportlist") != 0)) log_errx(1, "%s: cur_port == 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, "frontend_type") == 0) { cur_port->port_frontend = str; str = NULL; } else if (strcmp(name, "port_name") == 0) { cur_port->port_name = str; str = NULL; } else if (strcmp(name, "physical_port") == 0) { if (str == NULL) log_errx(1, "%s: %s missing its argument", __func__, name); cur_port->pp = strtoul(str, NULL, 0); } else if (strcmp(name, "virtual_port") == 0) { if (str == NULL) log_errx(1, "%s: %s missing its argument", __func__, name); cur_port->vp = strtoul(str, NULL, 0); } else if (strcmp(name, "cfiscsi_target") == 0) { cur_port->cfiscsi_target = str; str = NULL; } else if (strcmp(name, "cfiscsi_state") == 0) { if (str == NULL) log_errx(1, "%s: %s missing its argument", __func__, name); cur_port->cfiscsi_state = strtoul(str, NULL, 0); } else if (strcmp(name, "cfiscsi_portal_group_tag") == 0) { if (str == NULL) log_errx(1, "%s: %s missing its argument", __func__, name); cur_port->cfiscsi_portal_group_tag = strtoul(str, NULL, 0); } else if (strcmp(name, "ctld_portal_group_name") == 0) { cur_port->ctld_portal_group_name = str; str = NULL; } else if (strcmp(name, "targ_port") == 0) { devlist->cur_port = NULL; } else if (strcmp(name, "ctlportlist") == 0) { /* Nothing. */ } 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_port->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 portal_group *pg; struct pport *pp; struct port *cp; struct lun *cl; struct option *o; struct ctl_lun_list list; struct cctl_devlist_data devlist; struct cctl_lun *lun; struct cctl_port *port; XML_Parser parser; char *str, *name; int len, retval; bzero(&devlist, sizeof(devlist)); STAILQ_INIT(&devlist.lun_list); STAILQ_INIT(&devlist.port_list); log_debugx("obtaining previously configured CTL luns from the kernel"); str = NULL; len = 4096; retry: str = realloc(str, len); if (str == NULL) log_err(1, "realloc"); bzero(&list, sizeof(list)); list.alloc_len = len; list.status = CTL_LUN_LIST_NONE; list.lun_xml = str; if (ioctl(ctl_fd, CTL_LUN_LIST, &list) == -1) { log_warn("error issuing CTL_LUN_LIST ioctl"); free(str); return (NULL); } if (list.status == CTL_LUN_LIST_ERROR) { log_warnx("error returned from CTL_LUN_LIST ioctl: %s", list.error_str); free(str); return (NULL); } if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) { len = len << 1; goto retry; } parser = XML_ParserCreate(NULL); if (parser == NULL) { log_warnx("unable to create XML parser"); free(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, str, strlen(str), 1); XML_ParserFree(parser); free(str); if (retval != 1) { log_warnx("XML_Parse failed"); return (NULL); } str = NULL; len = 4096; retry_port: str = realloc(str, len); if (str == NULL) log_err(1, "realloc"); bzero(&list, sizeof(list)); list.alloc_len = len; list.status = CTL_LUN_LIST_NONE; list.lun_xml = str; if (ioctl(ctl_fd, CTL_PORT_LIST, &list) == -1) { log_warn("error issuing CTL_PORT_LIST ioctl"); free(str); return (NULL); } if (list.status == CTL_LUN_LIST_ERROR) { log_warnx("error returned from CTL_PORT_LIST ioctl: %s", list.error_str); free(str); return (NULL); } if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) { len = len << 1; goto retry_port; } parser = XML_ParserCreate(NULL); if (parser == NULL) { log_warnx("unable to create XML parser"); free(str); return (NULL); } XML_SetUserData(parser, &devlist); XML_SetElementHandler(parser, cctl_start_pelement, cctl_end_pelement); XML_SetCharacterDataHandler(parser, cctl_char_handler); retval = XML_Parse(parser, str, strlen(str), 1); XML_ParserFree(parser); free(str); if (retval != 1) { log_warnx("XML_Parse failed"); return (NULL); } conf = conf_new(); name = NULL; STAILQ_FOREACH(port, &devlist.port_list, links) { if (strcmp(port->port_frontend, "ha") == 0) continue; free(name); if (port->pp == 0 && port->vp == 0) { name = checked_strdup(port->port_name); } else if (port->vp == 0) { retval = asprintf(&name, "%s/%d", port->port_name, port->pp); if (retval <= 0) log_err(1, "asprintf"); } else { retval = asprintf(&name, "%s/%d/%d", port->port_name, port->pp, port->vp); if (retval <= 0) log_err(1, "asprintf"); } if (port->cfiscsi_target == NULL) { log_debugx("CTL port %u \"%s\" wasn't managed by ctld; ", port->port_id, name); pp = pport_find(conf, name); if (pp == NULL) { #if 0 log_debugx("found new kernel port %u \"%s\"", port->port_id, name); #endif pp = pport_new(conf, name, port->port_id); if (pp == NULL) { log_warnx("pport_new failed"); continue; } } continue; } if (port->cfiscsi_state != 1) { log_debugx("CTL port %ju is not active (%d); ignoring", (uintmax_t)port->port_id, port->cfiscsi_state); continue; } targ = target_find(conf, port->cfiscsi_target); if (targ == NULL) { #if 0 log_debugx("found new kernel target %s for CTL port %ld", port->cfiscsi_target, port->port_id); #endif targ = target_new(conf, port->cfiscsi_target); if (targ == NULL) { log_warnx("target_new failed"); continue; } } if (port->ctld_portal_group_name == NULL) continue; pg = portal_group_find(conf, port->ctld_portal_group_name); if (pg == NULL) { #if 0 log_debugx("found new kernel portal group %s for CTL port %ld", port->ctld_portal_group_name, port->port_id); #endif pg = portal_group_new(conf, port->ctld_portal_group_name); if (pg == NULL) { log_warnx("portal_group_new failed"); continue; } } pg->pg_tag = port->cfiscsi_portal_group_tag; cp = port_new(conf, targ, pg); if (cp == NULL) { log_warnx("port_new failed"); continue; } cp->p_ctl_port = port->port_id; } free(name); STAILQ_FOREACH(lun, &devlist.lun_list, links) { struct cctl_lun_nv *nv; if (lun->ctld_name == NULL) { log_debugx("CTL lun %ju wasn't managed by ctld; " "ignoring", (uintmax_t)lun->lun_id); continue; } cl = lun_find(conf, lun->ctld_name); if (cl != NULL) { log_warnx("found CTL lun %ju \"%s\", " "also backed by CTL lun %d; ignoring", (uintmax_t)lun->lun_id, lun->ctld_name, cl->l_ctl_lun); continue; } log_debugx("found CTL lun %ju \"%s\"", (uintmax_t)lun->lun_id, lun->ctld_name); cl = lun_new(conf, lun->ctld_name); if (cl == NULL) { log_warnx("lun_new failed"); continue; } lun_set_backend(cl, lun->backend_type); lun_set_device_type(cl, lun->device_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; } o = option_new(&cl->l_options, nv->name, nv->value); if (o == NULL) log_warnx("unable to add CTL lun option %s " "for CTL lun %ju \"%s\"", nv->name, (uintmax_t) lun->lun_id, cl->l_name); } } return (conf); } int kernel_lun_add(struct lun *lun) { struct option *o; struct ctl_lun_req req; int error; 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; if (lun->l_ctl_lun >= 0) { req.reqdata.create.req_lun_id = lun->l_ctl_lun; req.reqdata.create.flags |= CTL_LUN_FLAG_ID_REQ; } req.reqdata.create.flags |= CTL_LUN_FLAG_DEV_TYPE; req.reqdata.create.device_type = lun->l_device_type; if (lun->l_serial != NULL) { strncpy(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) { strncpy(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) { o = option_find(&lun->l_options, "file"); if (o != NULL) { option_set(o, lun->l_path); } else { o = option_new(&lun->l_options, "file", lun->l_path); assert(o != NULL); } } o = option_find(&lun->l_options, "ctld_name"); if (o != NULL) { option_set(o, lun->l_name); } else { o = option_new(&lun->l_options, "ctld_name", lun->l_name); assert(o != NULL); } o = option_find(&lun->l_options, "scsiname"); if (o == NULL && lun->l_scsiname != NULL) { o = option_new(&lun->l_options, "scsiname", lun->l_scsiname); assert(o != NULL); } if (!TAILQ_EMPTY(&lun->l_options)) { req.args_nvl = nvlist_create(0); if (req.args_nvl == NULL) { log_warn("error allocating nvlist"); return (1); } TAILQ_FOREACH(o, &lun->l_options, o_next) nvlist_add_string(req.args_nvl, o->o_name, o->o_value); req.args = nvlist_pack(req.args_nvl, &req.args_len); if (req.args == NULL) { log_warn("error packing nvlist"); return (1); } } error = ioctl(ctl_fd, CTL_LUN_REQ, &req); nvlist_destroy(req.args_nvl); if (error != 0) { log_warn("error issuing CTL_LUN_REQ ioctl"); return (1); } switch (req.status) { case CTL_LUN_ERROR: log_warnx("LUN creation error: %s", req.error_str); return (1); case CTL_LUN_WARNING: log_warnx("LUN creation warning: %s", req.error_str); break; case CTL_LUN_OK: break; default: log_warnx("unknown LUN creation status: %d", req.status); return (1); } lun_set_ctl_lun(lun, req.reqdata.create.req_lun_id); return (0); } int kernel_lun_modify(struct lun *lun) { struct option *o; struct ctl_lun_req req; int error; 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 (lun->l_path != NULL) { o = option_find(&lun->l_options, "file"); if (o != NULL) { option_set(o, lun->l_path); } else { o = option_new(&lun->l_options, "file", lun->l_path); assert(o != NULL); } } o = option_find(&lun->l_options, "ctld_name"); if (o != NULL) { option_set(o, lun->l_name); } else { o = option_new(&lun->l_options, "ctld_name", lun->l_name); assert(o != NULL); } o = option_find(&lun->l_options, "scsiname"); if (o == NULL && lun->l_scsiname != NULL) { o = option_new(&lun->l_options, "scsiname", lun->l_scsiname); assert(o != NULL); } if (!TAILQ_EMPTY(&lun->l_options)) { req.args_nvl = nvlist_create(0); if (req.args_nvl == NULL) { log_warn("error allocating nvlist"); return (1); } TAILQ_FOREACH(o, &lun->l_options, o_next) nvlist_add_string(req.args_nvl, o->o_name, o->o_value); req.args = nvlist_pack(req.args_nvl, &req.args_len); if (req.args == NULL) { log_warn("error packing nvlist"); return (1); } } error = ioctl(ctl_fd, CTL_LUN_REQ, &req); nvlist_destroy(req.args_nvl); if (error != 0) { log_warn("error issuing CTL_LUN_REQ ioctl"); return (1); } switch (req.status) { case CTL_LUN_ERROR: log_warnx("LUN modification error: %s", req.error_str); return (1); case CTL_LUN_WARNING: log_warnx("LUN modification warning: %s", req.error_str); break; case CTL_LUN_OK: break; default: log_warnx("unknown LUN modification 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); } switch (req.status) { case CTL_LUN_ERROR: log_warnx("LUN removal error: %s", req.error_str); return (1); case CTL_LUN_WARNING: log_warnx("LUN removal warning: %s", req.error_str); break; case CTL_LUN_OK: break; default: log_warnx("unknown LUN removal status: %d", req.status); return (1); } return (0); } void -kernel_handoff(struct connection *conn) +kernel_handoff(struct ctld_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)); } memcpy(req.data.handoff.initiator_isid, conn->conn_initiator_isid, sizeof(req.data.handoff.initiator_isid)); strlcpy(req.data.handoff.target_name, conn->conn_target->t_name, sizeof(req.data.handoff.target_name)); if (conn->conn_portal->p_portal_group->pg_offload != NULL) { strlcpy(req.data.handoff.offload, conn->conn_portal->p_portal_group->pg_offload, sizeof(req.data.handoff.offload)); } #ifdef ICL_KERNEL_PROXY if (proxy_mode) - req.data.handoff.connection_id = conn->conn_socket; + req.data.handoff.connection_id = conn->conn.conn_socket; else - req.data.handoff.socket = conn->conn_socket; + req.data.handoff.socket = conn->conn.conn_socket; #else - req.data.handoff.socket = conn->conn_socket; + req.data.handoff.socket = conn->conn.conn_socket; #endif req.data.handoff.portal_group_tag = conn->conn_portal->p_portal_group->pg_tag; - if (conn->conn_header_digest == CONN_DIGEST_CRC32C) + if (conn->conn.conn_header_digest == CONN_DIGEST_CRC32C) req.data.handoff.header_digest = CTL_ISCSI_DIGEST_CRC32C; - if (conn->conn_data_digest == CONN_DIGEST_CRC32C) + if (conn->conn.conn_data_digest == CONN_DIGEST_CRC32C) req.data.handoff.data_digest = CTL_ISCSI_DIGEST_CRC32C; - req.data.handoff.cmdsn = conn->conn_cmdsn; - req.data.handoff.statsn = conn->conn_statsn; + req.data.handoff.cmdsn = conn->conn.conn_cmdsn; + req.data.handoff.statsn = conn->conn.conn_statsn; req.data.handoff.max_recv_data_segment_length = - conn->conn_max_recv_data_segment_length; + conn->conn.conn_max_recv_data_segment_length; req.data.handoff.max_send_data_segment_length = - conn->conn_max_send_data_segment_length; - req.data.handoff.max_burst_length = conn->conn_max_burst_length; - req.data.handoff.first_burst_length = conn->conn_first_burst_length; - req.data.handoff.immediate_data = conn->conn_immediate_data; + conn->conn.conn_max_send_data_segment_length; + req.data.handoff.max_burst_length = conn->conn.conn_max_burst_length; + req.data.handoff.first_burst_length = + conn->conn.conn_first_burst_length; + req.data.handoff.immediate_data = conn->conn.conn_immediate_data; if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) { log_err(1, "error issuing CTL_ISCSI ioctl; " "dropping connection"); } if (req.status != CTL_ISCSI_OK) { log_errx(1, "error returned from CTL iSCSI handoff request: " "%s; dropping connection", req.error_str); } } void kernel_limits(const char *offload, int *max_recv_dsl, int *max_send_dsl, int *max_burst_length, int *first_burst_length) { struct ctl_iscsi req; struct ctl_iscsi_limits_params *cilp; bzero(&req, sizeof(req)); req.type = CTL_ISCSI_LIMITS; cilp = (struct ctl_iscsi_limits_params *)&(req.data.limits); if (offload != NULL) { strlcpy(cilp->offload, offload, sizeof(cilp->offload)); } 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 limits request: " "%s; dropping connection", req.error_str); } if (cilp->max_recv_data_segment_length != 0) { *max_recv_dsl = cilp->max_recv_data_segment_length; *max_send_dsl = cilp->max_recv_data_segment_length; } if (cilp->max_send_data_segment_length != 0) *max_send_dsl = cilp->max_send_data_segment_length; if (cilp->max_burst_length != 0) *max_burst_length = cilp->max_burst_length; if (cilp->first_burst_length != 0) *first_burst_length = cilp->first_burst_length; if (*max_burst_length < *first_burst_length) *first_burst_length = *max_burst_length; if (offload != NULL) { log_debugx("Kernel limits for offload \"%s\" are " "MaxRecvDataSegment=%d, max_send_dsl=%d, " "MaxBurstLength=%d, FirstBurstLength=%d", offload, *max_recv_dsl, *max_send_dsl, *max_burst_length, *first_burst_length); } else { log_debugx("Kernel limits are " "MaxRecvDataSegment=%d, max_send_dsl=%d, " "MaxBurstLength=%d, FirstBurstLength=%d", *max_recv_dsl, *max_send_dsl, *max_burst_length, *first_burst_length); } } int kernel_port_add(struct port *port) { struct option *o; struct ctl_port_entry entry; struct ctl_req req; struct ctl_lun_map lm; struct target *targ = port->p_target; struct portal_group *pg = port->p_portal_group; char result_buf[NVLIST_BUFSIZE]; int error, i; /* Create iSCSI port. */ if (port->p_portal_group || port->p_ioctl_port) { bzero(&req, sizeof(req)); req.reqtype = CTL_REQ_CREATE; if (port->p_portal_group) { strlcpy(req.driver, "iscsi", sizeof(req.driver)); req.args_nvl = nvlist_create(0); nvlist_add_string(req.args_nvl, "cfiscsi_target", targ->t_name); nvlist_add_string(req.args_nvl, "ctld_portal_group_name", pg->pg_name); nvlist_add_stringf(req.args_nvl, "cfiscsi_portal_group_tag", "%u", pg->pg_tag); if (targ->t_alias) { nvlist_add_string(req.args_nvl, "cfiscsi_target_alias", targ->t_alias); } TAILQ_FOREACH(o, &pg->pg_options, o_next) nvlist_add_string(req.args_nvl, o->o_name, o->o_value); } if (port->p_ioctl_port) { strlcpy(req.driver, "ioctl", sizeof(req.driver)); req.args_nvl = nvlist_create(0); nvlist_add_stringf(req.args_nvl, "pp", "%d", port->p_ioctl_pp); nvlist_add_stringf(req.args_nvl, "vp", "%d", port->p_ioctl_vp); } req.args = nvlist_pack(req.args_nvl, &req.args_len); if (req.args == NULL) { log_warn("error packing nvlist"); return (1); } req.result = result_buf; req.result_len = sizeof(result_buf); error = ioctl(ctl_fd, CTL_PORT_REQ, &req); nvlist_destroy(req.args_nvl); if (error != 0) { log_warn("error issuing CTL_PORT_REQ ioctl"); return (1); } if (req.status == CTL_LUN_ERROR) { log_warnx("error returned from port creation request: %s", req.error_str); return (1); } if (req.status != CTL_LUN_OK) { log_warnx("unknown port creation request status %d", req.status); return (1); } req.result_nvl = nvlist_unpack(result_buf, req.result_len, 0); if (req.result_nvl == NULL) { log_warnx("error unpacking result nvlist"); return (1); } port->p_ctl_port = nvlist_get_number(req.result_nvl, "port_id"); nvlist_destroy(req.result_nvl); } else if (port->p_pport) { port->p_ctl_port = port->p_pport->pp_ctl_port; if (strncmp(targ->t_name, "naa.", 4) == 0 && strlen(targ->t_name) == 20) { bzero(&entry, sizeof(entry)); entry.port_type = CTL_PORT_NONE; entry.targ_port = port->p_ctl_port; entry.flags |= CTL_PORT_WWNN_VALID; entry.wwnn = strtoull(targ->t_name + 4, NULL, 16); if (ioctl(ctl_fd, CTL_SET_PORT_WWNS, &entry) == -1) log_warn("CTL_SET_PORT_WWNS ioctl failed"); } } /* Explicitly enable mapping to block any access except allowed. */ lm.port = port->p_ctl_port; lm.plun = UINT32_MAX; lm.lun = 0; error = ioctl(ctl_fd, CTL_LUN_MAP, &lm); if (error != 0) log_warn("CTL_LUN_MAP ioctl failed"); /* Map configured LUNs */ for (i = 0; i < MAX_LUNS; i++) { if (targ->t_luns[i] == NULL) continue; lm.port = port->p_ctl_port; lm.plun = i; lm.lun = targ->t_luns[i]->l_ctl_lun; error = ioctl(ctl_fd, CTL_LUN_MAP, &lm); if (error != 0) log_warn("CTL_LUN_MAP ioctl failed"); } /* Enable port */ bzero(&entry, sizeof(entry)); entry.targ_port = port->p_ctl_port; 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_update(struct port *port, struct port *oport) { struct ctl_lun_map lm; struct target *targ = port->p_target; struct target *otarg = oport->p_target; int error, i; uint32_t olun; /* Map configured LUNs and unmap others */ for (i = 0; i < MAX_LUNS; i++) { lm.port = port->p_ctl_port; lm.plun = i; if (targ->t_luns[i] == NULL) lm.lun = UINT32_MAX; else lm.lun = targ->t_luns[i]->l_ctl_lun; if (otarg->t_luns[i] == NULL) olun = UINT32_MAX; else olun = otarg->t_luns[i]->l_ctl_lun; if (lm.lun == olun) continue; error = ioctl(ctl_fd, CTL_LUN_MAP, &lm); if (error != 0) log_warn("CTL_LUN_MAP ioctl failed"); } return (0); } int kernel_port_remove(struct port *port) { struct ctl_port_entry entry; struct ctl_lun_map lm; struct ctl_req req; struct target *targ = port->p_target; struct portal_group *pg = port->p_portal_group; int error; /* Disable port */ bzero(&entry, sizeof(entry)); entry.targ_port = port->p_ctl_port; error = ioctl(ctl_fd, CTL_DISABLE_PORT, &entry); if (error != 0) { log_warn("CTL_DISABLE_PORT ioctl failed"); return (-1); } /* Remove iSCSI or ioctl port. */ if (port->p_portal_group || port->p_ioctl_port) { bzero(&req, sizeof(req)); strlcpy(req.driver, port->p_ioctl_port ? "ioctl" : "iscsi", sizeof(req.driver)); req.reqtype = CTL_REQ_REMOVE; req.args_nvl = nvlist_create(0); if (req.args_nvl == NULL) log_err(1, "nvlist_create"); if (port->p_ioctl_port) nvlist_add_stringf(req.args_nvl, "port_id", "%d", port->p_ctl_port); else { nvlist_add_string(req.args_nvl, "cfiscsi_target", targ->t_name); nvlist_add_stringf(req.args_nvl, "cfiscsi_portal_group_tag", "%u", pg->pg_tag); } req.args = nvlist_pack(req.args_nvl, &req.args_len); if (req.args == NULL) { log_warn("error packing nvlist"); return (1); } error = ioctl(ctl_fd, CTL_PORT_REQ, &req); nvlist_destroy(req.args_nvl); if (error != 0) { log_warn("error issuing CTL_PORT_REQ ioctl"); return (1); } if (req.status == CTL_LUN_ERROR) { log_warnx("error returned from port removal request: %s", req.error_str); return (1); } if (req.status != CTL_LUN_OK) { log_warnx("unknown port removal request status %d", req.status); return (1); } } else { /* Disable LUN mapping. */ lm.port = port->p_ctl_port; lm.plun = UINT32_MAX; lm.lun = UINT32_MAX; error = ioctl(ctl_fd, CTL_LUN_MAP, &lm); if (error != 0) log_warn("CTL_LUN_MAP ioctl failed"); } return (0); } #ifdef ICL_KERNEL_PROXY void kernel_listen(struct addrinfo *ai, bool iser, int portal_id) { 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; req.data.listen.portal_id = portal_id; if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) log_err(1, "error issuing CTL_ISCSI ioctl"); if (req.status != CTL_ISCSI_OK) { log_errx(1, "error returned from CTL iSCSI listen: %s", req.error_str); } } void kernel_accept(int *connection_id, int *portal_id, struct sockaddr *client_sa, socklen_t *client_salen) { struct ctl_iscsi req; struct sockaddr_storage ss; bzero(&req, sizeof(req)); req.type = CTL_ISCSI_ACCEPT; req.data.accept.initiator_addr = (struct sockaddr *)&ss; if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) log_err(1, "error issuing CTL_ISCSI ioctl"); if (req.status != CTL_ISCSI_OK) { log_errx(1, "error returned from CTL iSCSI accept: %s", req.error_str); } *connection_id = req.data.accept.connection_id; *portal_id = req.data.accept.portal_id; *client_salen = req.data.accept.initiator_addrlen; memcpy(client_sa, &ss, *client_salen); } 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 connection *conn; struct ctl_iscsi req; conn = pdu->pdu_connection; pdu->pdu_data = malloc(conn->conn_max_recv_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 = conn->conn_socket; req.data.receive.bhs = pdu->pdu_bhs; req.data.receive.data_segment_len = conn->conn_max_recv_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) { cap_rights_t rights; const unsigned long cmds[] = { CTL_ISCSI }; cap_rights_init(&rights, CAP_IOCTL); if (caph_rights_limit(ctl_fd, &rights) < 0) log_err(1, "cap_rights_limit"); if (caph_ioctls_limit(ctl_fd, cmds, nitems(cmds)) < 0) log_err(1, "cap_ioctls_limit"); if (caph_enter() < 0) log_err(1, "cap_enter"); if (cap_sandboxed()) log_debugx("Capsicum capability mode enabled"); else log_warnx("Capsicum capability mode not supported"); } diff --git a/usr.sbin/ctld/login.c b/usr.sbin/ctld/login.c index 63b2cfd51d92..da69961e40ae 100644 --- a/usr.sbin/ctld/login.c +++ b/usr.sbin/ctld/login.c @@ -1,1059 +1,1060 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 The FreeBSD Foundation * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include "ctld.h" #include "iscsi_proto.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; bhslr->bhslr_flags |= BHSLR_FLAGS_TRANSIT; } 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 (initial == false && ISCSI_SNLT(ntohl(bhslr->bhslr_cmdsn), conn->conn_cmdsn)) { login_send_error(request, 0x02, 0x00); log_errx(1, "received Login PDU with decreasing CmdSN: " "was %u, is %u", conn->conn_cmdsn, ntohl(bhslr->bhslr_cmdsn)); } if (initial == false && ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) { login_send_error(request, 0x02, 0x00); log_errx(1, "received Login PDU with wrong ExpStatSN: " "is %u, should be %u", 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 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, struct chap *chap) { struct pdu *response; struct keys *response_keys; char *chap_c, *chap_i; chap_c = chap_get_challenge(chap); chap_i = chap_get_id(chap); 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_i); free(chap_c); keys_save(response_keys, response); pdu_send(response); pdu_delete(response); keys_delete(response_keys); } static struct pdu * login_receive_chap_r(struct connection *conn, struct auth_group *ag, struct chap *chap, const struct auth **authp) { struct pdu *request; struct keys *request_keys; const char *chap_n, *chap_r; 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 = chap_receive(chap, chap_r); 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); error = chap_authenticate(chap, auth->a_secret); if (error != 0) { login_send_error(request, 0x02, 0x01); log_errx(1, "CHAP authentication failed for user \"%s\"", auth->a_user); } keys_delete(request_keys); *authp = 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 rchap *rchap; const char *chap_i, *chap_c; char *chap_r; int error; response = login_new_response(request); 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); } log_debugx("performing mutual authentication as user \"%s\"", auth->a_mutual_user); rchap = rchap_new(auth->a_mutual_secret); error = rchap_receive(rchap, chap_i, chap_c); if (error != 0) { login_send_error(request, 0x02, 0x07); log_errx(1, "received CHAP Login PDU with malformed " "CHAP_I or CHAP_C"); } chap_r = rchap_get_response(rchap); rchap_delete(rchap); 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); pdu_delete(response); } static void -login_chap(struct connection *conn, struct auth_group *ag) +login_chap(struct ctld_connection *conn, struct auth_group *ag) { const struct auth *auth; struct chap *chap; struct pdu *request; /* * Receive CHAP_A PDU. */ log_debugx("beginning CHAP authentication; waiting for CHAP_A"); - request = login_receive_chap_a(conn); + request = login_receive_chap_a(&conn->conn); /* * Generate the challenge. */ chap = chap_new(); /* * Send the challenge. */ log_debugx("sending CHAP_C, binary challenge size is %zd bytes", sizeof(chap->chap_challenge)); login_send_chap_c(request, chap); 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, chap, &auth); + request = login_receive_chap_r(&conn->conn, ag, chap, &auth); /* * Yay, authentication succeeded! */ log_debugx("authentication succeeded for user \"%s\"; " "transitioning to operational parameter negotiation", auth->a_user); login_send_chap_success(request, auth); pdu_delete(request); /* * Leave username and CHAP information for discovery(). */ conn->conn_user = auth->a_user; conn->conn_chap = chap; } static void login_negotiate_key(struct pdu *request, const char *name, const char *value, bool skipped_security, struct keys *response_keys) { int which; size_t tmp; - struct connection *conn; + struct ctld_connection *conn; - conn = request->pdu_connection; + conn = (struct ctld_connection *)request->pdu_connection; if (strcmp(name, "InitiatorName") == 0) { if (!skipped_security) 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; + conn->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; + conn->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; + conn->conn.conn_immediate_data = true; keys_add(response_keys, name, "Yes"); } else { - conn->conn_immediate_data = false; + conn->conn.conn_immediate_data = false; keys_add(response_keys, name, "No"); } } } 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"); } /* * MaxRecvDataSegmentLength is a direction-specific parameter. * We'll limit our _send_ to what the initiator can handle but * our MaxRecvDataSegmentLength is not influenced by the * initiator in any way. */ if ((int)tmp > conn->conn_max_send_data_segment_limit) { log_debugx("capping MaxRecvDataSegmentLength " "from %zd to %d", tmp, conn->conn_max_send_data_segment_limit); tmp = conn->conn_max_send_data_segment_limit; } - conn->conn_max_send_data_segment_length = tmp; + conn->conn.conn_max_send_data_segment_length = tmp; } else if (strcmp(name, "MaxBurstLength") == 0) { tmp = strtoul(value, NULL, 10); if (tmp <= 0) { login_send_error(request, 0x02, 0x00); log_errx(1, "received invalid MaxBurstLength"); } if ((int)tmp > conn->conn_max_burst_limit) { log_debugx("capping MaxBurstLength from %zd to %d", tmp, conn->conn_max_burst_limit); tmp = conn->conn_max_burst_limit; } - conn->conn_max_burst_length = tmp; + conn->conn.conn_max_burst_length = tmp; keys_add_int(response_keys, name, tmp); } else if (strcmp(name, "FirstBurstLength") == 0) { tmp = strtoul(value, NULL, 10); if (tmp <= 0) { login_send_error(request, 0x02, 0x00); log_errx(1, "received invalid FirstBurstLength"); } if ((int)tmp > conn->conn_first_burst_limit) { log_debugx("capping FirstBurstLength from %zd to %d", tmp, conn->conn_first_burst_limit); tmp = conn->conn_first_burst_limit; } - conn->conn_first_burst_length = tmp; + conn->conn.conn_first_burst_length = tmp; keys_add_int(response_keys, name, tmp); } else if (strcmp(name, "DefaultTime2Wait") == 0) { keys_add(response_keys, name, value); } 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 if (strcmp(name, "iSCSIProtocolLevel") == 0) { tmp = strtoul(value, NULL, 10); if (tmp > 2) tmp = 2; keys_add_int(response_keys, name, tmp); } else { log_debugx("unknown key \"%s\"; responding " "with NotUnderstood", name); keys_add(response_keys, name, "NotUnderstood"); } } static void login_redirect(struct pdu *request, const char *target_address) { struct pdu *response; struct iscsi_bhs_login_response *bhslr2; struct keys *response_keys; response = login_new_response(request); login_set_csg(response, login_csg(request)); bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; bhslr2->bhslr_status_class = 0x01; bhslr2->bhslr_status_detail = 0x01; response_keys = keys_new(); keys_add(response_keys, "TargetAddress", target_address); keys_save(response_keys, response); pdu_send(response); pdu_delete(response); keys_delete(response_keys); } static bool -login_portal_redirect(struct connection *conn, struct pdu *request) +login_portal_redirect(struct ctld_connection *conn, struct pdu *request) { const struct portal_group *pg; pg = conn->conn_portal->p_portal_group; if (pg->pg_redirection == NULL) return (false); log_debugx("portal-group \"%s\" configured to redirect to %s", pg->pg_name, pg->pg_redirection); login_redirect(request, pg->pg_redirection); return (true); } static bool -login_target_redirect(struct connection *conn, struct pdu *request) +login_target_redirect(struct ctld_connection *conn, struct pdu *request) { const char *target_address; assert(conn->conn_portal->p_portal_group->pg_redirection == NULL); if (conn->conn_target == NULL) return (false); target_address = conn->conn_target->t_redirection; if (target_address == NULL) return (false); log_debugx("target \"%s\" configured to redirect to %s", conn->conn_target->t_name, target_address); login_redirect(request, target_address); return (true); } static void -login_negotiate(struct connection *conn, struct pdu *request) +login_negotiate(struct ctld_connection *conn, struct pdu *request) { struct pdu *response; struct iscsi_bhs_login_response *bhslr2; struct keys *request_keys, *response_keys; int i; bool redirected, skipped_security; if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { /* * Query the kernel for various size limits. In case of * offload, it depends on hardware capabilities. */ assert(conn->conn_target != NULL); conn->conn_max_recv_data_segment_limit = (1 << 24) - 1; conn->conn_max_send_data_segment_limit = (1 << 24) - 1; conn->conn_max_burst_limit = (1 << 24) - 1; conn->conn_first_burst_limit = (1 << 24) - 1; kernel_limits(conn->conn_portal->p_portal_group->pg_offload, &conn->conn_max_recv_data_segment_limit, &conn->conn_max_send_data_segment_limit, &conn->conn_max_burst_limit, &conn->conn_first_burst_limit); /* We expect legal, usable values at this point. */ assert(conn->conn_max_recv_data_segment_limit >= 512); assert(conn->conn_max_recv_data_segment_limit < (1 << 24)); assert(conn->conn_max_send_data_segment_limit >= 512); assert(conn->conn_max_send_data_segment_limit < (1 << 24)); assert(conn->conn_max_burst_limit >= 512); assert(conn->conn_max_burst_limit < (1 << 24)); assert(conn->conn_first_burst_limit >= 512); assert(conn->conn_first_burst_limit < (1 << 24)); assert(conn->conn_first_burst_limit <= conn->conn_max_burst_limit); /* * Limit default send length in case it won't be negotiated. * We can't do it for other limits, since they may affect both * sender and receiver operation, and we must obey defaults. */ if (conn->conn_max_send_data_segment_limit < - conn->conn_max_send_data_segment_length) { - conn->conn_max_send_data_segment_length = + conn->conn.conn_max_send_data_segment_length) { + conn->conn.conn_max_send_data_segment_length = conn->conn_max_send_data_segment_limit; } } else { conn->conn_max_recv_data_segment_limit = MAX_DATA_SEGMENT_LENGTH; conn->conn_max_send_data_segment_limit = MAX_DATA_SEGMENT_LENGTH; } if (request == NULL) { log_debugx("beginning operational parameter negotiation; " "waiting for Login PDU"); - request = login_receive(conn, false); + request = login_receive(&conn->conn, false); skipped_security = false; } else skipped_security = true; /* * RFC 3720, 10.13.5. Status-Class and Status-Detail, says * the redirection SHOULD be accepted by the initiator before * authentication, but MUST be accepted afterwards; that's * why we're doing it here and not earlier. */ redirected = login_target_redirect(conn, request); if (redirected) { log_debugx("initiator redirected; exiting"); exit(0); } 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_tsih = htons(0xbadd); login_set_csg(response, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); login_set_nsg(response, BHSLR_STAGE_FULL_FEATURE_PHASE); response_keys = keys_new(); if (skipped_security && 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); keys_add_int(response_keys, "TargetPortalGroupTag", conn->conn_portal->p_portal_group->pg_tag); } 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); } /* * We'd started with usable values at our end. But a bad initiator * could have presented a large FirstBurstLength and then a smaller * MaxBurstLength (in that order) and because we process the key/value * pairs in the order they are in the request we might have ended up * with illegal values here. */ if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL && - conn->conn_first_burst_length > conn->conn_max_burst_length) { + conn->conn.conn_first_burst_length > + conn->conn.conn_max_burst_length) { log_errx(1, "initiator sent FirstBurstLength > MaxBurstLength"); } - conn->conn_max_recv_data_segment_length = + conn->conn.conn_max_recv_data_segment_length = conn->conn_max_recv_data_segment_limit; keys_add_int(response_keys, "MaxRecvDataSegmentLength", - conn->conn_max_recv_data_segment_length); + conn->conn.conn_max_recv_data_segment_length); log_debugx("operational parameter negotiation done; " "transitioning to Full Feature Phase"); keys_save(response_keys, response); pdu_send(response); pdu_delete(response); keys_delete(response_keys); pdu_delete(request); keys_delete(request_keys); } static void -login_wait_transition(struct connection *conn) +login_wait_transition(struct ctld_connection *conn) { struct pdu *request, *response; struct iscsi_bhs_login_request *bhslr; log_debugx("waiting for state transition request"); - request = login_receive(conn, false); + request = login_receive(&conn->conn, false); bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; if ((bhslr->bhslr_flags & BHSLR_FLAGS_TRANSIT) == 0) { login_send_error(request, 0x02, 0x00); log_errx(1, "got no \"T\" flag after answering AuthMethod"); } log_debugx("got state transition request"); response = login_new_response(request); pdu_delete(request); login_set_nsg(response, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); pdu_send(response); pdu_delete(response); login_negotiate(conn, NULL); } void -login(struct connection *conn) +login(struct ctld_connection *conn) { struct pdu *request, *response; struct iscsi_bhs_login_request *bhslr; struct keys *request_keys, *response_keys; struct auth_group *ag; struct portal_group *pg; const char *initiator_name, *initiator_alias, *session_type, *target_name, *auth_method; bool redirected, fail, trans; /* * 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); + request = login_receive(&conn->conn, true); bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; if (bhslr->bhslr_tsih != 0) { login_send_error(request, 0x02, 0x0a); log_errx(1, "received Login PDU with non-zero TSIH"); } pg = conn->conn_portal->p_portal_group; memcpy(conn->conn_initiator_isid, bhslr->bhslr_isid, sizeof(conn->conn_initiator_isid)); /* * 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); setproctitle("%s (%s)", conn->conn_initiator_addr, conn->conn_initiator_name); redirected = login_portal_redirect(conn, request); if (redirected) { log_debugx("initiator redirected; exiting"); exit(0); } 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_port = port_find_in_pg(pg, target_name); if (conn->conn_port == NULL) { login_send_error(request, 0x02, 0x03); log_errx(1, "requested target \"%s\" not found", target_name); } conn->conn_target = conn->conn_port->p_target; } /* * At this point we know what kind of authentication we need. */ if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { ag = conn->conn_port->p_auth_group; if (ag == NULL) 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_name, ag->ag_name); } else { log_debugx("initiator requests to connect " "to target \"%s\"", conn->conn_target->t_name); } } else { assert(conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY); ag = pg->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"); } } if (ag->ag_type == AG_TYPE_DENY) { login_send_error(request, 0x02, 0x01); log_errx(1, "auth-type is \"deny\""); } if (ag->ag_type == AG_TYPE_UNKNOWN) { /* * This can happen with empty auth-group. */ login_send_error(request, 0x02, 0x01); log_errx(1, "auth-type not set, denying access"); } /* * Enforce initiator-name and initiator-portal. */ if (auth_name_check(ag, initiator_name) != 0) { login_send_error(request, 0x02, 0x02); log_errx(1, "initiator does not match allowed initiator names"); } if (auth_portal_check(ag, &conn->conn_initiator_sa) != 0) { login_send_error(request, 0x02, 0x02); log_errx(1, "initiator does not match allowed " "initiator portals"); } /* * 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, " "but authentication is required"); } keys_delete(request_keys); log_debugx("initiator skipped the authentication, " "and we don't need it; proceeding with negotiation"); login_negotiate(conn, request); return; } fail = false; response = login_new_response(request); response_keys = keys_new(); trans = (bhslr->bhslr_flags & BHSLR_FLAGS_TRANSIT) != 0; auth_method = keys_find(request_keys, "AuthMethod"); if (ag->ag_type == AG_TYPE_NO_AUTHENTICATION) { log_debugx("authentication not required"); if (auth_method == NULL || login_list_contains(auth_method, "None")) { keys_add(response_keys, "AuthMethod", "None"); } else { log_warnx("initiator requests " "AuthMethod \"%s\" instead of \"None\"", auth_method); keys_add(response_keys, "AuthMethod", "Reject"); } if (trans) login_set_nsg(response, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); } else { log_debugx("CHAP authentication required"); if (auth_method == NULL || login_list_contains(auth_method, "CHAP")) { keys_add(response_keys, "AuthMethod", "CHAP"); } else { log_warnx("initiator requests unsupported " "AuthMethod \"%s\" instead of \"CHAP\"", auth_method); keys_add(response_keys, "AuthMethod", "Reject"); fail = true; } } 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); keys_add_int(response_keys, "TargetPortalGroupTag", pg->pg_tag); } keys_save(response_keys, response); pdu_send(response); pdu_delete(response); keys_delete(response_keys); pdu_delete(request); keys_delete(request_keys); if (fail) { log_debugx("sent reject for AuthMethod; exiting"); exit(1); } if (ag->ag_type != AG_TYPE_NO_AUTHENTICATION) { login_chap(conn, ag); login_negotiate(conn, NULL); } else if (trans) { login_negotiate(conn, NULL); } else { login_wait_transition(conn); } } diff --git a/usr.sbin/iscsid/Makefile b/usr.sbin/iscsid/Makefile index e3b1acb636b2..f9694866b7f6 100644 --- a/usr.sbin/iscsid/Makefile +++ b/usr.sbin/iscsid/Makefile @@ -1,14 +1,15 @@ # $FreeBSD$ PACKAGE= iscsi PROG= iscsid -SRCS= chap.c discovery.c iscsid.c keys.c log.c login.c pdu.c +SRCS= discovery.c iscsid.c login.c CFLAGS+= -I${.CURDIR} CFLAGS+= -I${SRCTOP}/sys/cam CFLAGS+= -I${SRCTOP}/sys/dev/iscsi +CFLAGS+= -I${SRCTOP}/lib/libiscsiutil CFLAGS+= -DICL_KERNEL_PROXY MAN= iscsid.8 -LIBADD= md util +LIBADD= iscsiutil md util .include diff --git a/usr.sbin/iscsid/chap.c b/usr.sbin/iscsid/chap.c deleted file mode 100644 index bbedfd77fb0f..000000000000 --- a/usr.sbin/iscsid/chap.c +++ /dev/null @@ -1,423 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2014 The FreeBSD Foundation - * - * This software was developed by Edward Tomasz Napierala under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include - -#include "iscsid.h" - -static void -chap_compute_md5(const char id, const char *secret, - const void *challenge, size_t challenge_len, void *response, - size_t response_len) -{ - MD5_CTX ctx; - - assert(response_len == CHAP_DIGEST_LEN); - - MD5Init(&ctx); - MD5Update(&ctx, &id, sizeof(id)); - MD5Update(&ctx, secret, strlen(secret)); - MD5Update(&ctx, challenge, challenge_len); - MD5Final(response, &ctx); -} - -static int -chap_hex2int(const char hex) -{ - switch (hex) { - case '0': - return (0x00); - case '1': - return (0x01); - case '2': - return (0x02); - case '3': - return (0x03); - case '4': - return (0x04); - case '5': - return (0x05); - case '6': - return (0x06); - case '7': - return (0x07); - case '8': - return (0x08); - case '9': - return (0x09); - case 'a': - case 'A': - return (0x0a); - case 'b': - case 'B': - return (0x0b); - case 'c': - case 'C': - return (0x0c); - case 'd': - case 'D': - return (0x0d); - case 'e': - case 'E': - return (0x0e); - case 'f': - case 'F': - return (0x0f); - default: - return (-1); - } -} - -static int -chap_b642bin(const char *b64, void **binp, size_t *bin_lenp) -{ - char *bin; - int b64_len, bin_len; - - b64_len = strlen(b64); - bin_len = (b64_len + 3) / 4 * 3; - bin = calloc(bin_len, 1); - if (bin == NULL) - log_err(1, "calloc"); - - bin_len = b64_pton(b64, bin, bin_len); - if (bin_len < 0) { - log_warnx("malformed base64 variable"); - free(bin); - return (-1); - } - *binp = bin; - *bin_lenp = bin_len; - return (0); -} - -/* - * XXX: Review this _carefully_. - */ -static int -chap_hex2bin(const char *hex, void **binp, size_t *bin_lenp) -{ - int i, hex_len, nibble; - bool lo = true; /* As opposed to 'hi'. */ - char *bin; - size_t bin_off, bin_len; - - if (strncasecmp(hex, "0b", strlen("0b")) == 0) - return (chap_b642bin(hex + 2, binp, bin_lenp)); - - if (strncasecmp(hex, "0x", strlen("0x")) != 0) { - log_warnx("malformed variable, should start with \"0x\"" - " or \"0b\""); - return (-1); - } - - hex += strlen("0x"); - hex_len = strlen(hex); - if (hex_len < 1) { - log_warnx("malformed variable; doesn't contain anything " - "but \"0x\""); - return (-1); - } - - bin_len = hex_len / 2 + hex_len % 2; - bin = calloc(bin_len, 1); - if (bin == NULL) - log_err(1, "calloc"); - - bin_off = bin_len - 1; - for (i = hex_len - 1; i >= 0; i--) { - nibble = chap_hex2int(hex[i]); - if (nibble < 0) { - log_warnx("malformed variable, invalid char \"%c\"", - hex[i]); - free(bin); - return (-1); - } - - assert(bin_off < bin_len); - if (lo) { - bin[bin_off] = nibble; - lo = false; - } else { - bin[bin_off] |= nibble << 4; - bin_off--; - lo = true; - } - } - - *binp = bin; - *bin_lenp = bin_len; - return (0); -} - -#ifdef USE_BASE64 -static char * -chap_bin2hex(const char *bin, size_t bin_len) -{ - unsigned char *b64, *tmp; - size_t b64_len; - - b64_len = (bin_len + 2) / 3 * 4 + 3; /* +2 for "0b", +1 for '\0'. */ - b64 = malloc(b64_len); - if (b64 == NULL) - log_err(1, "malloc"); - - tmp = b64; - tmp += sprintf(tmp, "0b"); - b64_ntop(bin, bin_len, tmp, b64_len - 2); - - return (b64); -} -#else -static char * -chap_bin2hex(const char *bin, size_t bin_len) -{ - unsigned char *hex, *tmp, ch; - size_t hex_len; - size_t i; - - hex_len = bin_len * 2 + 3; /* +2 for "0x", +1 for '\0'. */ - hex = malloc(hex_len); - if (hex == NULL) - log_err(1, "malloc"); - - tmp = hex; - tmp += sprintf(tmp, "0x"); - for (i = 0; i < bin_len; i++) { - ch = bin[i]; - tmp += sprintf(tmp, "%02x", ch); - } - - return (hex); -} -#endif /* !USE_BASE64 */ - -struct chap * -chap_new(void) -{ - struct chap *chap; - - chap = calloc(1, sizeof(*chap)); - if (chap == NULL) - log_err(1, "calloc"); - - /* - * Generate the challenge. - */ - arc4random_buf(chap->chap_challenge, sizeof(chap->chap_challenge)); - arc4random_buf(&chap->chap_id, sizeof(chap->chap_id)); - - return (chap); -} - -char * -chap_get_id(const struct chap *chap) -{ - char *chap_i; - int ret; - - ret = asprintf(&chap_i, "%d", chap->chap_id); - if (ret < 0) - log_err(1, "asprintf"); - - return (chap_i); -} - -char * -chap_get_challenge(const struct chap *chap) -{ - char *chap_c; - - chap_c = chap_bin2hex(chap->chap_challenge, - sizeof(chap->chap_challenge)); - - return (chap_c); -} - -static int -chap_receive_bin(struct chap *chap, void *response, size_t response_len) -{ - - if (response_len != sizeof(chap->chap_response)) { - log_debugx("got CHAP response with invalid length; " - "got %zd, should be %zd", - response_len, sizeof(chap->chap_response)); - return (1); - } - - memcpy(chap->chap_response, response, response_len); - return (0); -} - -int -chap_receive(struct chap *chap, const char *response) -{ - void *response_bin; - size_t response_bin_len; - int error; - - error = chap_hex2bin(response, &response_bin, &response_bin_len); - if (error != 0) { - log_debugx("got incorrectly encoded CHAP response \"%s\"", - response); - return (1); - } - - error = chap_receive_bin(chap, response_bin, response_bin_len); - free(response_bin); - - return (error); -} - -int -chap_authenticate(struct chap *chap, const char *secret) -{ - char expected_response[CHAP_DIGEST_LEN]; - - chap_compute_md5(chap->chap_id, secret, - chap->chap_challenge, sizeof(chap->chap_challenge), - expected_response, sizeof(expected_response)); - - if (memcmp(chap->chap_response, - expected_response, sizeof(expected_response)) != 0) { - return (-1); - } - - return (0); -} - -void -chap_delete(struct chap *chap) -{ - - free(chap); -} - -struct rchap * -rchap_new(const char *secret) -{ - struct rchap *rchap; - - rchap = calloc(1, sizeof(*rchap)); - if (rchap == NULL) - log_err(1, "calloc"); - - rchap->rchap_secret = checked_strdup(secret); - - return (rchap); -} - -static void -rchap_receive_bin(struct rchap *rchap, const unsigned char id, - const void *challenge, size_t challenge_len) -{ - - rchap->rchap_id = id; - rchap->rchap_challenge = calloc(challenge_len, 1); - if (rchap->rchap_challenge == NULL) - log_err(1, "calloc"); - memcpy(rchap->rchap_challenge, challenge, challenge_len); - rchap->rchap_challenge_len = challenge_len; -} - -int -rchap_receive(struct rchap *rchap, const char *id, const char *challenge) -{ - unsigned char id_bin; - void *challenge_bin; - size_t challenge_bin_len; - - int error; - - id_bin = strtoul(id, NULL, 10); - - error = chap_hex2bin(challenge, &challenge_bin, &challenge_bin_len); - if (error != 0) { - log_debugx("got incorrectly encoded CHAP challenge \"%s\"", - challenge); - return (1); - } - - rchap_receive_bin(rchap, id_bin, challenge_bin, challenge_bin_len); - free(challenge_bin); - - return (0); -} - -static void -rchap_get_response_bin(struct rchap *rchap, - void **responsep, size_t *response_lenp) -{ - void *response_bin; - size_t response_bin_len = CHAP_DIGEST_LEN; - - response_bin = calloc(response_bin_len, 1); - if (response_bin == NULL) - log_err(1, "calloc"); - - chap_compute_md5(rchap->rchap_id, rchap->rchap_secret, - rchap->rchap_challenge, rchap->rchap_challenge_len, - response_bin, response_bin_len); - - *responsep = response_bin; - *response_lenp = response_bin_len; -} - -char * -rchap_get_response(struct rchap *rchap) -{ - void *response; - size_t response_len; - char *chap_r; - - rchap_get_response_bin(rchap, &response, &response_len); - chap_r = chap_bin2hex(response, response_len); - free(response); - - return (chap_r); -} - -void -rchap_delete(struct rchap *rchap) -{ - - free(rchap->rchap_secret); - free(rchap->rchap_challenge); - free(rchap); -} diff --git a/usr.sbin/iscsid/discovery.c b/usr.sbin/iscsid/discovery.c index 801b5c96a441..c60c77307a55 100644 --- a/usr.sbin/iscsid/discovery.c +++ b/usr.sbin/iscsid/discovery.c @@ -1,233 +1,233 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 The FreeBSD Foundation * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include "iscsid.h" #include "iscsi_proto.h" static struct pdu * text_receive(struct connection *conn) { struct pdu *response; struct iscsi_bhs_text_response *bhstr; response = pdu_new(conn); pdu_receive(response); if (response->pdu_bhs->bhs_opcode != ISCSI_BHS_OPCODE_TEXT_RESPONSE) log_errx(1, "protocol error: received invalid opcode 0x%x", response->pdu_bhs->bhs_opcode); bhstr = (struct iscsi_bhs_text_response *)response->pdu_bhs; #if 0 if ((bhstr->bhstr_flags & BHSTR_FLAGS_FINAL) == 0) log_errx(1, "received Text PDU without the \"F\" flag"); #endif /* * XXX: Implement the C flag some day. */ if ((bhstr->bhstr_flags & BHSTR_FLAGS_CONTINUE) != 0) log_errx(1, "received Text PDU with unsupported \"C\" flag"); if (ntohl(bhstr->bhstr_statsn) != conn->conn_statsn + 1) { log_errx(1, "received Text PDU with wrong StatSN: " "is %u, should be %u", 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 %u, should be %u", 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) +kernel_add(const struct iscsid_connection *conn, const char *target) { struct iscsi_session_add isa; int error; memset(&isa, 0, sizeof(isa)); memcpy(&isa.isa_conf, &conn->conn_conf, sizeof(isa.isa_conf)); strlcpy(isa.isa_conf.isc_target, target, sizeof(isa.isa_conf.isc_target)); isa.isa_conf.isc_discovery = 0; error = ioctl(conn->conn_iscsi_fd, ISCSISADD, &isa); if (error != 0) log_warn("failed to add %s: ISCSISADD", target); } static void -kernel_remove(const struct connection *conn) +kernel_remove(const struct iscsid_connection *conn) { struct iscsi_session_remove isr; int error; memset(&isr, 0, sizeof(isr)); isr.isr_session_id = conn->conn_session_id; error = ioctl(conn->conn_iscsi_fd, ISCSISREMOVE, &isr); if (error != 0) log_warn("ISCSISREMOVE"); } void -discovery(struct connection *conn) +discovery(struct iscsid_connection *conn) { struct pdu *request, *response; struct keys *request_keys, *response_keys; int i; log_debugx("beginning discovery session"); - request = text_new_request(conn); + request = text_new_request(&conn->conn); request_keys = keys_new(); keys_add(request_keys, "SendTargets", "All"); keys_save(request_keys, request); 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 = text_receive(&conn->conn); response_keys = keys_new(); keys_load(response_keys, response); for (i = 0; i < KEYS_MAX; i++) { if (response_keys->keys_names[i] == NULL) break; if (strcmp(response_keys->keys_names[i], "TargetName") != 0) continue; log_debugx("adding target %s", response_keys->keys_values[i]); /* * XXX: Validate the target name? */ kernel_add(conn, response_keys->keys_values[i]); } keys_delete(response_keys); pdu_delete(response); log_debugx("removing temporary discovery session"); kernel_remove(conn); #ifdef ICL_KERNEL_PROXY if (conn->conn_conf.isc_iser == 1) { /* * If we're going through the proxy, the kernel already * sent Logout PDU for us and destroyed the session, * so we can't send anything anymore. */ log_debugx("discovery session done"); return; } #endif log_debugx("discovery done; logging out"); - request = logout_new_request(conn); + request = logout_new_request(&conn->conn); pdu_send(request); pdu_delete(request); request = NULL; log_debugx("waiting for Logout Response"); - response = logout_receive(conn); + response = logout_receive(&conn->conn); pdu_delete(response); log_debugx("discovery session done"); } diff --git a/usr.sbin/iscsid/iscsid.c b/usr.sbin/iscsid/iscsid.c index db8b5d4f5e4c..dc28a4f6f0cb 100644 --- a/usr.sbin/iscsid/iscsid.c +++ b/usr.sbin/iscsid/iscsid.c @@ -1,694 +1,755 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 The FreeBSD Foundation * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __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 "iscsid.h" +static bool timed_out(void); +#ifdef ICL_KERNEL_PROXY +static void pdu_receive_proxy(struct pdu *pdu); +static void pdu_send_proxy(struct pdu *pdu); +#endif /* ICL_KERNEL_PROXY */ + static volatile bool sigalrm_received = false; static int nchildren = 0; +static struct connection_ops conn_ops = { + .timed_out = timed_out, +#ifdef ICL_KERNEL_PROXY + .pdu_receive_proxy = pdu_receive_proxy, + .pdu_send_proxy = pdu_send_proxy, +#endif + .fail = fail, +}; + static void usage(void) { fprintf(stderr, "usage: iscsid [-P pidfile][-d][-m maxproc][-t timeout]\n"); exit(1); } -char * -checked_strdup(const char *s) +#ifdef ICL_KERNEL_PROXY + +static void +pdu_receive_proxy(struct pdu *pdu) +{ + struct iscsid_connection *conn; + struct iscsi_daemon_receive idr; + size_t len; + int error; + + conn = (struct iscsid_connection *)pdu->pdu_connection; + assert(conn->conn_conf.isc_iser != 0); + + pdu->pdu_data = malloc(conn->conn.conn_max_recv_data_segment_length); + if (pdu->pdu_data == NULL) + log_err(1, "malloc"); + + memset(&idr, 0, sizeof(idr)); + idr.idr_session_id = conn->conn_session_id; + idr.idr_bhs = pdu->pdu_bhs; + idr.idr_data_segment_len = conn->conn.conn_max_recv_data_segment_length; + idr.idr_data_segment = pdu->pdu_data; + + error = ioctl(conn->conn_iscsi_fd, ISCSIDRECEIVE, &idr); + if (error != 0) + log_err(1, "ISCSIDRECEIVE"); + + len = pdu_ahs_length(pdu); + if (len > 0) + log_errx(1, "protocol error: non-empty AHS"); + + len = pdu_data_segment_length(pdu); + assert(len <= (size_t)conn->conn.conn_max_recv_data_segment_length); + pdu->pdu_data_len = len; +} + +static void +pdu_send_proxy(struct pdu *pdu) { - char *c; + struct iscsid_connection *conn; + struct iscsi_daemon_send ids; + int error; + + conn = (struct iscsid_connection *)pdu->pdu_connection; + assert(conn->conn_conf.isc_iser != 0); - c = strdup(s); - if (c == NULL) - log_err(1, "strdup"); - return (c); + pdu_set_data_segment_length(pdu, pdu->pdu_data_len); + + memset(&ids, 0, sizeof(ids)); + ids.ids_session_id = conn->conn_session_id; + ids.ids_bhs = pdu->pdu_bhs; + ids.ids_data_segment_len = pdu->pdu_data_len; + ids.ids_data_segment = pdu->pdu_data; + + error = ioctl(conn->conn_iscsi_fd, ISCSIDSEND, &ids); + if (error != 0) + log_err(1, "ISCSIDSEND"); } +#endif /* ICL_KERNEL_PROXY */ + static void resolve_addr(const struct connection *conn, const char *address, struct addrinfo **ai, bool initiator_side) { struct addrinfo hints; char *arg, *addr, *ch; const char *port; int error, colons = 0; arg = checked_strdup(address); if (arg[0] == '\0') { fail(conn, "empty address"); log_errx(1, "empty address"); } if (arg[0] == '[') { /* * IPv6 address in square brackets, perhaps with port. */ arg++; addr = strsep(&arg, "]"); if (arg == NULL) { fail(conn, "malformed address"); log_errx(1, "malformed address %s", address); } if (arg[0] == '\0') { port = NULL; } else if (arg[0] == ':') { port = arg + 1; } else { fail(conn, "malformed address"); log_errx(1, "malformed address %s", address); } } 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 = NULL; } else { addr = strsep(&arg, ":"); if (arg == NULL) port = NULL; else port = arg; } } if (port == NULL && !initiator_side) port = "3260"; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; if (initiator_side) hints.ai_flags |= AI_PASSIVE; error = getaddrinfo(addr, port, &hints, ai); if (error != 0) { fail(conn, gai_strerror(error)); log_errx(1, "getaddrinfo for %s failed: %s", address, gai_strerror(error)); } free(addr); } -static struct connection * +static struct iscsid_connection * connection_new(int iscsi_fd, const struct iscsi_daemon_request *request) { - struct connection *conn; + struct iscsid_connection *conn; struct iscsi_session_limits *isl; struct addrinfo *from_ai, *to_ai; const char *from_addr, *to_addr; #ifdef ICL_KERNEL_PROXY struct iscsi_daemon_connect idc; #endif int error, optval; conn = calloc(1, sizeof(*conn)); if (conn == NULL) log_err(1, "calloc"); - /* - * Default values, from RFC 3720, section 12. - */ + connection_init(&conn->conn, &conn_ops, + request->idr_conf.isc_iser != 0); conn->conn_protocol_level = 0; - conn->conn_header_digest = CONN_DIGEST_NONE; - conn->conn_data_digest = CONN_DIGEST_NONE; conn->conn_initial_r2t = true; - conn->conn_immediate_data = true; - conn->conn_max_recv_data_segment_length = 8192; - conn->conn_max_send_data_segment_length = 8192; - conn->conn_max_burst_length = 262144; - conn->conn_first_burst_length = 65536; conn->conn_iscsi_fd = iscsi_fd; conn->conn_session_id = request->idr_session_id; memcpy(&conn->conn_conf, &request->idr_conf, sizeof(conn->conn_conf)); - memcpy(&conn->conn_isid, &request->idr_isid, sizeof(conn->conn_isid)); - conn->conn_tsih = request->idr_tsih; + memcpy(&conn->conn.conn_isid, &request->idr_isid, + sizeof(conn->conn.conn_isid)); + conn->conn.conn_tsih = request->idr_tsih; /* * Read the driver limits and provide reasonable defaults for the ones * the driver doesn't care about. If a max_snd_dsl is not explicitly * provided by the driver then we'll make sure both conn->max_snd_dsl * and isl->max_snd_dsl are set to the rcv_dsl. This preserves historic * behavior. */ isl = &conn->conn_limits; memcpy(isl, &request->idr_limits, sizeof(*isl)); if (isl->isl_max_recv_data_segment_length == 0) isl->isl_max_recv_data_segment_length = (1 << 24) - 1; if (isl->isl_max_send_data_segment_length == 0) isl->isl_max_send_data_segment_length = isl->isl_max_recv_data_segment_length; if (isl->isl_max_burst_length == 0) isl->isl_max_burst_length = (1 << 24) - 1; if (isl->isl_first_burst_length == 0) isl->isl_first_burst_length = (1 << 24) - 1; if (isl->isl_first_burst_length > isl->isl_max_burst_length) isl->isl_first_burst_length = isl->isl_max_burst_length; /* * Limit default send length in case it won't be negotiated. * We can't do it for other limits, since they may affect both * sender and receiver operation, and we must obey defaults. */ - if (conn->conn_max_send_data_segment_length > + if (conn->conn.conn_max_send_data_segment_length > isl->isl_max_send_data_segment_length) { - conn->conn_max_send_data_segment_length = + conn->conn.conn_max_send_data_segment_length = isl->isl_max_send_data_segment_length; } from_addr = conn->conn_conf.isc_initiator_addr; to_addr = conn->conn_conf.isc_target_addr; if (from_addr[0] != '\0') - resolve_addr(conn, from_addr, &from_ai, true); + resolve_addr(&conn->conn, from_addr, &from_ai, true); else from_ai = NULL; - resolve_addr(conn, to_addr, &to_ai, false); + resolve_addr(&conn->conn, to_addr, &to_ai, false); #ifdef ICL_KERNEL_PROXY if (conn->conn_conf.isc_iser) { memset(&idc, 0, sizeof(idc)); idc.idc_session_id = conn->conn_session_id; if (conn->conn_conf.isc_iser) idc.idc_iser = 1; idc.idc_domain = to_ai->ai_family; idc.idc_socktype = to_ai->ai_socktype; idc.idc_protocol = to_ai->ai_protocol; if (from_ai != NULL) { idc.idc_from_addr = from_ai->ai_addr; idc.idc_from_addrlen = from_ai->ai_addrlen; } idc.idc_to_addr = to_ai->ai_addr; idc.idc_to_addrlen = to_ai->ai_addrlen; log_debugx("connecting to %s using ICL kernel proxy", to_addr); error = ioctl(iscsi_fd, ISCSIDCONNECT, &idc); if (error != 0) { - fail(conn, strerror(errno)); + fail(&conn->conn, strerror(errno)); log_err(1, "failed to connect to %s " "using ICL kernel proxy: ISCSIDCONNECT", to_addr); } if (from_ai != NULL) freeaddrinfo(from_ai); freeaddrinfo(to_ai); return (conn); } #endif /* ICL_KERNEL_PROXY */ if (conn->conn_conf.isc_iser) { - fail(conn, "iSER not supported"); + fail(&conn->conn, "iSER not supported"); log_errx(1, "iscsid(8) compiled without ICL_KERNEL_PROXY " "does not support iSER"); } - conn->conn_socket = socket(to_ai->ai_family, to_ai->ai_socktype, + conn->conn.conn_socket = socket(to_ai->ai_family, to_ai->ai_socktype, to_ai->ai_protocol); - if (conn->conn_socket < 0) { - fail(conn, strerror(errno)); + if (conn->conn.conn_socket < 0) { + fail(&conn->conn, strerror(errno)); log_err(1, "failed to create socket for %s", from_addr); } optval = SOCKBUF_SIZE; - if (setsockopt(conn->conn_socket, SOL_SOCKET, SO_RCVBUF, + if (setsockopt(conn->conn.conn_socket, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval)) == -1) log_warn("setsockopt(SO_RCVBUF) failed"); optval = SOCKBUF_SIZE; - if (setsockopt(conn->conn_socket, SOL_SOCKET, SO_SNDBUF, + if (setsockopt(conn->conn.conn_socket, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)) == -1) log_warn("setsockopt(SO_SNDBUF) failed"); optval = 1; - if (setsockopt(conn->conn_socket, SOL_SOCKET, SO_NO_DDP, + if (setsockopt(conn->conn.conn_socket, SOL_SOCKET, SO_NO_DDP, &optval, sizeof(optval)) == -1) log_warn("setsockopt(SO_NO_DDP) failed"); if (conn->conn_conf.isc_dscp != -1) { int tos = conn->conn_conf.isc_dscp << 2; if (to_ai->ai_family == AF_INET) { - if (setsockopt(conn->conn_socket, + if (setsockopt(conn->conn.conn_socket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1) log_warn("setsockopt(IP_TOS) " "failed for %s", from_addr); } else if (to_ai->ai_family == AF_INET6) { - if (setsockopt(conn->conn_socket, + if (setsockopt(conn->conn.conn_socket, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)) == -1) log_warn("setsockopt(IPV6_TCLASS) " "failed for %s", from_addr); } } if (conn->conn_conf.isc_pcp != -1) { int pcp = conn->conn_conf.isc_pcp; if (to_ai->ai_family == AF_INET) { - if (setsockopt(conn->conn_socket, + if (setsockopt(conn->conn.conn_socket, IPPROTO_IP, IP_VLAN_PCP, &pcp, sizeof(pcp)) == -1) log_warn("setsockopt(IP_VLAN_PCP) " "failed for %s", from_addr); } else if (to_ai->ai_family == AF_INET6) { - if (setsockopt(conn->conn_socket, + if (setsockopt(conn->conn.conn_socket, IPPROTO_IPV6, IPV6_VLAN_PCP, &pcp, sizeof(pcp)) == -1) log_warn("setsockopt(IPV6_VLAN_PCP) " "failed for %s", from_addr); } } if (from_ai != NULL) { - error = bind(conn->conn_socket, from_ai->ai_addr, + error = bind(conn->conn.conn_socket, from_ai->ai_addr, from_ai->ai_addrlen); if (error != 0) { - fail(conn, strerror(errno)); + fail(&conn->conn, strerror(errno)); log_err(1, "failed to bind to %s", from_addr); } } log_debugx("connecting to %s", to_addr); - error = connect(conn->conn_socket, to_ai->ai_addr, to_ai->ai_addrlen); + error = connect(conn->conn.conn_socket, to_ai->ai_addr, + to_ai->ai_addrlen); if (error != 0) { - fail(conn, strerror(errno)); + fail(&conn->conn, strerror(errno)); log_err(1, "failed to connect to %s", to_addr); } if (from_ai != NULL) freeaddrinfo(from_ai); freeaddrinfo(to_ai); return (conn); } static void -handoff(struct connection *conn) +handoff(struct iscsid_connection *conn) { struct iscsi_daemon_handoff idh; int error; log_debugx("handing off connection to the kernel"); memset(&idh, 0, sizeof(idh)); idh.idh_session_id = conn->conn_session_id; - idh.idh_socket = conn->conn_socket; + idh.idh_socket = conn->conn.conn_socket; strlcpy(idh.idh_target_alias, conn->conn_target_alias, sizeof(idh.idh_target_alias)); - idh.idh_tsih = conn->conn_tsih; - idh.idh_statsn = conn->conn_statsn; + idh.idh_tsih = conn->conn.conn_tsih; + idh.idh_statsn = conn->conn.conn_statsn; idh.idh_protocol_level = conn->conn_protocol_level; - idh.idh_header_digest = conn->conn_header_digest; - idh.idh_data_digest = conn->conn_data_digest; + idh.idh_header_digest = conn->conn.conn_header_digest; + idh.idh_data_digest = conn->conn.conn_data_digest; idh.idh_initial_r2t = conn->conn_initial_r2t; - idh.idh_immediate_data = conn->conn_immediate_data; + idh.idh_immediate_data = conn->conn.conn_immediate_data; idh.idh_max_recv_data_segment_length = - conn->conn_max_recv_data_segment_length; + conn->conn.conn_max_recv_data_segment_length; idh.idh_max_send_data_segment_length = - conn->conn_max_send_data_segment_length; - idh.idh_max_burst_length = conn->conn_max_burst_length; - idh.idh_first_burst_length = conn->conn_first_burst_length; + conn->conn.conn_max_send_data_segment_length; + idh.idh_max_burst_length = conn->conn.conn_max_burst_length; + idh.idh_first_burst_length = conn->conn.conn_first_burst_length; error = ioctl(conn->conn_iscsi_fd, ISCSIDHANDOFF, &idh); if (error != 0) log_err(1, "ISCSIDHANDOFF"); } void -fail(const struct connection *conn, const char *reason) +fail(const struct connection *base_conn, const char *reason) { + const struct iscsid_connection *conn; struct iscsi_daemon_fail idf; int error, saved_errno; + conn = (const struct iscsid_connection *)base_conn; saved_errno = errno; memset(&idf, 0, sizeof(idf)); idf.idf_session_id = conn->conn_session_id; strlcpy(idf.idf_reason, reason, sizeof(idf.idf_reason)); error = ioctl(conn->conn_iscsi_fd, ISCSIDFAIL, &idf); if (error != 0) log_err(1, "ISCSIDFAIL"); errno = saved_errno; } /* * XXX: I CANT INTO LATIN */ static void -capsicate(struct connection *conn) +capsicate(struct iscsid_connection *conn) { cap_rights_t rights; #ifdef ICL_KERNEL_PROXY const unsigned long cmds[] = { ISCSIDCONNECT, ISCSIDSEND, ISCSIDRECEIVE, ISCSIDHANDOFF, ISCSIDFAIL, ISCSISADD, ISCSISREMOVE, ISCSISMODIFY }; #else const unsigned long cmds[] = { ISCSIDHANDOFF, ISCSIDFAIL, ISCSISADD, ISCSISREMOVE, ISCSISMODIFY }; #endif cap_rights_init(&rights, CAP_IOCTL); if (caph_rights_limit(conn->conn_iscsi_fd, &rights) < 0) log_err(1, "cap_rights_limit"); if (caph_ioctls_limit(conn->conn_iscsi_fd, cmds, nitems(cmds)) < 0) log_err(1, "cap_ioctls_limit"); if (caph_enter() != 0) log_err(1, "cap_enter"); if (cap_sandboxed()) log_debugx("Capsicum capability mode enabled"); else log_warnx("Capsicum capability mode not supported"); } -bool +static bool timed_out(void) { return (sigalrm_received); } static void sigalrm_handler(int dummy __unused) { /* * It would be easiest to just log an error and exit. We can't * do this, though, because log_errx() is not signal safe, since * it calls syslog(3). Instead, set a flag checked by pdu_send() * and pdu_receive(), to call log_errx() there. Should they fail * to notice, we'll exit here one second later. */ if (sigalrm_received) { /* * Oh well. Just give up and quit. */ _exit(2); } sigalrm_received = true; } static void set_timeout(int timeout) { struct sigaction sa; struct itimerval itv; int error; if (timeout <= 0) { log_debugx("session timeout disabled"); return; } bzero(&sa, sizeof(sa)); sa.sa_handler = sigalrm_handler; sigfillset(&sa.sa_mask); error = sigaction(SIGALRM, &sa, NULL); if (error != 0) log_err(1, "sigaction"); /* * First SIGALRM will arive after conf_timeout seconds. * If we do nothing, another one will arrive a second later. */ bzero(&itv, sizeof(itv)); itv.it_interval.tv_sec = 1; itv.it_value.tv_sec = timeout; log_debugx("setting session timeout to %d seconds", timeout); error = setitimer(ITIMER_REAL, &itv, NULL); if (error != 0) log_err(1, "setitimer"); } static void sigchld_handler(int dummy __unused) { /* * The only purpose of this handler is to make SIGCHLD * interrupt the ISCSIDWAIT ioctl(2), so we can call * wait_for_children(). */ } static void register_sigchld(void) { struct sigaction sa; int error; bzero(&sa, sizeof(sa)); sa.sa_handler = sigchld_handler; sigfillset(&sa.sa_mask); error = sigaction(SIGCHLD, &sa, NULL); if (error != 0) log_err(1, "sigaction"); } static void handle_request(int iscsi_fd, const struct iscsi_daemon_request *request, int timeout) { - struct connection *conn; + struct iscsid_connection *conn; log_set_peer_addr(request->idr_conf.isc_target_addr); if (request->idr_conf.isc_target[0] != '\0') { log_set_peer_name(request->idr_conf.isc_target); setproctitle("%s (%s)", request->idr_conf.isc_target_addr, request->idr_conf.isc_target); } else { setproctitle("%s", request->idr_conf.isc_target_addr); } conn = connection_new(iscsi_fd, request); set_timeout(timeout); capsicate(conn); login(conn); if (conn->conn_conf.isc_discovery != 0) discovery(conn); else handoff(conn); log_debugx("nothing more to do; exiting"); exit (0); } static int wait_for_children(bool block) { pid_t pid; int status; int num = 0; for (;;) { /* * If "block" is true, wait for at least one process. */ if (block && num == 0) pid = wait4(-1, &status, 0, NULL); else pid = wait4(-1, &status, WNOHANG, NULL); if (pid <= 0) break; if (WIFSIGNALED(status)) { log_warnx("child process %d terminated with signal %d", pid, WTERMSIG(status)); } else if (WEXITSTATUS(status) != 0) { log_warnx("child process %d terminated with exit status %d", pid, WEXITSTATUS(status)); } else { log_debugx("child process %d terminated gracefully", pid); } num++; } return (num); } int main(int argc, char **argv) { int ch, debug = 0, error, iscsi_fd, maxproc = 30, retval, saved_errno, timeout = 60; bool dont_daemonize = false; struct pidfh *pidfh; pid_t pid, otherpid; const char *pidfile_path = DEFAULT_PIDFILE; struct iscsi_daemon_request request; while ((ch = getopt(argc, argv, "P:dl:m:t:")) != -1) { switch (ch) { case 'P': pidfile_path = optarg; break; case 'd': dont_daemonize = true; debug++; break; case 'l': debug = atoi(optarg); break; case 'm': maxproc = atoi(optarg); break; case 't': timeout = atoi(optarg); break; case '?': default: usage(); } } argc -= optind; if (argc != 0) usage(); log_init(debug); pidfh = pidfile_open(pidfile_path, 0600, &otherpid); if (pidfh == NULL) { if (errno == EEXIST) log_errx(1, "daemon already running, pid: %jd.", (intmax_t)otherpid); log_err(1, "cannot open or create pidfile \"%s\"", pidfile_path); } iscsi_fd = open(ISCSI_PATH, O_RDWR); if (iscsi_fd < 0 && errno == ENOENT) { saved_errno = errno; retval = kldload("iscsi"); if (retval != -1) iscsi_fd = open(ISCSI_PATH, O_RDWR); else errno = saved_errno; } if (iscsi_fd < 0) log_err(1, "failed to open %s", ISCSI_PATH); if (dont_daemonize == false) { if (daemon(0, 0) == -1) { log_warn("cannot daemonize"); pidfile_remove(pidfh); exit(1); } } pidfile_write(pidfh); register_sigchld(); for (;;) { log_debugx("waiting for request from the kernel"); memset(&request, 0, sizeof(request)); error = ioctl(iscsi_fd, ISCSIDWAIT, &request); if (error != 0) { if (errno == EINTR) { nchildren -= wait_for_children(false); assert(nchildren >= 0); continue; } log_err(1, "ISCSIDWAIT"); } if (dont_daemonize) { log_debugx("not forking due to -d flag; " "will exit after servicing a single request"); } else { nchildren -= wait_for_children(false); assert(nchildren >= 0); while (maxproc > 0 && nchildren >= maxproc) { log_debugx("maxproc limit of %d child processes hit; " "waiting for child process to exit", maxproc); nchildren -= wait_for_children(true); assert(nchildren >= 0); } log_debugx("incoming connection; forking child process #%d", nchildren); nchildren++; pid = fork(); if (pid < 0) log_err(1, "fork"); if (pid > 0) continue; } pidfile_close(pidfh); handle_request(iscsi_fd, &request, timeout); } return (0); } diff --git a/usr.sbin/iscsid/iscsid.h b/usr.sbin/iscsid/iscsid.h index b67df4a8197f..42343c0556ae 100644 --- a/usr.sbin/iscsid/iscsid.h +++ b/usr.sbin/iscsid/iscsid.h @@ -1,152 +1,65 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 The FreeBSD Foundation * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef ISCSID_H #define ISCSID_H #include #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 #define SOCKBUF_SIZE 1048576 -struct connection { +struct iscsid_connection { + struct connection conn; int conn_iscsi_fd; - int conn_socket; unsigned int conn_session_id; struct iscsi_session_conf conn_conf; struct iscsi_session_limits conn_limits; char conn_target_alias[ISCSI_ADDR_LEN]; - uint8_t conn_isid[6]; - uint16_t conn_tsih; - uint32_t conn_statsn; int conn_protocol_level; - int conn_header_digest; - int conn_data_digest; bool conn_initial_r2t; - bool conn_immediate_data; - int conn_max_recv_data_segment_length; - int conn_max_send_data_segment_length; - int conn_max_burst_length; - int conn_first_burst_length; struct chap *conn_mutual_chap; }; -struct pdu { - struct connection *pdu_connection; - struct iscsi_bhs *pdu_bhs; - char *pdu_data; - size_t pdu_data_len; -}; - -#define KEYS_MAX 1024 - -struct keys { - char *keys_names[KEYS_MAX]; - char *keys_values[KEYS_MAX]; - char *keys_data; - size_t keys_data_len; -}; - -#define CHAP_CHALLENGE_LEN 1024 -#define CHAP_DIGEST_LEN 16 /* Equal to MD5 digest size. */ - -struct chap { - unsigned char chap_id; - char chap_challenge[CHAP_CHALLENGE_LEN]; - char chap_response[CHAP_DIGEST_LEN]; -}; - -struct rchap { - char *rchap_secret; - unsigned char rchap_id; - void *rchap_challenge; - size_t rchap_challenge_len; -}; - -struct chap *chap_new(void); -char *chap_get_id(const struct chap *chap); -char *chap_get_challenge(const struct chap *chap); -int chap_receive(struct chap *chap, const char *response); -int chap_authenticate(struct chap *chap, - const char *secret); -void chap_delete(struct chap *chap); - -struct rchap *rchap_new(const char *secret); -int rchap_receive(struct rchap *rchap, - const char *id, const char *challenge); -char *rchap_get_response(struct rchap *rchap); -void rchap_delete(struct rchap *rchap); - -struct keys *keys_new(void); -void keys_delete(struct keys *key); -void keys_load(struct keys *keys, const struct pdu *pdu); -void keys_save(struct keys *keys, struct pdu *pdu); -const char *keys_find(struct keys *keys, const char *name); -void keys_add(struct keys *keys, - const char *name, const char *value); -void keys_add_int(struct keys *keys, - const char *name, int value); - -struct pdu *pdu_new(struct connection *ic); -struct pdu *pdu_new_response(struct pdu *request); -void pdu_receive(struct pdu *request); -void pdu_send(struct pdu *response); -void pdu_delete(struct pdu *ip); - -void login(struct connection *ic); - -void discovery(struct connection *ic); +void login(struct iscsid_connection *ic); -void log_init(int level); -void log_set_peer_name(const char *name); -void log_set_peer_addr(const char *addr); -void log_err(int, const char *, ...) - __dead2 __printflike(2, 3); -void log_errx(int, const char *, ...) - __dead2 __printflike(2, 3); -void log_warn(const char *, ...) __printflike(1, 2); -void log_warnx(const char *, ...) __printflike(1, 2); -void log_debugx(const char *, ...) __printflike(1, 2); +void discovery(struct iscsid_connection *ic); -char *checked_strdup(const char *); -bool timed_out(void); void fail(const struct connection *, const char *); #endif /* !ISCSID_H */ diff --git a/usr.sbin/iscsid/keys.c b/usr.sbin/iscsid/keys.c deleted file mode 100644 index e1d49ff585de..000000000000 --- a/usr.sbin/iscsid/keys.c +++ /dev/null @@ -1,199 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2012 The FreeBSD Foundation - * - * This software was developed by Edward Tomasz Napierala under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include - -#include "iscsid.h" - -struct keys * -keys_new(void) -{ - struct keys *keys; - - keys = calloc(1, sizeof(*keys)); - if (keys == NULL) - log_err(1, "calloc"); - - return (keys); -} - -void -keys_delete(struct keys *keys) -{ - - free(keys->keys_data); - free(keys); -} - -void -keys_load(struct keys *keys, const struct pdu *pdu) -{ - int i; - char *pair; - size_t pair_len; - - if (pdu->pdu_data_len == 0) - return; - - if (pdu->pdu_data[pdu->pdu_data_len - 1] != '\0') - log_errx(1, "protocol error: key not NULL-terminated\n"); - - assert(keys->keys_data == NULL); - keys->keys_data_len = pdu->pdu_data_len; - keys->keys_data = malloc(keys->keys_data_len); - if (keys->keys_data == NULL) - log_err(1, "malloc"); - memcpy(keys->keys_data, pdu->pdu_data, keys->keys_data_len); - - /* - * XXX: Review this carefully. - */ - pair = keys->keys_data; - for (i = 0;; i++) { - if (i >= KEYS_MAX) - log_errx(1, "too many keys received"); - - pair_len = strlen(pair); - - keys->keys_values[i] = pair; - keys->keys_names[i] = strsep(&keys->keys_values[i], "="); - if (keys->keys_names[i] == NULL || keys->keys_values[i] == NULL) - log_errx(1, "malformed keys"); - log_debugx("key received: \"%s=%s\"", - keys->keys_names[i], keys->keys_values[i]); - - pair += pair_len + 1; /* +1 to skip the terminating '\0'. */ - if (pair == keys->keys_data + keys->keys_data_len) - break; - assert(pair < keys->keys_data + keys->keys_data_len); - } -} - -void -keys_save(struct keys *keys, struct pdu *pdu) -{ - char *data; - size_t len; - int i; - - /* - * XXX: Not particularly efficient. - */ - len = 0; - for (i = 0; i < KEYS_MAX; i++) { - if (keys->keys_names[i] == NULL) - break; - /* - * +1 for '=', +1 for '\0'. - */ - len += strlen(keys->keys_names[i]) + - strlen(keys->keys_values[i]) + 2; - } - - if (len == 0) - return; - - data = malloc(len); - if (data == NULL) - log_err(1, "malloc"); - - pdu->pdu_data = data; - pdu->pdu_data_len = len; - - for (i = 0; i < KEYS_MAX; i++) { - if (keys->keys_names[i] == NULL) - break; - data += sprintf(data, "%s=%s", - keys->keys_names[i], keys->keys_values[i]); - data += 1; /* for '\0'. */ - } -} - -const char * -keys_find(struct keys *keys, const char *name) -{ - int i; - - /* - * Note that we don't handle duplicated key names here, - * as they are not supposed to happen in requests, and if they do, - * it's an initiator error. - */ - for (i = 0; i < KEYS_MAX; i++) { - if (keys->keys_names[i] == NULL) - return (NULL); - if (strcmp(keys->keys_names[i], name) == 0) - return (keys->keys_values[i]); - } - return (NULL); -} - -void -keys_add(struct keys *keys, const char *name, const char *value) -{ - int i; - - log_debugx("key to send: \"%s=%s\"", name, value); - - /* - * Note that we don't check for duplicates here, as they are perfectly - * fine in responses, e.g. the "TargetName" keys in discovery sesion - * response. - */ - for (i = 0; i < KEYS_MAX; i++) { - if (keys->keys_names[i] == NULL) { - keys->keys_names[i] = checked_strdup(name); - keys->keys_values[i] = checked_strdup(value); - return; - } - } - log_errx(1, "too many keys"); -} - -void -keys_add_int(struct keys *keys, const char *name, int value) -{ - char *str; - int ret; - - ret = asprintf(&str, "%d", value); - if (ret <= 0) - log_err(1, "asprintf"); - - keys_add(keys, name, str); - free(str); -} diff --git a/usr.sbin/iscsid/log.c b/usr.sbin/iscsid/log.c deleted file mode 100644 index fe0009be774d..000000000000 --- a/usr.sbin/iscsid/log.c +++ /dev/null @@ -1,202 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2012 The FreeBSD Foundation - * - * This software was developed by Edward Tomasz Napierala under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include - -#include "iscsid.h" - -static int log_level = 0; -static char *peer_name = NULL; -static char *peer_addr = NULL; - -#define MSGBUF_LEN 1024 - -void -log_init(int level) -{ - - log_level = level; - openlog(getprogname(), LOG_NDELAY | LOG_PID, LOG_DAEMON); -} - -void -log_set_peer_name(const char *name) -{ - - /* - * XXX: Turn it into assertion? - */ - if (peer_name != NULL) - log_errx(1, "%s called twice", __func__); - if (peer_addr == NULL) - log_errx(1, "%s called before log_set_peer_addr", __func__); - - peer_name = checked_strdup(name); -} - -void -log_set_peer_addr(const char *addr) -{ - - /* - * XXX: Turn it into assertion? - */ - if (peer_addr != NULL) - log_errx(1, "%s called twice", __func__); - - peer_addr = checked_strdup(addr); -} - -static void -log_common(int priority, int log_errno, const char *fmt, va_list ap) -{ - static char msgbuf[MSGBUF_LEN]; - static char msgbuf_strvised[MSGBUF_LEN * 4 + 1]; - char *errstr; - int ret; - - ret = vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); - if (ret < 0) { - fprintf(stderr, "%s: snprintf failed", getprogname()); - syslog(LOG_CRIT, "snprintf failed"); - exit(1); - } - - ret = strnvis(msgbuf_strvised, sizeof(msgbuf_strvised), msgbuf, VIS_NL); - if (ret < 0) { - fprintf(stderr, "%s: strnvis failed", getprogname()); - syslog(LOG_CRIT, "strnvis failed"); - exit(1); - } - - if (log_errno == -1) { - if (peer_name != NULL) { - fprintf(stderr, "%s: %s (%s): %s\n", getprogname(), - peer_addr, peer_name, msgbuf_strvised); - syslog(priority, "%s (%s): %s", - peer_addr, peer_name, msgbuf_strvised); - } else if (peer_addr != NULL) { - fprintf(stderr, "%s: %s: %s\n", getprogname(), - peer_addr, msgbuf_strvised); - syslog(priority, "%s: %s", - peer_addr, msgbuf_strvised); - } else { - fprintf(stderr, "%s: %s\n", getprogname(), msgbuf_strvised); - syslog(priority, "%s", msgbuf_strvised); - } - - } else { - errstr = strerror(log_errno); - - if (peer_name != NULL) { - fprintf(stderr, "%s: %s (%s): %s: %s\n", getprogname(), - peer_addr, peer_name, msgbuf_strvised, errstr); - syslog(priority, "%s (%s): %s: %s", - peer_addr, peer_name, msgbuf_strvised, errstr); - } else if (peer_addr != NULL) { - fprintf(stderr, "%s: %s: %s: %s\n", getprogname(), - peer_addr, msgbuf_strvised, errstr); - syslog(priority, "%s: %s: %s", - peer_addr, msgbuf_strvised, errstr); - } else { - fprintf(stderr, "%s: %s: %s\n", getprogname(), - msgbuf_strvised, errstr); - syslog(priority, "%s: %s", - msgbuf_strvised, errstr); - } - } -} - -void -log_err(int eval, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - log_common(LOG_CRIT, errno, fmt, ap); - va_end(ap); - - exit(eval); -} - -void -log_errx(int eval, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - log_common(LOG_CRIT, -1, fmt, ap); - va_end(ap); - - exit(eval); -} - -void -log_warn(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - log_common(LOG_WARNING, errno, fmt, ap); - va_end(ap); -} - -void -log_warnx(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - log_common(LOG_WARNING, -1, fmt, ap); - va_end(ap); -} - -void -log_debugx(const char *fmt, ...) -{ - va_list ap; - - if (log_level == 0) - return; - - va_start(ap, fmt); - log_common(LOG_DEBUG, -1, fmt, ap); - va_end(ap); -} diff --git a/usr.sbin/iscsid/login.c b/usr.sbin/iscsid/login.c index 3f0020ef140b..11a22dfcfaf1 100644 --- a/usr.sbin/iscsid/login.c +++ b/usr.sbin/iscsid/login.c @@ -1,897 +1,901 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 The FreeBSD Foundation * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "iscsid.h" #include "iscsi_proto.h" static int login_nsg(const struct pdu *response) { struct iscsi_bhs_login_response *bhslr; bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs; return (bhslr->bhslr_flags & 0x03); } static void login_set_nsg(struct pdu *request, int nsg) { struct iscsi_bhs_login_request *bhslr; assert(nsg == BHSLR_STAGE_SECURITY_NEGOTIATION || nsg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION || nsg == BHSLR_STAGE_FULL_FEATURE_PHASE); bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; bhslr->bhslr_flags &= 0xFC; bhslr->bhslr_flags |= nsg; } static void login_set_csg(struct pdu *request, int csg) { struct iscsi_bhs_login_request *bhslr; assert(csg == BHSLR_STAGE_SECURITY_NEGOTIATION || csg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION || csg == BHSLR_STAGE_FULL_FEATURE_PHASE); bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; bhslr->bhslr_flags &= 0xF3; bhslr->bhslr_flags |= csg << 2; } static const char * login_target_error_str(int class, int detail) { static char msg[128]; /* * RFC 3270, 10.13.5. Status-Class and Status-Detail */ switch (class) { case 0x01: switch (detail) { case 0x01: return ("Target moved temporarily"); case 0x02: return ("Target moved permanently"); default: snprintf(msg, sizeof(msg), "unknown redirection; " "Status-Class 0x%x, Status-Detail 0x%x", class, detail); return (msg); } case 0x02: switch (detail) { case 0x00: return ("Initiator error"); case 0x01: return ("Authentication failure"); case 0x02: return ("Authorization failure"); case 0x03: return ("Not found"); case 0x04: return ("Target removed"); case 0x05: return ("Unsupported version"); case 0x06: return ("Too many connections"); case 0x07: return ("Missing parameter"); case 0x08: return ("Can't include in session"); case 0x09: return ("Session type not supported"); case 0x0a: return ("Session does not exist"); case 0x0b: return ("Invalid during login"); default: snprintf(msg, sizeof(msg), "unknown initiator error; " "Status-Class 0x%x, Status-Detail 0x%x", class, detail); return (msg); } case 0x03: switch (detail) { case 0x00: return ("Target error"); case 0x01: return ("Service unavailable"); case 0x02: return ("Out of resources"); default: snprintf(msg, sizeof(msg), "unknown target error; " "Status-Class 0x%x, Status-Detail 0x%x", class, detail); return (msg); } default: snprintf(msg, sizeof(msg), "unknown error; " "Status-Class 0x%x, Status-Detail 0x%x", class, detail); return (msg); } } static void -kernel_modify(const struct connection *conn, const char *target_address) +kernel_modify(const struct iscsid_connection *conn, const char *target_address) { struct iscsi_session_modify ism; int error; memset(&ism, 0, sizeof(ism)); ism.ism_session_id = conn->conn_session_id; memcpy(&ism.ism_conf, &conn->conn_conf, sizeof(ism.ism_conf)); strlcpy(ism.ism_conf.isc_target_addr, target_address, sizeof(ism.ism_conf.isc_target_addr)); error = ioctl(conn->conn_iscsi_fd, ISCSISMODIFY, &ism); if (error != 0) { log_err(1, "failed to redirect to %s: ISCSISMODIFY", target_address); } } /* * XXX: The way it works is suboptimal; what should happen is described * in draft-gilligan-iscsi-fault-tolerance-00. That, however, would * be much more complicated: we would need to keep "dependencies" * for sessions, so that, in case described in draft and using draft * terminology, we would have three sessions: one for discovery, * one for initial target portal, and one for redirect portal. * This would allow us to "backtrack" on connection failure, * as described in draft. */ static void -login_handle_redirection(struct connection *conn, struct pdu *response) +login_handle_redirection(struct iscsid_connection *conn, struct pdu *response) { struct iscsi_bhs_login_response *bhslr; struct keys *response_keys; const char *target_address; bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs; assert (bhslr->bhslr_status_class == 1); response_keys = keys_new(); keys_load(response_keys, response); target_address = keys_find(response_keys, "TargetAddress"); if (target_address == NULL) log_errx(1, "received redirection without TargetAddress"); if (target_address[0] == '\0') log_errx(1, "received redirection with empty TargetAddress"); if (strlen(target_address) >= sizeof(conn->conn_conf.isc_target_addr) - 1) log_errx(1, "received TargetAddress is too long"); log_debugx("received redirection to \"%s\"", target_address); kernel_modify(conn, target_address); keys_delete(response_keys); } static struct pdu * login_receive(struct connection *conn) { struct pdu *response; struct iscsi_bhs_login_response *bhslr; const char *errorstr; static bool initial = true; 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 == 1) { - login_handle_redirection(conn, response); + login_handle_redirection((struct iscsid_connection *)conn, + response); log_debugx("redirection handled; exiting"); exit(0); } if (bhslr->bhslr_status_class != 0) { errorstr = login_target_error_str(bhslr->bhslr_status_class, bhslr->bhslr_status_detail); fail(conn, errorstr); log_errx(1, "target returned error: %s", errorstr); } if (initial == false && ntohl(bhslr->bhslr_statsn) != conn->conn_statsn + 1) { /* * It's a warning, not an error, to work around what seems * to be bug in NetBSD iSCSI target. */ log_warnx("received Login PDU with wrong StatSN: " "is %u, should be %u", ntohl(bhslr->bhslr_statsn), conn->conn_statsn + 1); } conn->conn_tsih = ntohs(bhslr->bhslr_tsih); conn->conn_statsn = ntohl(bhslr->bhslr_statsn); initial = false; return (response); } static struct pdu * login_new_request(struct connection *conn, int csg) { struct pdu *request; struct iscsi_bhs_login_request *bhslr; int nsg; 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; switch (csg) { case BHSLR_STAGE_SECURITY_NEGOTIATION: nsg = BHSLR_STAGE_OPERATIONAL_NEGOTIATION; break; case BHSLR_STAGE_OPERATIONAL_NEGOTIATION: nsg = BHSLR_STAGE_FULL_FEATURE_PHASE; break; default: assert(!"invalid csg"); log_errx(1, "invalid csg %d", csg); } login_set_csg(request, csg); login_set_nsg(request, nsg); memcpy(bhslr->bhslr_isid, &conn->conn_isid, sizeof(bhslr->bhslr_isid)); bhslr->bhslr_tsih = htons(conn->conn_tsih); 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 void -login_negotiate_key(struct connection *conn, const char *name, +login_negotiate_key(struct iscsid_connection *conn, const char *name, const char *value) { struct iscsi_session_limits *isl; int which, tmp; isl = &conn->conn_limits; if (strcmp(name, "TargetAlias") == 0) { strlcpy(conn->conn_target_alias, value, sizeof(conn->conn_target_alias)); } else if (strcmp(value, "Irrelevant") == 0) { /* Ignore. */ } else if (strcmp(name, "iSCSIProtocolLevel") == 0) { tmp = strtoul(value, NULL, 10); if (tmp < 0 || tmp > 31) log_errx(1, "received invalid iSCSIProtocolLevel"); conn->conn_protocol_level = tmp; } 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; + conn->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; + conn->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; + conn->conn.conn_immediate_data = true; else - conn->conn_immediate_data = false; + conn->conn.conn_immediate_data = false; } else if (strcmp(name, "MaxRecvDataSegmentLength") == 0) { tmp = strtoul(value, NULL, 10); if (tmp <= 0) log_errx(1, "received invalid " "MaxRecvDataSegmentLength"); if (tmp > isl->isl_max_send_data_segment_length) { log_debugx("capping max_send_data_segment_length " "from %d to %d", tmp, isl->isl_max_send_data_segment_length); tmp = isl->isl_max_send_data_segment_length; } - conn->conn_max_send_data_segment_length = tmp; + conn->conn.conn_max_send_data_segment_length = tmp; } else if (strcmp(name, "MaxBurstLength") == 0) { tmp = strtoul(value, NULL, 10); if (tmp <= 0) log_errx(1, "received invalid MaxBurstLength"); if (tmp > isl->isl_max_burst_length) { log_debugx("capping MaxBurstLength " "from %d to %d", tmp, isl->isl_max_burst_length); tmp = isl->isl_max_burst_length; } - conn->conn_max_burst_length = tmp; + conn->conn.conn_max_burst_length = tmp; } else if (strcmp(name, "FirstBurstLength") == 0) { tmp = strtoul(value, NULL, 10); if (tmp <= 0) log_errx(1, "received invalid FirstBurstLength"); if (tmp > isl->isl_first_burst_length) { log_debugx("capping FirstBurstLength " "from %d to %d", tmp, isl->isl_first_burst_length); tmp = isl->isl_first_burst_length; } - conn->conn_first_burst_length = tmp; + conn->conn.conn_first_burst_length = tmp; } else if (strcmp(name, "DefaultTime2Wait") == 0) { /* Ignore */ } else if (strcmp(name, "DefaultTime2Retain") == 0) { /* 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, "RDMAExtensions") == 0) { if (conn->conn_conf.isc_iser == 1 && strcmp(value, "Yes") != 0) { log_errx(1, "received unsupported RDMAExtensions"); } } else if (strcmp(name, "InitiatorRecvDataSegmentLength") == 0) { tmp = strtoul(value, NULL, 10); if (tmp <= 0) log_errx(1, "received invalid " "InitiatorRecvDataSegmentLength"); if ((int)tmp > isl->isl_max_recv_data_segment_length) { log_debugx("capping InitiatorRecvDataSegmentLength " "from %d to %d", tmp, isl->isl_max_recv_data_segment_length); tmp = isl->isl_max_recv_data_segment_length; } - conn->conn_max_recv_data_segment_length = tmp; + conn->conn.conn_max_recv_data_segment_length = tmp; } else if (strcmp(name, "TargetPortalGroupTag") == 0) { /* Ignore */ } else if (strcmp(name, "TargetRecvDataSegmentLength") == 0) { tmp = strtoul(value, NULL, 10); if (tmp <= 0) { log_errx(1, "received invalid TargetRecvDataSegmentLength"); } if (tmp > isl->isl_max_send_data_segment_length) { log_debugx("capping TargetRecvDataSegmentLength " "from %d to %d", tmp, isl->isl_max_send_data_segment_length); tmp = isl->isl_max_send_data_segment_length; } - conn->conn_max_send_data_segment_length = tmp; + conn->conn.conn_max_send_data_segment_length = tmp; } else { log_debugx("unknown key \"%s\"; ignoring", name); } } static void -login_negotiate(struct connection *conn) +login_negotiate(struct iscsid_connection *conn) { struct pdu *request, *response; struct keys *request_keys, *response_keys; struct iscsi_bhs_login_response *bhslr; int i, nrequests = 0; struct iscsi_session_limits *isl; log_debugx("beginning operational parameter negotiation"); - request = login_new_request(conn, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); + request = login_new_request(&conn->conn, + BHSLR_STAGE_OPERATIONAL_NEGOTIATION); request_keys = keys_new(); isl = &conn->conn_limits; log_debugx("Limits for offload \"%s\" are " "MaxRecvDataSegment=%d, max_send_dsl=%d, " "MaxBurstLength=%d, FirstBurstLength=%d", conn->conn_conf.isc_offload, isl->isl_max_recv_data_segment_length, isl->isl_max_send_data_segment_length, isl->isl_max_burst_length, isl->isl_first_burst_length); /* * The following keys are irrelevant for discovery sessions. */ if (conn->conn_conf.isc_discovery == 0) { keys_add(request_keys, "iSCSIProtocolLevel", "2"); if (conn->conn_conf.isc_header_digest != 0) keys_add(request_keys, "HeaderDigest", "CRC32C"); else keys_add(request_keys, "HeaderDigest", "None"); if (conn->conn_conf.isc_data_digest != 0) keys_add(request_keys, "DataDigest", "CRC32C"); else keys_add(request_keys, "DataDigest", "None"); keys_add(request_keys, "ImmediateData", "Yes"); keys_add_int(request_keys, "MaxBurstLength", isl->isl_max_burst_length); keys_add_int(request_keys, "FirstBurstLength", isl->isl_first_burst_length); keys_add(request_keys, "InitialR2T", "Yes"); keys_add(request_keys, "MaxOutstandingR2T", "1"); if (conn->conn_conf.isc_iser == 1) { keys_add_int(request_keys, "InitiatorRecvDataSegmentLength", isl->isl_max_recv_data_segment_length); keys_add_int(request_keys, "TargetRecvDataSegmentLength", isl->isl_max_send_data_segment_length); keys_add(request_keys, "RDMAExtensions", "Yes"); } else { keys_add_int(request_keys, "MaxRecvDataSegmentLength", isl->isl_max_recv_data_segment_length); } } else { keys_add(request_keys, "HeaderDigest", "None"); keys_add(request_keys, "DataDigest", "None"); keys_add_int(request_keys, "MaxRecvDataSegmentLength", isl->isl_max_recv_data_segment_length); } - conn->conn_max_recv_data_segment_length = + conn->conn.conn_max_recv_data_segment_length = isl->isl_max_recv_data_segment_length; keys_add(request_keys, "DefaultTime2Wait", "0"); keys_add(request_keys, "DefaultTime2Retain", "0"); keys_add(request_keys, "ErrorRecoveryLevel", "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); + response = login_receive(&conn->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; login_negotiate_key(conn, response_keys->keys_names[i], response_keys->keys_values[i]); } keys_delete(response_keys); response_keys = NULL; for (;;) { bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs; if ((bhslr->bhslr_flags & BHSLR_FLAGS_TRANSIT) != 0) break; nrequests++; if (nrequests > 5) { log_warnx("received login response " "without the \"T\" flag too many times; giving up"); break; } log_debugx("received login response " "without the \"T\" flag; sending another request"); pdu_delete(response); - request = login_new_request(conn, + request = login_new_request(&conn->conn, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); pdu_send(request); pdu_delete(request); - response = login_receive(conn); + response = login_receive(&conn->conn); } if (login_nsg(response) != BHSLR_STAGE_FULL_FEATURE_PHASE) log_warnx("received final login response with wrong NSG 0x%x", login_nsg(response)); pdu_delete(response); log_debugx("operational parameter negotiation done; " "transitioning to Full Feature phase"); } static void login_send_chap_a(struct connection *conn) { struct pdu *request; struct keys *request_keys; request = login_new_request(conn, BHSLR_STAGE_SECURITY_NEGOTIATION); 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 iscsid_connection *conn; struct pdu *request; struct keys *request_keys, *response_keys; struct rchap *rchap; const char *chap_a, *chap_c, *chap_i; char *chap_r; int error; char *mutual_chap_c, *mutual_chap_i; /* * 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; + conn = (struct iscsid_connection *)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); } rchap = rchap_new(conn->conn_conf.isc_secret); error = rchap_receive(rchap, chap_i, chap_c); if (error != 0) { log_errx(1, "received CHAP packet " "with malformed CHAP_I or CHAP_C"); } chap_r = rchap_get_response(rchap); rchap_delete(rchap); keys_delete(response_keys); - request = login_new_request(conn, BHSLR_STAGE_SECURITY_NEGOTIATION); + request = login_new_request(&conn->conn, + BHSLR_STAGE_SECURITY_NEGOTIATION); request_keys = keys_new(); keys_add(request_keys, "CHAP_N", conn->conn_conf.isc_user); keys_add(request_keys, "CHAP_R", chap_r); free(chap_r); /* * If we want mutual authentication, we're expected to send * our CHAP_I/CHAP_C now. */ if (conn->conn_conf.isc_mutual_user[0] != '\0') { log_debugx("requesting mutual authentication; " "binary challenge size is %zd bytes", sizeof(conn->conn_mutual_chap->chap_challenge)); assert(conn->conn_mutual_chap == NULL); conn->conn_mutual_chap = chap_new(); mutual_chap_i = chap_get_id(conn->conn_mutual_chap); mutual_chap_c = chap_get_challenge(conn->conn_mutual_chap); keys_add(request_keys, "CHAP_I", mutual_chap_i); keys_add(request_keys, "CHAP_C", mutual_chap_c); free(mutual_chap_i); 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 iscsid_connection *conn; struct keys *response_keys; const char *chap_n, *chap_r; int error; - conn = response->pdu_connection; + conn = (struct iscsid_connection *)response->pdu_connection; response_keys = keys_new(); keys_load(response_keys, response); 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 = chap_receive(conn->conn_mutual_chap, chap_r); if (error != 0) log_errx(1, "received CHAP Response PDU with invalid CHAP_R"); if (strcmp(chap_n, conn->conn_conf.isc_mutual_user) != 0) { - fail(conn, "Mutual CHAP failed"); + fail(&conn->conn, "Mutual CHAP failed"); log_errx(1, "mutual CHAP authentication failed: wrong user"); } error = chap_authenticate(conn->conn_mutual_chap, conn->conn_conf.isc_mutual_secret); if (error != 0) { - fail(conn, "Mutual CHAP failed"); + fail(&conn->conn, "Mutual CHAP failed"); log_errx(1, "mutual CHAP authentication failed: wrong secret"); } keys_delete(response_keys); chap_delete(conn->conn_mutual_chap); conn->conn_mutual_chap = NULL; log_debugx("mutual CHAP authentication succeeded"); } static void -login_chap(struct connection *conn) +login_chap(struct iscsid_connection *conn) { struct pdu *response; log_debugx("beginning CHAP authentication; sending CHAP_A"); - login_send_chap_a(conn); + login_send_chap_a(&conn->conn); log_debugx("waiting for CHAP_A/CHAP_C/CHAP_I"); - response = login_receive(conn); + response = login_receive(&conn->conn); log_debugx("sending CHAP_N/CHAP_R"); login_send_chap_r(response); pdu_delete(response); /* * XXX: Make sure this is not susceptible to MITM. */ log_debugx("waiting for CHAP result"); - response = login_receive(conn); + response = login_receive(&conn->conn); if (conn->conn_conf.isc_mutual_user[0] != '\0') login_verify_mutual(response); pdu_delete(response); log_debugx("CHAP authentication done"); } void -login(struct connection *conn) +login(struct iscsid_connection *conn) { struct pdu *request, *response; struct keys *request_keys, *response_keys; struct iscsi_bhs_login_response *bhslr2; const char *auth_method; int i; log_debugx("beginning Login phase; sending Login PDU"); - request = login_new_request(conn, BHSLR_STAGE_SECURITY_NEGOTIATION); + request = login_new_request(&conn->conn, + BHSLR_STAGE_SECURITY_NEGOTIATION); request_keys = keys_new(); if (conn->conn_conf.isc_mutual_user[0] != '\0') { keys_add(request_keys, "AuthMethod", "CHAP"); } else if (conn->conn_conf.isc_user[0] != '\0') { /* * Give target a chance to skip authentication if it * doesn't feel like it. * * None is first, CHAP second; this is to work around * what seems to be LIO (Linux target) bug: otherwise, * if target is configured with no authentication, * and we are configured to authenticate, the target * will erroneously respond with AuthMethod=CHAP * instead of AuthMethod=None, and will subsequently * fail the connection. This usually happens with * Discovery sessions, which default to no authentication. */ keys_add(request_keys, "AuthMethod", "None,CHAP"); } else { keys_add(request_keys, "AuthMethod", "None"); } keys_add(request_keys, "InitiatorName", conn->conn_conf.isc_initiator); if (conn->conn_conf.isc_initiator_alias[0] != '\0') { keys_add(request_keys, "InitiatorAlias", conn->conn_conf.isc_initiator_alias); } if (conn->conn_conf.isc_discovery == 0) { keys_add(request_keys, "SessionType", "Normal"); keys_add(request_keys, "TargetName", conn->conn_conf.isc_target); } else { keys_add(request_keys, "SessionType", "Discovery"); } keys_save(request_keys, request); keys_delete(request_keys); pdu_send(request); pdu_delete(request); - response = login_receive(conn); + response = login_receive(&conn->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; /* * 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) { if (conn->conn_conf.isc_mutual_user[0] != '\0') { log_errx(1, "target requested transition " "to operational parameter negotiation, " "but we require mutual CHAP"); } log_debugx("target requested transition " "to operational parameter 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) { if (conn->conn_conf.isc_mutual_user[0] != '\0') { log_errx(1, "target does not require authantication, " "but we require mutual CHAP"); } log_debugx("target does not require authentication"); keys_delete(response_keys); pdu_delete(response); login_negotiate(conn); return; } if (strcmp(auth_method, "CHAP") != 0) { - fail(conn, "Unsupported AuthMethod"); + fail(&conn->conn, "Unsupported AuthMethod"); log_errx(1, "received response " "with unsupported AuthMethod \"%s\"", auth_method); } if (conn->conn_conf.isc_user[0] == '\0' || conn->conn_conf.isc_secret[0] == '\0') { - fail(conn, "Authentication required"); + fail(&conn->conn, "Authentication required"); log_errx(1, "target requests CHAP authentication, but we don't " "have user and secret"); } keys_delete(response_keys); response_keys = NULL; pdu_delete(response); response = NULL; login_chap(conn); login_negotiate(conn); } diff --git a/usr.sbin/iscsid/pdu.c b/usr.sbin/iscsid/pdu.c deleted file mode 100644 index 246195bf1dcc..000000000000 --- a/usr.sbin/iscsid/pdu.c +++ /dev/null @@ -1,309 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2012 The FreeBSD Foundation - * - * This software was developed by Edward Tomasz Napierala under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include - -#include "iscsid.h" -#include "iscsi_proto.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(1, sizeof(*pdu)); - if (pdu == NULL) - log_err(1, "calloc"); - - pdu->pdu_bhs = calloc(1, sizeof(*pdu->pdu_bhs)); - if (pdu->pdu_bhs == NULL) - log_err(1, "calloc"); - - pdu->pdu_connection = conn; - - return (pdu); -} - -struct pdu * -pdu_new_response(struct pdu *request) -{ - - return (pdu_new(request->pdu_connection)); -} - -#ifdef ICL_KERNEL_PROXY - -static void -pdu_receive_proxy(struct pdu *pdu) -{ - struct connection *conn; - struct iscsi_daemon_receive *idr; - size_t len; - int error; - - conn = pdu->pdu_connection; - assert(conn->conn_conf.isc_iser != 0); - - pdu->pdu_data = malloc(conn->conn_max_recv_data_segment_length); - if (pdu->pdu_data == NULL) - log_err(1, "malloc"); - - idr = calloc(1, sizeof(*idr)); - if (idr == NULL) - log_err(1, "calloc"); - - idr->idr_session_id = conn->conn_session_id; - idr->idr_bhs = pdu->pdu_bhs; - idr->idr_data_segment_len = conn->conn_max_recv_data_segment_length; - idr->idr_data_segment = pdu->pdu_data; - - error = ioctl(conn->conn_iscsi_fd, ISCSIDRECEIVE, idr); - if (error != 0) - log_err(1, "ISCSIDRECEIVE"); - - len = pdu_ahs_length(pdu); - if (len > 0) - log_errx(1, "protocol error: non-empty AHS"); - - len = pdu_data_segment_length(pdu); - assert(len <= (size_t)conn->conn_max_recv_data_segment_length); - pdu->pdu_data_len = len; - - free(idr); -} - -static void -pdu_send_proxy(struct pdu *pdu) -{ - struct connection *conn; - struct iscsi_daemon_send *ids; - int error; - - conn = pdu->pdu_connection; - assert(conn->conn_conf.isc_iser != 0); - - pdu_set_data_segment_length(pdu, pdu->pdu_data_len); - - ids = calloc(1, sizeof(*ids)); - if (ids == NULL) - log_err(1, "calloc"); - - ids->ids_session_id = conn->conn_session_id; - ids->ids_bhs = pdu->pdu_bhs; - ids->ids_data_segment_len = pdu->pdu_data_len; - ids->ids_data_segment = pdu->pdu_data; - - error = ioctl(conn->conn_iscsi_fd, ISCSIDSEND, ids); - if (error != 0) - log_err(1, "ISCSIDSEND"); - - free(ids); -} - -#endif /* ICL_KERNEL_PROXY */ - -static size_t -pdu_padding(const struct pdu *pdu) -{ - - if ((pdu->pdu_data_len % 4) != 0) - return (4 - (pdu->pdu_data_len % 4)); - - return (0); -} - -static void -pdu_read(const struct connection *conn, char *data, size_t len) -{ - ssize_t ret; - - while (len > 0) { - ret = read(conn->conn_socket, data, len); - if (ret < 0) { - if (timed_out()) { - fail(conn, "Login Phase timeout"); - log_errx(1, "exiting due to timeout"); - } - fail(conn, strerror(errno)); - log_err(1, "read"); - } else if (ret == 0) { - fail(conn, "connection lost"); - log_errx(1, "read: connection lost"); - } - len -= ret; - data += ret; - } -} - -void -pdu_receive(struct pdu *pdu) -{ - struct connection *conn; - size_t len, padding; - char dummy[4]; - - conn = pdu->pdu_connection; -#ifdef ICL_KERNEL_PROXY - if (conn->conn_conf.isc_iser != 0) - return (pdu_receive_proxy(pdu)); -#endif - assert(conn->conn_conf.isc_iser == 0); - - pdu_read(conn, (char *)pdu->pdu_bhs, sizeof(*pdu->pdu_bhs)); - - len = pdu_ahs_length(pdu); - if (len > 0) - log_errx(1, "protocol error: non-empty AHS"); - - len = pdu_data_segment_length(pdu); - if (len > 0) { - if (len > (size_t)conn->conn_max_recv_data_segment_length) { - log_errx(1, "protocol error: received PDU " - "with DataSegmentLength exceeding %d", - conn->conn_max_recv_data_segment_length); - } - - pdu->pdu_data_len = len; - pdu->pdu_data = malloc(len); - if (pdu->pdu_data == NULL) - log_err(1, "malloc"); - - pdu_read(conn, (char *)pdu->pdu_data, pdu->pdu_data_len); - - padding = pdu_padding(pdu); - if (padding != 0) { - assert(padding < sizeof(dummy)); - pdu_read(conn, (char *)dummy, padding); - } - } -} - -void -pdu_send(struct pdu *pdu) -{ - struct connection *conn; - ssize_t ret, total_len; - size_t padding; - uint32_t zero = 0; - struct iovec iov[3]; - int iovcnt; - - conn = pdu->pdu_connection; -#ifdef ICL_KERNEL_PROXY - if (conn->conn_conf.isc_iser != 0) - return (pdu_send_proxy(pdu)); -#endif - - assert(conn->conn_conf.isc_iser == 0); - - pdu_set_data_segment_length(pdu, pdu->pdu_data_len); - iov[0].iov_base = pdu->pdu_bhs; - iov[0].iov_len = sizeof(*pdu->pdu_bhs); - total_len = iov[0].iov_len; - iovcnt = 1; - - if (pdu->pdu_data_len > 0) { - iov[1].iov_base = pdu->pdu_data; - iov[1].iov_len = pdu->pdu_data_len; - total_len += iov[1].iov_len; - iovcnt = 2; - - padding = pdu_padding(pdu); - if (padding > 0) { - assert(padding < sizeof(zero)); - iov[2].iov_base = &zero; - iov[2].iov_len = padding; - total_len += iov[2].iov_len; - iovcnt = 3; - } - } - - ret = writev(conn->conn_socket, iov, iovcnt); - if (ret < 0) { - if (timed_out()) - log_errx(1, "exiting due to timeout"); - log_err(1, "writev"); - } - if (ret != total_len) - log_errx(1, "short write"); -} - -void -pdu_delete(struct pdu *pdu) -{ - - free(pdu->pdu_data); - free(pdu->pdu_bhs); - free(pdu); -}