[Midnightbsd-cvs] src [10818] trunk/usr.sbin/ctld: add ctld
laffer1 at midnightbsd.org
laffer1 at midnightbsd.org
Sun Jun 10 16:15:35 EDT 2018
Revision: 10818
http://svnweb.midnightbsd.org/src/?rev=10818
Author: laffer1
Date: 2018-06-10 16:15:35 -0400 (Sun, 10 Jun 2018)
Log Message:
-----------
add ctld
Added Paths:
-----------
trunk/usr.sbin/ctld/
trunk/usr.sbin/ctld/Makefile
trunk/usr.sbin/ctld/chap.c
trunk/usr.sbin/ctld/ctl.conf.5
trunk/usr.sbin/ctld/ctld.8
trunk/usr.sbin/ctld/ctld.c
trunk/usr.sbin/ctld/ctld.h
trunk/usr.sbin/ctld/discovery.c
trunk/usr.sbin/ctld/isns.c
trunk/usr.sbin/ctld/isns.h
trunk/usr.sbin/ctld/kernel.c
trunk/usr.sbin/ctld/keys.c
trunk/usr.sbin/ctld/log.c
trunk/usr.sbin/ctld/login.c
trunk/usr.sbin/ctld/parse.y
trunk/usr.sbin/ctld/pdu.c
trunk/usr.sbin/ctld/token.l
Added: trunk/usr.sbin/ctld/Makefile
===================================================================
--- trunk/usr.sbin/ctld/Makefile (rev 0)
+++ trunk/usr.sbin/ctld/Makefile 2018-06-10 20:15:35 UTC (rev 10818)
@@ -0,0 +1,23 @@
+# $MidnightBSD$
+# $FreeBSD: stable/10/usr.sbin/ctld/Makefile 316235 2017-03-30 05:54:27Z ngie $
+
+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
+CFLAGS+= -I${.CURDIR}
+CFLAGS+= -I${.CURDIR}/../../sys
+CFLAGS+= -I${.CURDIR}/../../sys/cam/ctl
+CFLAGS+= -I${.CURDIR}/../../sys/dev/iscsi
+#CFLAGS+= -DICL_KERNEL_PROXY
+MAN= ctld.8 ctl.conf.5
+
+DPADD= ${LIBBSDXML} ${LIBL} ${LIBMD} ${LIBSBUF} ${LIBUTIL}
+LDADD= -lbsdxml -ll -lmd -lsbuf -lutil
+
+YFLAGS+= -v
+CLEANFILES= y.tab.c y.tab.h y.output
+
+WARNS?= 6
+NO_WMISSING_VARIABLE_DECLARATIONS=
+
+.include <bsd.prog.mk>
Property changes on: trunk/usr.sbin/ctld/Makefile
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/ctld/chap.c
===================================================================
--- trunk/usr.sbin/ctld/chap.c (rev 0)
+++ trunk/usr.sbin/ctld/chap.c 2018-06-10 20:15:35 UTC (rev 10818)
@@ -0,0 +1,423 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2014 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/10/usr.sbin/ctld/chap.c 286219 2015-08-03 07:20:33Z trasz $");
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <resolv.h>
+#include <md5.h>
+
+#include "ctld.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(sizeof(*chap), 1);
+ 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(sizeof(*rchap), 1);
+ 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);
+}
Property changes on: trunk/usr.sbin/ctld/chap.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/ctld/ctl.conf.5
===================================================================
--- trunk/usr.sbin/ctld/ctl.conf.5 (rev 0)
+++ trunk/usr.sbin/ctld/ctl.conf.5 2018-06-10 20:15:35 UTC (rev 10818)
@@ -0,0 +1,471 @@
+.\" $MidnightBSD$
+.\" Copyright (c) 2012 The FreeBSD Foundation
+.\" Copyright (c) 2015 Alexander Motin <mav at FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" This software was developed by Edward Tomasz Napierala under sponsorship
+.\" from the FreeBSD Foundation.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD: stable/10/usr.sbin/ctld/ctl.conf.5 291387 2015-11-27 15:19:36Z mav $
+.\"
+.Dd November 9, 2015
+.Dt CTL.CONF 5
+.Os
+.Sh NAME
+.Nm ctl.conf
+.Nd CAM Target Layer / iSCSI target daemon configuration file
+.Sh DESCRIPTION
+The
+.Nm
+configuration file is used by the
+.Xr ctld 8
+daemon.
+Lines starting with
+.Ql #
+are interpreted as comments.
+The general syntax of the
+.Nm
+file is:
+.Bd -literal -offset indent
+.No pidfile Ar path
+
+.No auth-group Ar name No {
+.Dl chap Ar user Ar secret
+.Dl ...
+}
+
+.No portal-group Ar name No {
+.Dl listen Ar address
+.\".Dl listen-iser Ar address
+.Dl discovery-auth-group Ar name
+.Dl ...
+}
+
+.No lun Ar name No {
+.Dl path Ar path
+}
+
+.No target Ar name {
+.Dl auth-group Ar name
+.Dl portal-group Ar name Op Ar agname
+.Dl port Ar name
+.Dl lun Ar number Ar name
+.Dl lun Ar number No {
+.Dl path Ar path
+.Dl }
+.Dl ...
+}
+.Ed
+.Ss Global Context
+.Bl -tag -width indent
+.It Ic auth-group Ar name
+Create an
+.Sy auth-group
+configuration context,
+defining a new auth-group,
+which can then be assigned to any number of targets.
+.It Ic debug Ar level
+The debug verbosity level.
+The default is 0.
+.It Ic maxproc Ar number
+The limit for concurrently running child processes handling
+incoming connections.
+The default is 30.
+A setting of 0 disables the limit.
+.It Ic pidfile Ar path
+The path to the pidfile.
+The default is
+.Pa /var/run/ctld.pid .
+.It Ic portal-group Ar name
+Create a
+.Sy portal-group
+configuration context,
+defining a new portal-group,
+which can then be assigned to any number of targets.
+.It Ic lun Ar name
+Create a
+.Sy lun
+configuration context, defining a LUN to be exported by some target(s).
+.It Ic target Ar name
+Create a
+.Sy target
+configuration context, which can contain one or more
+.Sy lun
+contexts.
+.It Ic timeout Ar seconds
+The timeout for login sessions, after which the connection
+will be forcibly terminated.
+The default is 60.
+A setting of 0 disables the timeout.
+.It Ic isns-server Ar address
+An IPv4 or IPv6 address and optionally port of iSNS server to register on.
+.It Ic isns-period Ar seconds
+iSNS registration period.
+Registered Network Entity not updated during this period will be unregistered.
+The default is 900.
+.It Ic isns-timeout Ar seconds
+Timeout for iSNS requests.
+The default is 5.
+.El
+.Ss auth-group Context
+.Bl -tag -width indent
+.It Ic auth-type Ar type
+Sets the authentication type.
+Type can be either
+.Qq Ar none ,
+.Qq Ar deny ,
+.Qq Ar chap ,
+or
+.Qq Ar chap-mutual .
+In most cases it is not necessary to set the type using this clause;
+it is usually used to disable authentication for a given
+.Sy auth-group .
+.It Ic chap Ar user Ar secret
+A set of CHAP authentication credentials.
+Note that for any
+.Sy auth-group ,
+the configuration may only contain either
+.Sy chap
+or
+.Sy chap-mutual
+entries; it is an error to mix them.
+.It Ic chap-mutual Ar user Ar secret Ar mutualuser Ar mutualsecret
+A set of mutual CHAP authentication credentials.
+Note that for any
+.Sy auth-group ,
+the configuration may only contain either
+.Sy chap
+or
+.Sy chap-mutual
+entries; it is an error to mix them.
+.It Ic initiator-name Ar initiator-name
+An iSCSI initiator name.
+Only initiators with a name matching one of the defined
+names will be allowed to connect.
+If not defined, there will be no restrictions based on initiator
+name.
+.It Ic initiator-portal Ar address Ns Op / Ns Ar prefixlen
+An iSCSI initiator portal: an IPv4 or IPv6 address, optionally
+followed by a literal slash and a prefix length.
+Only initiators with an address matching one of the defined
+addresses will be allowed to connect.
+If not defined, there will be no restrictions based on initiator
+address.
+.El
+.Ss portal-group Context
+.Bl -tag -width indent
+.It Ic discovery-auth-group Ar name
+Assign a previously defined authentication group to the portal group,
+to be used for target discovery.
+By default, portal groups are assigned predefined
+.Sy auth-group
+.Qq Ar default ,
+which denies discovery.
+Another predefined
+.Sy auth-group ,
+.Qq Ar no-authentication ,
+may be used
+to permit discovery without authentication.
+.It Ic discovery-filter Ar filter
+Determines which targets are returned during discovery.
+Filter can be either
+.Qq Ar none ,
+.Qq Ar portal ,
+.Qq Ar portal-name ,
+or
+.Qq Ar portal-name-auth .
+When set to
+.Qq Ar none ,
+discovery will return all targets assigned to that portal group.
+When set to
+.Qq Ar portal ,
+discovery will not return targets that cannot be accessed by the
+initiator because of their
+.Sy initiator-portal .
+When set to
+.Qq Ar portal-name ,
+the check will include both
+.Sy initiator-portal
+and
+.Sy initiator-name .
+When set to
+.Qq Ar portal-name-auth ,
+the check will include
+.Sy initiator-portal ,
+.Sy initiator-name ,
+and authentication credentials.
+The target is returned if it does not require CHAP authentication,
+or if the CHAP user and secret used during discovery match those
+used by the target.
+Note that when using
+.Qq Ar portal-name-auth ,
+targets that require CHAP authentication will only be returned if
+.Sy discovery-auth-group
+requires CHAP.
+The default is
+.Qq Ar none .
+.It Ic listen Ar address
+An IPv4 or IPv6 address and port to listen on for incoming connections.
+.\".It Ic listen-iser Ar address
+.\"An IPv4 or IPv6 address and port to listen on for incoming connections
+.\"using iSER (iSCSI over RDMA) protocol.
+.It Ic option Ar name Ar value
+The CTL-specific port options passed to the kernel.
+.It Ic redirect Ar address
+IPv4 or IPv6 address to redirect initiators to.
+When configured, all initiators attempting to connect to portal
+belonging to this
+.Sy portal-group
+will get redirected using "Target moved temporarily" login response.
+Redirection happens before authentication and any
+.Sy initiator-name
+or
+.Sy initiator-portal
+checks are skipped.
+.It Ic tag Ar value
+Unique 16-bit tag value of this
+.Sy portal-group .
+If not specified, the value is generated automatically.
+.It Ic foreign
+Specifies that this
+.Sy portal-group
+is listened by some other host.
+This host will announce it on discovery stage, but won't listen.
+.El
+.Ss target Context
+.Bl -tag -width indent
+.It Ic alias Ar text
+Assign a human-readable description to the target.
+There is no default.
+.It Ic auth-group Ar name
+Assign a previously defined authentication group to the target.
+By default, targets that do not specify their own auth settings,
+using clauses such as
+.Sy chap
+or
+.Sy initiator-name ,
+are assigned
+predefined
+.Sy auth-group
+.Qq Ar default ,
+which denies all access.
+Another predefined
+.Sy auth-group ,
+.Qq Ar no-authentication ,
+may be used to permit access
+without authentication.
+Note that targets must only use one of
+.Sy auth-group , chap , No or Sy chap-mutual ;
+it is a configuration error to mix multiple types in one target.
+.It Ic auth-type Ar type
+Sets the authentication type.
+Type can be either
+.Qq Ar none ,
+.Qq Ar deny ,
+.Qq Ar chap ,
+or
+.Qq Ar chap-mutual .
+In most cases it is not necessary to set the type using this clause;
+it is usually used to disable authentication for a given
+.Sy target .
+This clause is mutually exclusive with
+.Sy auth-group ;
+one cannot use
+both in a single target.
+.It Ic chap Ar user Ar secret
+A set of CHAP authentication credentials.
+Note that targets must only use one of
+.Sy auth-group , chap , No or Sy chap-mutual ;
+it is a configuration error to mix multiple types in one target.
+.It Ic chap-mutual Ar user Ar secret Ar mutualuser Ar mutualsecret
+A set of mutual CHAP authentication credentials.
+Note that targets must only use one of
+.Sy auth-group , chap , No or Sy chap-mutual ;
+it is a configuration error to mix multiple types in one target.
+.It Ic initiator-name Ar initiator-name
+An iSCSI initiator name.
+Only initiators with a name matching one of the defined
+names will be allowed to connect.
+If not defined, there will be no restrictions based on initiator
+name.
+This clause is mutually exclusive with
+.Sy auth-group ;
+one cannot use
+both in a single target.
+.It Ic initiator-portal Ar address Ns Op / Ns Ar prefixlen
+An iSCSI initiator portal: an IPv4 or IPv6 address, optionally
+followed by a literal slash and a prefix length.
+Only initiators with an address matching one of the defined
+addresses will be allowed to connect.
+If not defined, there will be no restrictions based on initiator
+address.
+This clause is mutually exclusive with
+.Sy auth-group ;
+one cannot use
+both in a single target.
+.It Ic portal-group Ar name Op Ar agname
+Assign a previously defined portal group to the target.
+The default portal group is
+.Qq Ar default ,
+which makes the target available
+on TCP port 3260 on all configured IPv4 and IPv6 addresses.
+Optional second argument specifies auth group name for connections
+to this specific portal group.
+If second argument is not specified, target auth group is used.
+.It Ic port Ar name
+.It Ic port Ar name/pp
+.It Ic port Ar name/pp/vp
+Assign specified CTL port (such as "isp0" or "isp2/1") to the target.
+On startup ctld configures LUN mapping and enables all assigned ports.
+Each port can be assigned to only one target.
+.It Ic redirect Aq Ar address
+IPv4 or IPv6 address to redirect initiators to.
+When configured, all initiators attempting to connect to this target
+will get redirected using "Target moved temporarily" login response.
+Redirection happens after successful authentication.
+.It Ic lun Ar number Ar name
+Export previously defined
+.Sy lun
+by the parent target.
+.It Ic lun Ar number
+Create a
+.Sy lun
+configuration context, defining a LUN exported by the parent target.
+.El
+.Ss lun Context
+.Bl -tag -width indent
+.It Ic backend Ar block No | Ar ramdisk
+The CTL backend to use for a given LUN.
+Valid choices are
+.Qq Ar block
+and
+.Qq Ar ramdisk ;
+block is used for LUNs backed
+by files or disk device nodes; ramdisk is a bitsink device, used mostly for
+testing.
+The default backend is block.
+.It Ic blocksize Ar size
+The blocksize visible to the initiator.
+The default blocksize is 512 for disks, and 2048 for CD/DVDs.
+.It Ic ctl-lun Ar lun_id
+Global numeric identifier to use for a given LUN inside CTL.
+By default CTL allocates those IDs dynamically, but explicit specification
+may be needed for consistency in HA configurations.
+.It Ic device-id Ar string
+The SCSI Device Identification string presented to the initiator.
+.It Ic device-type Ar type
+Specify the SCSI device type to use when creating the LUN.
+Currently CTL supports Direct Access (type 0), Processor (type 3)
+and CD/DVD (type 5) LUNs.
+.It Ic option Ar name Ar value
+The CTL-specific options passed to the kernel.
+All CTL-specific options are documented in the
+.Sx OPTIONS
+section of
+.Xr ctladm 8 .
+.It Ic path Ar path
+The path to the file, device node, or
+.Xr zfs 8
+volume used to back the LUN.
+For optimal performance, create the volume with the
+.Qq Ar volmode=dev
+property set.
+.It Ic serial Ar string
+The SCSI serial number presented to the initiator.
+.It Ic size Ar size
+The LUN size, in bytes.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /etc/ctl.conf" -compact
+.It Pa /etc/ctl.conf
+The default location of the
+.Xr ctld 8
+configuration file.
+.El
+.Sh EXAMPLES
+.Bd -literal
+auth-group ag0 {
+ chap-mutual "user" "secret" "mutualuser" "mutualsecret"
+ chap-mutual "user2" "secret2" "mutualuser" "mutualsecret"
+}
+
+auth-group ag1 {
+ auth-type none
+ initiator-name "iqn.2012-06.com.example:initiatorhost1"
+ initiator-name "iqn.2012-06.com.example:initiatorhost2"
+ initiator-portal 192.168.1.1/24
+ initiator-portal [2001:db8::de:ef]
+}
+
+portal-group pg0 {
+ discovery-auth-group no-authentication
+ listen 0.0.0.0:3260
+ listen [::]:3260
+ listen [fe80::be:ef]:3261
+}
+
+target iqn.2012-06.com.example:target0 {
+ alias "Example target"
+ auth-group no-authentication
+ lun 0 {
+ path /dev/zvol/tank/example_0
+ blocksize 4096
+ size 4G
+ }
+}
+
+lun example_1 {
+ path /dev/zvol/tank/example_1
+ option naa 0x50015178f369f093
+}
+
+target iqn.2012-06.com.example:target1 {
+ auth-group ag0
+ portal-group pg0
+ lun 0 example_1
+ lun 1 {
+ path /dev/zvol/tank/example_2
+ option foo bar
+ }
+}
+
+target naa.50015178f369f092 {
+ port isp0
+ port isp1
+ lun 0 example_1
+}
+.Ed
+.Sh SEE ALSO
+.Xr ctl 4 ,
+.Xr ctladm 8 ,
+.Xr ctld 8 ,
+.Xr zfs 8
+.Sh AUTHORS
+The
+.Nm
+configuration file functionality for
+.Xr ctld 8
+was developed by
+.An Edward Tomasz Napierala Aq trasz at FreeBSD.org
+under sponsorship from the FreeBSD Foundation.
Property changes on: trunk/usr.sbin/ctld/ctl.conf.5
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/ctld/ctld.8
===================================================================
--- trunk/usr.sbin/ctld/ctld.8 (rev 0)
+++ trunk/usr.sbin/ctld/ctld.8 2018-06-10 20:15:35 UTC (rev 10818)
@@ -0,0 +1,120 @@
+.\" $MidnightBSD$
+.\" Copyright (c) 2012 The FreeBSD Foundation
+.\" All rights reserved.
+.\"
+.\" This software was developed by Edward Tomasz Napierala under sponsorship
+.\" from the FreeBSD Foundation.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD: stable/10/usr.sbin/ctld/ctld.8 284671 2015-06-21 06:54:54Z trasz $
+.\"
+.Dd May 22, 2015
+.Dt CTLD 8
+.Os
+.Sh NAME
+.Nm ctld
+.Nd CAM Target Layer / iSCSI target daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl f Ar config-file
+.Sh DESCRIPTION
+The
+.Nm
+daemon is responsible for managing the CAM Target Layer configuration,
+accepting incoming iSCSI connections, performing authentication and
+passing connections to the kernel part of the native iSCSI target.
+.Pp
+Upon startup, the
+.Nm
+daemon parses the configuration file and exits, if it encounters any errors.
+Then it compares the configuration with the kernel list of LUNs managed
+by previously running
+.Nm
+instances, removes LUNs no longer existing in the configuration file,
+and creates new LUNs as necessary.
+After that it listens for the incoming iSCSI connections, performs
+authentication, and, if successful, passes the connections to the kernel part
+of CTL iSCSI target, which handles it from that point.
+.Pp
+When it receives a SIGHUP signal, the
+.Nm
+reloads its configuration and applies the changes to the kernel.
+Changes are applied in a way that avoids unnecessary disruptions;
+for example removing one LUN does not affect other LUNs.
+.Pp
+When exiting gracefully, the
+.Nm
+daemon removes LUNs it managed and forcibly disconnects all the clients.
+Otherwise - for example, when killed with SIGKILL - LUNs stay configured
+and clients remain connected.
+.Pp
+To perform administrative actions that apply to already connected
+sessions, such as forcing termination, use
+.Xr ctladm 8 .
+.Pp
+The following options are available:
+.Bl -tag -width ".Fl P Ar pidfile"
+.It Fl f Ar config-file
+Specifies the name of the configuration file.
+The default is
+.Pa /etc/ctl.conf .
+.It Fl d
+Debug mode.
+The server sends verbose debug output to standard error, and does not
+put itself in the background.
+The server will also not fork and will exit after processing one connection.
+This option is only intended for debugging the target.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /var/run/ctld.pid" -compact
+.It Pa /etc/ctl.conf
+The configuration file for
+.Nm .
+The file format and configuration options are described in
+.Xr ctl.conf 5 .
+.It Pa /var/run/ctld.pid
+The default location of the
+.Nm
+PID file.
+.El
+.Sh EXIT STATUS
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr ctl 4 ,
+.Xr ctl.conf 5 ,
+.Xr ctladm 8 ,
+.Xr ctlstat 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 10.0 .
+.Sh AUTHORS
+The
+.Nm
+was developed by
+.An Edward Tomasz Napierala Aq trasz at FreeBSD.org
+under sponsorship from the FreeBSD Foundation.
Property changes on: trunk/usr.sbin/ctld/ctld.8
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/ctld/ctld.c
===================================================================
--- trunk/usr.sbin/ctld/ctld.c (rev 0)
+++ trunk/usr.sbin/ctld/ctld.c 2018-06-10 20:15:35 UTC (rev 10818)
@@ -0,0 +1,2618 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2012 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/10/usr.sbin/ctld/ctld.c 317352 2017-04-24 06:33:08Z mav $");
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ctld.h"
+#include "isns.h"
+
+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 void
+usage(void)
+{
+
+ fprintf(stderr, "usage: ctld [-d][-f config-file]\n");
+ exit(1);
+}
+
+char *
+checked_strdup(const char *s)
+{
+ char *c;
+
+ c = strdup(s);
+ if (c == NULL)
+ log_err(1, "strdup");
+ return (c);
+}
+
+struct conf *
+conf_new(void)
+{
+ struct conf *conf;
+
+ conf = calloc(1, sizeof(*conf));
+ if (conf == NULL)
+ log_err(1, "calloc");
+ TAILQ_INIT(&conf->conf_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_initator_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_initator_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_initator_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_initator_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_errx(1, "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_initator_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(). */
+ 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_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 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;
+ }
+ gethostname(hostname, sizeof(hostname));
+
+ 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 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;
+ }
+ gethostname(hostname, sizeof(hostname));
+
+ 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 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;
+ gethostname(hostname, sizeof(hostname));
+
+ 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_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;
+ 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_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 *
+connection_new(struct portal *portal, int fd, const char *host,
+ const struct sockaddr *client_sa)
+{
+ struct connection *conn;
+
+ conn = calloc(1, sizeof(*conn));
+ if (conn == NULL)
+ log_err(1, "calloc");
+ conn->conn_portal = portal;
+ conn->conn_socket = fd;
+ conn->conn_initiator_addr = checked_strdup(host);
+ memcpy(&conn->conn_initiator_sa, client_sa, client_sa->sa_len);
+
+ /*
+ * Default values, from RFC 3720, section 12.
+ */
+ conn->conn_max_data_segment_length = 8192;
+ conn->conn_max_burst_length = 262144;
+ conn->conn_immediate_data = true;
+
+ return (conn);
+}
+
+#if 0
+static void
+conf_print(struct conf *conf)
+{
+ struct auth_group *ag;
+ struct auth *auth;
+ struct 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_initator_name);
+ TAILQ_FOREACH(auth_portal, &ag->ag_portals, an_next)
+ fprintf(stderr, "\t initiator-portal %s\n",
+ auth_portal->an_initator_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",
+ lo->o_name, lo->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);
+ error = setsockopt(newp->p_socket, SOL_SOCKET,
+ SO_REUSEADDR, &one, sizeof(one));
+ if (error != 0) {
+ log_warn("setsockopt(SO_REUSEADDR) failed "
+ "for %s", newp->p_listen);
+ close(newp->p_socket);
+ newp->p_socket = 0;
+ cumulated_error++;
+ continue;
+ }
+ error = bind(newp->p_socket, newp->p_ai->ai_addr,
+ newp->p_ai->ai_addrlen);
+ if (error != 0) {
+ log_warn("bind(2) failed for %s",
+ newp->p_listen);
+ close(newp->p_socket);
+ newp->p_socket = 0;
+ cumulated_error++;
+ continue;
+ }
+ error = listen(newp->p_socket, -1);
+ if (error != 0) {
+ log_warn("listen(2) failed for %s",
+ newp->p_listen);
+ close(newp->p_socket);
+ newp->p_socket = 0;
+ cumulated_error++;
+ continue;
+ }
+ }
+ }
+
+ /*
+ * 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
+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;
+ 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");
+}
+
+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;
+
+ while ((ch = getopt(argc, argv, "df:R")) != -1) {
+ switch (ch) {
+ case 'd':
+ dont_daemonize = true;
+ debug++;
+ 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);
+ if (newconf == NULL)
+ log_errx(1, "configuration error; exiting");
+ 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);
+ 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 */
+}
Property changes on: trunk/usr.sbin/ctld/ctld.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/ctld/ctld.h
===================================================================
--- trunk/usr.sbin/ctld/ctld.h (rev 0)
+++ trunk/usr.sbin/ctld/ctld.h 2018-06-10 20:15:35 UTC (rev 10818)
@@ -0,0 +1,450 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2012 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: stable/10/usr.sbin/ctld/ctld.h 317352 2017-04-24 06:33:08Z mav $
+ */
+
+#ifndef CTLD_H
+#define CTLD_H
+
+#include <sys/queue.h>
+#ifdef ICL_KERNEL_PROXY
+#include <sys/types.h>
+#endif
+#include <sys/socket.h>
+#include <stdbool.h>
+#include <libutil.h>
+
+#define DEFAULT_CONFIG_PATH "/etc/ctl.conf"
+#define DEFAULT_PIDFILE "/var/run/ctld.pid"
+#define DEFAULT_BLOCKSIZE 512
+#define DEFAULT_CD_BLOCKSIZE 2048
+
+#define MAX_LUNS 1024
+#define MAX_NAME_LEN 223
+#define MAX_DATA_SEGMENT_LENGTH (128 * 1024)
+#define MAX_BURST_LENGTH 16776192
+#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_initator_name;
+};
+
+struct auth_portal {
+ TAILQ_ENTRY(auth_portal) ap_next;
+ struct auth_group *ap_auth_group;
+ char *ap_initator_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_redirection;
+
+ 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;
+
+ 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 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;
+ size_t conn_max_data_segment_length;
+ size_t conn_max_burst_length;
+ int conn_immediate_data;
+ int conn_header_digest;
+ int conn_data_digest;
+ 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);
+
+struct conf *conf_new(void);
+struct conf *conf_new_from_file(const char *path, struct conf *old);
+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_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_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);
+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 *);
+bool valid_iscsi_name(const char *name);
+void set_timeout(int timeout, int fatal);
+bool timed_out(void);
+
+#endif /* !CTLD_H */
Property changes on: trunk/usr.sbin/ctld/ctld.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/ctld/discovery.c
===================================================================
--- trunk/usr.sbin/ctld/discovery.c (rev 0)
+++ trunk/usr.sbin/ctld/discovery.c 2018-06-10 20:15:35 UTC (rev 10818)
@@ -0,0 +1,337 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2012 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/10/usr.sbin/ctld/discovery.c 288704 2015-10-05 07:42:05Z mav $");
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/socket.h>
+
+#include "ctld.h"
+#include "iscsi_proto.h"
+
+static struct pdu *
+text_receive(struct connection *conn)
+{
+ struct pdu *request;
+ struct iscsi_bhs_text_request *bhstr;
+
+ request = pdu_new(conn);
+ pdu_receive(request);
+ if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) !=
+ ISCSI_BHS_OPCODE_TEXT_REQUEST)
+ log_errx(1, "protocol error: received invalid opcode 0x%x",
+ request->pdu_bhs->bhs_opcode);
+ bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs;
+#if 0
+ if ((bhstr->bhstr_flags & ISCSI_BHSTR_FLAGS_FINAL) == 0)
+ log_errx(1, "received Text PDU without the \"F\" flag");
+#endif
+ /*
+ * XXX: Implement the C flag some day.
+ */
+ if ((bhstr->bhstr_flags & BHSTR_FLAGS_CONTINUE) != 0)
+ log_errx(1, "received Text PDU with unsupported \"C\" flag");
+ if (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 StatSN: "
+ "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 StatSN: "
+ "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,
+ 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)
+{
+ 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_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);
+ response = logout_new_response(request);
+
+ pdu_send(response);
+ pdu_delete(response);
+ pdu_delete(request);
+
+ log_debugx("discovery session done");
+}
Property changes on: trunk/usr.sbin/ctld/discovery.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/ctld/isns.c
===================================================================
--- trunk/usr.sbin/ctld/isns.c (rev 0)
+++ trunk/usr.sbin/ctld/isns.c 2018-06-10 20:15:35 UTC (rev 10818)
@@ -0,0 +1,253 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2014 Alexander Motin <mav at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/10/usr.sbin/ctld/isns.c 288704 2015-10-05 07:42:05Z mav $");
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/endian.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ctld.h"
+#include "isns.h"
+
+struct isns_req *
+isns_req_alloc(void)
+{
+ struct isns_req *req;
+
+ req = calloc(sizeof(struct isns_req), 1);
+ if (req == NULL) {
+ log_err(1, "calloc");
+ return (NULL);
+ }
+ req->ir_buflen = sizeof(struct isns_hdr);
+ req->ir_usedlen = 0;
+ req->ir_buf = calloc(req->ir_buflen, 1);
+ if (req->ir_buf == NULL) {
+ free(req);
+ log_err(1, "calloc");
+ return (NULL);
+ }
+ return (req);
+}
+
+struct isns_req *
+isns_req_create(uint16_t func, uint16_t flags)
+{
+ struct isns_req *req;
+ struct isns_hdr *hdr;
+
+ req = isns_req_alloc();
+ req->ir_usedlen = sizeof(struct isns_hdr);
+ hdr = (struct isns_hdr *)req->ir_buf;
+ be16enc(hdr->ih_version, ISNS_VERSION);
+ be16enc(hdr->ih_function, func);
+ be16enc(hdr->ih_flags, flags);
+ return (req);
+}
+
+void
+isns_req_free(struct isns_req *req)
+{
+
+ free(req->ir_buf);
+ free(req);
+}
+
+static int
+isns_req_getspace(struct isns_req *req, uint32_t len)
+{
+ void *newbuf;
+ int newlen;
+
+ if (req->ir_usedlen + len <= req->ir_buflen)
+ return (0);
+ newlen = 1 << flsl(req->ir_usedlen + len);
+ newbuf = realloc(req->ir_buf, newlen);
+ if (newbuf == NULL) {
+ log_err(1, "realloc");
+ return (1);
+ }
+ req->ir_buf = newbuf;
+ req->ir_buflen = newlen;
+ return (0);
+}
+
+void
+isns_req_add(struct isns_req *req, uint32_t tag, uint32_t len,
+ const void *value)
+{
+ struct isns_tlv *tlv;
+ uint32_t vlen;
+
+ vlen = len + ((len & 3) ? (4 - (len & 3)) : 0);
+ isns_req_getspace(req, sizeof(*tlv) + vlen);
+ tlv = (struct isns_tlv *)&req->ir_buf[req->ir_usedlen];
+ be32enc(tlv->it_tag, tag);
+ be32enc(tlv->it_length, vlen);
+ memcpy(tlv->it_value, value, len);
+ if (vlen != len)
+ memset(&tlv->it_value[len], 0, vlen - len);
+ req->ir_usedlen += sizeof(*tlv) + vlen;
+}
+
+void
+isns_req_add_delim(struct isns_req *req)
+{
+
+ isns_req_add(req, 0, 0, NULL);
+}
+
+void
+isns_req_add_str(struct isns_req *req, uint32_t tag, const char *value)
+{
+
+ isns_req_add(req, tag, strlen(value) + 1, value);
+}
+
+void
+isns_req_add_32(struct isns_req *req, uint32_t tag, uint32_t value)
+{
+ uint32_t beval;
+
+ be32enc(&beval, value);
+ isns_req_add(req, tag, sizeof(value), &beval);
+}
+
+void
+isns_req_add_addr(struct isns_req *req, uint32_t tag, struct addrinfo *ai)
+{
+ struct sockaddr_in *in4;
+ struct sockaddr_in6 *in6;
+ uint8_t buf[16];
+
+ switch (ai->ai_addr->sa_family) {
+ case AF_INET:
+ in4 = (struct sockaddr_in *)(void *)ai->ai_addr;
+ memset(buf, 0, 10);
+ buf[10] = 0xff;
+ buf[11] = 0xff;
+ memcpy(&buf[12], &in4->sin_addr, sizeof(in4->sin_addr));
+ isns_req_add(req, tag, sizeof(buf), buf);
+ break;
+ case AF_INET6:
+ in6 = (struct sockaddr_in6 *)(void *)ai->ai_addr;
+ isns_req_add(req, tag, sizeof(in6->sin6_addr), &in6->sin6_addr);
+ break;
+ default:
+ log_errx(1, "Unsupported address family %d",
+ ai->ai_addr->sa_family);
+ }
+}
+
+void
+isns_req_add_port(struct isns_req *req, uint32_t tag, struct addrinfo *ai)
+{
+ struct sockaddr_in *in4;
+ struct sockaddr_in6 *in6;
+ uint32_t buf;
+
+ switch (ai->ai_addr->sa_family) {
+ case AF_INET:
+ in4 = (struct sockaddr_in *)(void *)ai->ai_addr;
+ be32enc(&buf, ntohs(in4->sin_port));
+ isns_req_add(req, tag, sizeof(buf), &buf);
+ break;
+ case AF_INET6:
+ in6 = (struct sockaddr_in6 *)(void *)ai->ai_addr;
+ be32enc(&buf, ntohs(in6->sin6_port));
+ isns_req_add(req, tag, sizeof(buf), &buf);
+ break;
+ default:
+ log_errx(1, "Unsupported address family %d",
+ ai->ai_addr->sa_family);
+ }
+}
+
+int
+isns_req_send(int s, struct isns_req *req)
+{
+ struct isns_hdr *hdr;
+ int res;
+
+ hdr = (struct isns_hdr *)req->ir_buf;
+ be16enc(hdr->ih_length, req->ir_usedlen - sizeof(*hdr));
+ be16enc(hdr->ih_flags, be16dec(hdr->ih_flags) |
+ ISNS_FLAG_LAST | ISNS_FLAG_FIRST);
+ be16enc(hdr->ih_transaction, 0);
+ be16enc(hdr->ih_sequence, 0);
+
+ res = write(s, req->ir_buf, req->ir_usedlen);
+ return ((res < 0) ? -1 : 0);
+}
+
+int
+isns_req_receive(int s, struct isns_req *req)
+{
+ struct isns_hdr *hdr;
+ ssize_t res, len;
+
+ req->ir_usedlen = 0;
+ isns_req_getspace(req, sizeof(*hdr));
+ res = read(s, req->ir_buf, sizeof(*hdr));
+ if (res < (ssize_t)sizeof(*hdr))
+ return (-1);
+ req->ir_usedlen = sizeof(*hdr);
+ hdr = (struct isns_hdr *)req->ir_buf;
+ if (be16dec(hdr->ih_version) != ISNS_VERSION)
+ return (-1);
+ if ((be16dec(hdr->ih_flags) & (ISNS_FLAG_LAST | ISNS_FLAG_FIRST)) !=
+ (ISNS_FLAG_LAST | ISNS_FLAG_FIRST))
+ return (-1);
+ len = be16dec(hdr->ih_length);
+ isns_req_getspace(req, len);
+ res = read(s, &req->ir_buf[req->ir_usedlen], len);
+ if (res < len)
+ return (-1);
+ req->ir_usedlen += len;
+ return (0);
+}
+
+uint32_t
+isns_req_get_status(struct isns_req *req)
+{
+
+ if (req->ir_usedlen < sizeof(struct isns_hdr) + 4)
+ return (-1);
+ return (be32dec(&req->ir_buf[sizeof(struct isns_hdr)]));
+}
Property changes on: trunk/usr.sbin/ctld/isns.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/ctld/isns.h
===================================================================
--- trunk/usr.sbin/ctld/isns.h (rev 0)
+++ trunk/usr.sbin/ctld/isns.h 2018-06-10 20:15:35 UTC (rev 10818)
@@ -0,0 +1,93 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2014 Alexander Motin <mav at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: stable/10/usr.sbin/ctld/isns.h 274939 2014-11-24 00:47:04Z mav $
+ */
+
+#ifndef _ISNS_H
+#define _ISNS_H
+
+#define ISNS_VERSION 0x0001
+
+#define ISNS_FUNC_DEVATTRREG 0x0001
+#define ISNS_FUNC_DEVATTRQRY 0x0002
+#define ISNS_FUNC_DEVGETNEXT 0x0003
+#define ISNS_FUNC_DEVDEREG 0x0004
+#define ISNS_FUNC_SCNREG 0x0005
+#define ISNS_FUNC_SCNDEREG 0x0006
+#define ISNS_FUNC_SCNEVENT 0x0007
+#define ISNS_FUNC_SCN 0x0008
+#define ISNS_FUNC_DDREG 0x0009
+#define ISNS_FUNC_DDDEREG 0x000a
+#define ISNS_FUNC_DDSREG 0x000b
+#define ISNS_FUNC_DDSDEREG 0x000c
+#define ISNS_FUNC_ESI 0x000d
+#define ISNS_FUNC_HEARTBEAT 0x000e
+#define ISNS_FUNC_RESPONSE 0x8000
+
+#define ISNS_FLAG_CLIENT 0x8000
+#define ISNS_FLAG_SERVER 0x4000
+#define ISNS_FLAG_AUTH 0x2000
+#define ISNS_FLAG_REPLACE 0x1000
+#define ISNS_FLAG_LAST 0x0800
+#define ISNS_FLAG_FIRST 0x0400
+
+struct isns_hdr {
+ uint8_t ih_version[2];
+ uint8_t ih_function[2];
+ uint8_t ih_length[2];
+ uint8_t ih_flags[2];
+ uint8_t ih_transaction[2];
+ uint8_t ih_sequence[2];
+};
+
+struct isns_tlv {
+ uint8_t it_tag[4];
+ uint8_t it_length[4];
+ uint8_t it_value[];
+};
+
+struct isns_req {
+ u_int ir_buflen;
+ u_int ir_usedlen;
+ uint8_t *ir_buf;
+};
+
+struct isns_req * isns_req_alloc(void);
+struct isns_req * isns_req_create(uint16_t func, uint16_t flags);
+void isns_req_free(struct isns_req *req);
+void isns_req_add(struct isns_req *req, uint32_t tag, uint32_t len,
+ const void *value);
+void isns_req_add_delim(struct isns_req *req);
+void isns_req_add_str(struct isns_req *req, uint32_t tag, const char *value);
+void isns_req_add_32(struct isns_req *req, uint32_t tag, uint32_t value);
+void isns_req_add_addr(struct isns_req *req, uint32_t tag, struct addrinfo *ai);
+void isns_req_add_port(struct isns_req *req, uint32_t tag, struct addrinfo *ai);
+int isns_req_send(int s, struct isns_req *req);
+int isns_req_receive(int s, struct isns_req *req);
+uint32_t isns_req_get_status(struct isns_req *req);
+
+#endif /* _ISNS_H */
Property changes on: trunk/usr.sbin/ctld/isns.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/ctld/kernel.c
===================================================================
--- trunk/usr.sbin/ctld/kernel.c (rev 0)
+++ trunk/usr.sbin/ctld/kernel.c 2018-06-10 20:15:35 UTC (rev 10818)
@@ -0,0 +1,1234 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2003, 2004 Silicon Graphics International Corp.
+ * Copyright (c) 1997-2007 Kenneth D. Merry
+ * Copyright (c) 2012 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by Edward Tomasz Napierala
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/10/usr.sbin/ctld/kernel.c 316253 2017-03-30 06:13:54Z ngie $");
+
+#include <sys/param.h>
+#include <sys/capsicum.h>
+#include <sys/callout.h>
+#include <sys/ioctl.h>
+#include <sys/linker.h>
+#include <sys/queue.h>
+#include <sys/sbuf.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <bsdxml.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_message.h>
+#include <cam/ctl/ctl.h>
+#include <cam/ctl/ctl_io.h>
+#include <cam/ctl/ctl_backend.h>
+#include <cam/ctl/ctl_ioctl.h>
+#include <cam/ctl/ctl_util.h>
+#include <cam/ctl/ctl_scsi_all.h>
+
+#include "ctld.h"
+
+#ifdef ICL_KERNEL_PROXY
+#include <netdb.h>
+#endif
+
+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);
+}
+
+/*
+ * 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) {
+ cur_lun->device_type = strtoull(str, NULL, 0);
+ } else if (strcmp(name, "size") == 0) {
+ cur_lun->size_blocks = strtoull(str, NULL, 0);
+ } else if (strcmp(name, "blocksize") == 0) {
+ cur_lun->blocksize = strtoul(str, NULL, 0);
+ } else if (strcmp(name, "serial_number") == 0) {
+ cur_lun->serial_number = str;
+ str = NULL;
+ } else if (strcmp(name, "device_id") == 0) {
+ cur_lun->device_id = str;
+ str = NULL;
+ } else if (strcmp(name, "ctld_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) {
+ cur_port->pp = strtoul(str, NULL, 0);
+ } else if (strcmp(name, "virtual_port") == 0) {
+ 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) {
+ cur_port->cfiscsi_state = strtoul(str, NULL, 0);
+ } else if (strcmp(name, "cfiscsi_portal_group_tag") == 0) {
+ 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;
+ if (name)
+ free(name);
+ if (port->pp == 0 && port->vp == 0)
+ name = checked_strdup(port->port_name);
+ else if (port->vp == 0)
+ asprintf(&name, "%s/%d", port->port_name, port->pp);
+ else
+ asprintf(&name, "%s/%d/%d", port->port_name, port->pp,
+ port->vp);
+
+ 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;
+ }
+ if (name)
+ 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);
+}
+
+static void
+str_arg(struct ctl_be_arg *arg, const char *name, const char *value)
+{
+
+ arg->namelen = strlen(name) + 1;
+ arg->name = __DECONST(char *, name);
+ arg->vallen = strlen(value) + 1;
+ arg->value = __DECONST(char *, value);
+ arg->flags = CTL_BEARG_ASCII | CTL_BEARG_RD;
+}
+
+int
+kernel_lun_add(struct lun *lun)
+{
+ struct option *o;
+ struct ctl_lun_req req;
+ int error, i, num_options;
+
+ bzero(&req, sizeof(req));
+
+ strlcpy(req.backend, lun->l_backend, sizeof(req.backend));
+ req.reqtype = CTL_LUNREQ_CREATE;
+
+ req.reqdata.create.blocksize_bytes = lun->l_blocksize;
+
+ if (lun->l_size != 0)
+ req.reqdata.create.lun_size_bytes = lun->l_size;
+
+ 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);
+ }
+
+ num_options = 0;
+ TAILQ_FOREACH(o, &lun->l_options, o_next)
+ num_options++;
+
+ req.num_be_args = num_options;
+ if (num_options > 0) {
+ req.be_args = malloc(num_options * sizeof(*req.be_args));
+ if (req.be_args == NULL) {
+ log_warn("error allocating %zd bytes",
+ num_options * sizeof(*req.be_args));
+ return (1);
+ }
+
+ i = 0;
+ TAILQ_FOREACH(o, &lun->l_options, o_next) {
+ str_arg(&req.be_args[i], o->o_name, o->o_value);
+ i++;
+ }
+ assert(i == num_options);
+ }
+
+ error = ioctl(ctl_fd, CTL_LUN_REQ, &req);
+ free(req.be_args);
+ if (error != 0) {
+ log_warn("error issuing CTL_LUN_REQ ioctl");
+ return (1);
+ }
+
+ 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, i, num_options;
+
+ 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;
+
+ num_options = 0;
+ TAILQ_FOREACH(o, &lun->l_options, o_next)
+ num_options++;
+
+ req.num_be_args = num_options;
+ if (num_options > 0) {
+ req.be_args = malloc(num_options * sizeof(*req.be_args));
+ if (req.be_args == NULL) {
+ log_warn("error allocating %zd bytes",
+ num_options * sizeof(*req.be_args));
+ return (1);
+ }
+
+ i = 0;
+ TAILQ_FOREACH(o, &lun->l_options, o_next) {
+ str_arg(&req.be_args[i], o->o_name, o->o_value);
+ i++;
+ }
+ assert(i == num_options);
+ }
+
+ error = ioctl(ctl_fd, CTL_LUN_REQ, &req);
+ free(req.be_args);
+ if (error != 0) {
+ log_warn("error issuing CTL_LUN_REQ ioctl");
+ return (1);
+ }
+
+ 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)
+{
+ 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));
+#ifdef ICL_KERNEL_PROXY
+ if (proxy_mode)
+ req.data.handoff.connection_id = conn->conn_socket;
+ else
+ req.data.handoff.socket = conn->conn_socket;
+#else
+ req.data.handoff.socket = 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)
+ req.data.handoff.header_digest = CTL_ISCSI_DIGEST_CRC32C;
+ if (conn->conn_data_digest == CONN_DIGEST_CRC32C)
+ req.data.handoff.data_digest = CTL_ISCSI_DIGEST_CRC32C;
+ req.data.handoff.cmdsn = conn->conn_cmdsn;
+ req.data.handoff.statsn = conn->conn_statsn;
+ req.data.handoff.max_recv_data_segment_length =
+ conn->conn_max_data_segment_length;
+ req.data.handoff.max_burst_length = conn->conn_max_burst_length;
+ req.data.handoff.immediate_data = conn->conn_immediate_data;
+
+ if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) {
+ log_err(1, "error issuing CTL_ISCSI ioctl; "
+ "dropping connection");
+ }
+
+ if (req.status != CTL_ISCSI_OK) {
+ log_errx(1, "error returned from CTL iSCSI handoff request: "
+ "%s; dropping connection", req.error_str);
+ }
+}
+
+int
+kernel_port_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 tagstr[16];
+ int error, i, n;
+
+ /* Create iSCSI port. */
+ if (port->p_portal_group) {
+ bzero(&req, sizeof(req));
+ strlcpy(req.driver, "iscsi", sizeof(req.driver));
+ req.reqtype = CTL_REQ_CREATE;
+ req.num_args = 5;
+ TAILQ_FOREACH(o, &pg->pg_options, o_next)
+ req.num_args++;
+ req.args = malloc(req.num_args * sizeof(*req.args));
+ if (req.args == NULL)
+ log_err(1, "malloc");
+ n = 0;
+ req.args[n].namelen = sizeof("port_id");
+ req.args[n].name = __DECONST(char *, "port_id");
+ req.args[n].vallen = sizeof(port->p_ctl_port);
+ req.args[n].value = &port->p_ctl_port;
+ req.args[n++].flags = CTL_BEARG_WR;
+ str_arg(&req.args[n++], "cfiscsi_target", targ->t_name);
+ snprintf(tagstr, sizeof(tagstr), "%d", pg->pg_tag);
+ str_arg(&req.args[n++], "cfiscsi_portal_group_tag", tagstr);
+ if (targ->t_alias)
+ str_arg(&req.args[n++], "cfiscsi_target_alias", targ->t_alias);
+ str_arg(&req.args[n++], "ctld_portal_group_name", pg->pg_name);
+ TAILQ_FOREACH(o, &pg->pg_options, o_next)
+ str_arg(&req.args[n++], o->o_name, o->o_value);
+ req.num_args = n;
+ error = ioctl(ctl_fd, CTL_PORT_REQ, &req);
+ free(req.args);
+ 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);
+ }
+ } 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;
+ char tagstr[16];
+ 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 port. */
+ if (port->p_portal_group) {
+ bzero(&req, sizeof(req));
+ strlcpy(req.driver, "iscsi", sizeof(req.driver));
+ req.reqtype = CTL_REQ_REMOVE;
+ req.num_args = 2;
+ req.args = malloc(req.num_args * sizeof(*req.args));
+ if (req.args == NULL)
+ log_err(1, "malloc");
+ str_arg(&req.args[0], "cfiscsi_target", targ->t_name);
+ snprintf(tagstr, sizeof(tagstr), "%d", pg->pg_tag);
+ str_arg(&req.args[1], "cfiscsi_portal_group_tag", tagstr);
+ error = ioctl(ctl_fd, CTL_PORT_REQ, &req);
+ free(req.args);
+ 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 ctl_iscsi req;
+
+ pdu->pdu_data = malloc(MAX_DATA_SEGMENT_LENGTH);
+ if (pdu->pdu_data == NULL)
+ log_err(1, "malloc");
+
+ bzero(&req, sizeof(req));
+
+ req.type = CTL_ISCSI_RECEIVE;
+ req.data.receive.connection_id = pdu->pdu_connection->conn_socket;
+ req.data.receive.bhs = pdu->pdu_bhs;
+ req.data.receive.data_segment_len = MAX_DATA_SEGMENT_LENGTH;
+ req.data.receive.data_segment = pdu->pdu_data;
+
+ if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) {
+ log_err(1, "error issuing CTL_ISCSI ioctl; "
+ "dropping connection");
+ }
+
+ if (req.status != CTL_ISCSI_OK) {
+ log_errx(1, "error returned from CTL iSCSI receive: "
+ "%s; dropping connection", req.error_str);
+ }
+
+}
+
+#endif /* ICL_KERNEL_PROXY */
+
+/*
+ * XXX: I CANT INTO LATIN
+ */
+void
+kernel_capsicate(void)
+{
+ int error;
+ cap_rights_t rights;
+ const unsigned long cmds[] = { CTL_ISCSI };
+
+ cap_rights_init(&rights, CAP_IOCTL);
+ error = cap_rights_limit(ctl_fd, &rights);
+ if (error != 0 && errno != ENOSYS)
+ log_err(1, "cap_rights_limit");
+
+ error = cap_ioctls_limit(ctl_fd, cmds,
+ sizeof(cmds) / sizeof(cmds[0]));
+ if (error != 0 && errno != ENOSYS)
+ log_err(1, "cap_ioctls_limit");
+
+ error = cap_enter();
+ if (error != 0 && errno != ENOSYS)
+ log_err(1, "cap_enter");
+
+ if (cap_sandboxed())
+ log_debugx("Capsicum capability mode enabled");
+ else
+ log_warnx("Capsicum capability mode not supported");
+}
+
Property changes on: trunk/usr.sbin/ctld/kernel.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/ctld/keys.c
===================================================================
--- trunk/usr.sbin/ctld/keys.c (rev 0)
+++ trunk/usr.sbin/ctld/keys.c 2018-06-10 20:15:35 UTC (rev 10818)
@@ -0,0 +1,198 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2012 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/10/usr.sbin/ctld/keys.c 288704 2015-10-05 07:42:05Z mav $");
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "ctld.h"
+
+struct keys *
+keys_new(void)
+{
+ struct keys *keys;
+
+ keys = calloc(sizeof(*keys), 1);
+ if (keys == NULL)
+ log_err(1, "calloc");
+
+ return (keys);
+}
+
+void
+keys_delete(struct keys *keys)
+{
+
+ free(keys->keys_data);
+ free(keys);
+}
+
+void
+keys_load(struct keys *keys, const struct pdu *pdu)
+{
+ int i;
+ char *pair;
+ size_t pair_len;
+
+ if (pdu->pdu_data_len == 0)
+ 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);
+}
Property changes on: trunk/usr.sbin/ctld/keys.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/ctld/log.c
===================================================================
--- trunk/usr.sbin/ctld/log.c (rev 0)
+++ trunk/usr.sbin/ctld/log.c 2018-06-10 20:15:35 UTC (rev 10818)
@@ -0,0 +1,199 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2012 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/10/usr.sbin/ctld/log.c 270888 2014-08-31 20:21:08Z trasz $");
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <vis.h>
+
+#include "ctld.h"
+
+static int log_level = 0;
+static char *peer_name = NULL;
+static char *peer_addr = NULL;
+
+#define MSGBUF_LEN 1024
+
+void
+log_init(int level)
+{
+
+ log_level = level;
+ openlog(getprogname(), LOG_NDELAY | LOG_PID, LOG_DAEMON);
+}
+
+void
+log_set_peer_name(const char *name)
+{
+
+ /*
+ * XXX: Turn it into assertion?
+ */
+ if (peer_name != NULL)
+ log_errx(1, "%s called twice", __func__);
+ if (peer_addr == NULL)
+ log_errx(1, "%s called before log_set_peer_addr", __func__);
+
+ peer_name = checked_strdup(name);
+}
+
+void
+log_set_peer_addr(const char *addr)
+{
+
+ /*
+ * XXX: Turn it into assertion?
+ */
+ if (peer_addr != NULL)
+ log_errx(1, "%s called twice", __func__);
+
+ peer_addr = checked_strdup(addr);
+}
+
+static void
+log_common(int priority, int log_errno, const char *fmt, va_list ap)
+{
+ static char msgbuf[MSGBUF_LEN];
+ static char msgbuf_strvised[MSGBUF_LEN * 4 + 1];
+ int ret;
+
+ ret = vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
+ if (ret < 0) {
+ fprintf(stderr, "%s: snprintf failed", getprogname());
+ syslog(LOG_CRIT, "snprintf failed");
+ exit(1);
+ }
+
+ ret = strnvis(msgbuf_strvised, sizeof(msgbuf_strvised), msgbuf, VIS_NL);
+ if (ret < 0) {
+ fprintf(stderr, "%s: strnvis failed", getprogname());
+ syslog(LOG_CRIT, "strnvis failed");
+ exit(1);
+ }
+
+ if (log_errno == -1) {
+ if (peer_name != NULL) {
+ fprintf(stderr, "%s: %s (%s): %s\n", getprogname(),
+ peer_addr, peer_name, msgbuf_strvised);
+ syslog(priority, "%s (%s): %s",
+ peer_addr, peer_name, msgbuf_strvised);
+ } else if (peer_addr != NULL) {
+ fprintf(stderr, "%s: %s: %s\n", getprogname(),
+ peer_addr, msgbuf_strvised);
+ syslog(priority, "%s: %s",
+ peer_addr, msgbuf_strvised);
+ } else {
+ fprintf(stderr, "%s: %s\n", getprogname(), msgbuf_strvised);
+ syslog(priority, "%s", msgbuf_strvised);
+ }
+
+ } else {
+ if (peer_name != NULL) {
+ fprintf(stderr, "%s: %s (%s): %s: %s\n", getprogname(),
+ peer_addr, peer_name, msgbuf_strvised, strerror(errno));
+ syslog(priority, "%s (%s): %s: %s",
+ peer_addr, peer_name, msgbuf_strvised, strerror(errno));
+ } else if (peer_addr != NULL) {
+ fprintf(stderr, "%s: %s: %s: %s\n", getprogname(),
+ peer_addr, msgbuf_strvised, strerror(errno));
+ syslog(priority, "%s: %s: %s",
+ peer_addr, msgbuf_strvised, strerror(errno));
+ } else {
+ fprintf(stderr, "%s: %s: %s\n", getprogname(),
+ msgbuf_strvised, strerror(errno));
+ syslog(priority, "%s: %s",
+ msgbuf_strvised, strerror(errno));
+ }
+ }
+}
+
+void
+log_err(int eval, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ log_common(LOG_CRIT, errno, fmt, ap);
+ va_end(ap);
+
+ exit(eval);
+}
+
+void
+log_errx(int eval, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ log_common(LOG_CRIT, -1, fmt, ap);
+ va_end(ap);
+
+ exit(eval);
+}
+
+void
+log_warn(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ log_common(LOG_WARNING, errno, fmt, ap);
+ va_end(ap);
+}
+
+void
+log_warnx(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ log_common(LOG_WARNING, -1, fmt, ap);
+ va_end(ap);
+}
+
+void
+log_debugx(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (log_level == 0)
+ return;
+
+ va_start(ap, fmt);
+ log_common(LOG_DEBUG, -1, fmt, ap);
+ va_end(ap);
+}
Property changes on: trunk/usr.sbin/ctld/log.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/ctld/login.c
===================================================================
--- trunk/usr.sbin/ctld/login.c (rev 0)
+++ trunk/usr.sbin/ctld/login.c 2018-06-10 20:15:35 UTC (rev 10818)
@@ -0,0 +1,993 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2012 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/10/usr.sbin/ctld/login.c 300450 2016-05-23 04:50:01Z truckman $");
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netinet/in.h>
+
+#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)
+{
+ 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);
+
+ /*
+ * 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);
+
+ /*
+ * Yay, authentication succeeded!
+ */
+ log_debugx("authentication succeeded for user \"%s\"; "
+ "transitioning to Negotiation Phase", auth->a_user);
+ login_send_chap_success(request, auth);
+ pdu_delete(request);
+
+ /*
+ * 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, tmp;
+ struct connection *conn;
+
+ conn = request->pdu_connection;
+
+ if (strcmp(name, "InitiatorName") == 0) {
+ if (!skipped_security)
+ log_errx(1, "initiator resent InitiatorName");
+ } else if (strcmp(name, "SessionType") == 0) {
+ if (!skipped_security)
+ log_errx(1, "initiator resent SessionType");
+ } else if (strcmp(name, "TargetName") == 0) {
+ if (!skipped_security)
+ log_errx(1, "initiator resent TargetName");
+ } else if (strcmp(name, "InitiatorAlias") == 0) {
+ if (conn->conn_initiator_alias != NULL)
+ free(conn->conn_initiator_alias);
+ conn->conn_initiator_alias = checked_strdup(value);
+ } else if (strcmp(value, "Irrelevant") == 0) {
+ /* Ignore. */
+ } else if (strcmp(name, "HeaderDigest") == 0) {
+ /*
+ * We don't handle digests for discovery sessions.
+ */
+ if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) {
+ log_debugx("discovery session; digests disabled");
+ keys_add(response_keys, name, "None");
+ return;
+ }
+
+ which = login_list_prefers(value, "CRC32C", "None");
+ switch (which) {
+ case 1:
+ log_debugx("initiator prefers CRC32C "
+ "for header digest; we'll use it");
+ conn->conn_header_digest = CONN_DIGEST_CRC32C;
+ keys_add(response_keys, name, "CRC32C");
+ break;
+ case 2:
+ log_debugx("initiator prefers not to do "
+ "header digest; we'll comply");
+ keys_add(response_keys, name, "None");
+ break;
+ default:
+ log_warnx("initiator sent unrecognized "
+ "HeaderDigest value \"%s\"; will use None", value);
+ keys_add(response_keys, name, "None");
+ break;
+ }
+ } else if (strcmp(name, "DataDigest") == 0) {
+ if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) {
+ log_debugx("discovery session; digests disabled");
+ keys_add(response_keys, name, "None");
+ return;
+ }
+
+ which = login_list_prefers(value, "CRC32C", "None");
+ switch (which) {
+ case 1:
+ log_debugx("initiator prefers CRC32C "
+ "for data digest; we'll use it");
+ conn->conn_data_digest = CONN_DIGEST_CRC32C;
+ keys_add(response_keys, name, "CRC32C");
+ break;
+ case 2:
+ log_debugx("initiator prefers not to do "
+ "data digest; we'll comply");
+ keys_add(response_keys, name, "None");
+ break;
+ default:
+ log_warnx("initiator sent unrecognized "
+ "DataDigest value \"%s\"; will use None", value);
+ keys_add(response_keys, name, "None");
+ break;
+ }
+ } else if (strcmp(name, "MaxConnections") == 0) {
+ keys_add(response_keys, name, "1");
+ } else if (strcmp(name, "InitialR2T") == 0) {
+ keys_add(response_keys, name, "Yes");
+ } else if (strcmp(name, "ImmediateData") == 0) {
+ if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) {
+ log_debugx("discovery session; ImmediateData irrelevant");
+ keys_add(response_keys, name, "Irrelevant");
+ } else {
+ if (strcmp(value, "Yes") == 0) {
+ conn->conn_immediate_data = true;
+ keys_add(response_keys, name, "Yes");
+ } else {
+ conn->conn_immediate_data = false;
+ keys_add(response_keys, name, "No");
+ }
+ }
+ } else if (strcmp(name, "MaxRecvDataSegmentLength") == 0) {
+ tmp = strtoul(value, NULL, 10);
+ if (tmp <= 0) {
+ login_send_error(request, 0x02, 0x00);
+ log_errx(1, "received invalid "
+ "MaxRecvDataSegmentLength");
+ }
+ if (tmp > MAX_DATA_SEGMENT_LENGTH) {
+ log_debugx("capping MaxRecvDataSegmentLength "
+ "from %d to %d", tmp, MAX_DATA_SEGMENT_LENGTH);
+ tmp = MAX_DATA_SEGMENT_LENGTH;
+ }
+ conn->conn_max_data_segment_length = tmp;
+ keys_add_int(response_keys, name, MAX_DATA_SEGMENT_LENGTH);
+ } else if (strcmp(name, "MaxBurstLength") == 0) {
+ tmp = strtoul(value, NULL, 10);
+ if (tmp <= 0) {
+ login_send_error(request, 0x02, 0x00);
+ log_errx(1, "received invalid MaxBurstLength");
+ }
+ if (tmp > MAX_BURST_LENGTH) {
+ log_debugx("capping MaxBurstLength from %d to %d",
+ tmp, MAX_BURST_LENGTH);
+ tmp = MAX_BURST_LENGTH;
+ }
+ conn->conn_max_burst_length = tmp;
+ keys_add(response_keys, name, value);
+ } else if (strcmp(name, "FirstBurstLength") == 0) {
+ tmp = strtoul(value, NULL, 10);
+ if (tmp <= 0) {
+ login_send_error(request, 0x02, 0x00);
+ log_errx(1, "received invalid "
+ "FirstBurstLength");
+ }
+ if (tmp > MAX_DATA_SEGMENT_LENGTH) {
+ log_debugx("capping FirstBurstLength from %d to %d",
+ tmp, MAX_DATA_SEGMENT_LENGTH);
+ tmp = MAX_DATA_SEGMENT_LENGTH;
+ }
+ /*
+ * We don't pass the value to the kernel; it only enforces
+ * hardcoded limit anyway.
+ */
+ keys_add_int(response_keys, name, tmp);
+ } else if (strcmp(name, "DefaultTime2Wait") == 0) {
+ keys_add(response_keys, name, value);
+ } else if (strcmp(name, "DefaultTime2Retain") == 0) {
+ keys_add(response_keys, name, "0");
+ } else if (strcmp(name, "MaxOutstandingR2T") == 0) {
+ keys_add(response_keys, name, "1");
+ } else if (strcmp(name, "DataPDUInOrder") == 0) {
+ keys_add(response_keys, name, "Yes");
+ } else if (strcmp(name, "DataSequenceInOrder") == 0) {
+ keys_add(response_keys, name, "Yes");
+ } else if (strcmp(name, "ErrorRecoveryLevel") == 0) {
+ keys_add(response_keys, name, "0");
+ } else if (strcmp(name, "OFMarker") == 0) {
+ keys_add(response_keys, name, "No");
+ } else if (strcmp(name, "IFMarker") == 0) {
+ keys_add(response_keys, name, "No");
+ } else 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)
+{
+ 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)
+{
+ 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)
+{
+ struct pdu *response;
+ struct iscsi_bhs_login_response *bhslr2;
+ struct keys *request_keys, *response_keys;
+ int i;
+ bool redirected, skipped_security;
+
+ if (request == NULL) {
+ log_debugx("beginning operational parameter negotiation; "
+ "waiting for Login PDU");
+ request = login_receive(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 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);
+ }
+
+ 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)
+{
+ struct pdu *request, *response;
+ struct iscsi_bhs_login_request *bhslr;
+
+ log_debugx("waiting for state transition request");
+ request = login_receive(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)
+{
+ 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);
+ 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);
+ }
+}
Property changes on: trunk/usr.sbin/ctld/login.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/ctld/parse.y
===================================================================
--- trunk/usr.sbin/ctld/parse.y (rev 0)
+++ trunk/usr.sbin/ctld/parse.y 2018-06-10 20:15:35 UTC (rev 10818)
@@ -0,0 +1,1148 @@
+/* $MidnightBSD$ */
+%{
+/*-
+ * Copyright (c) 2012 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: stable/10/usr.sbin/ctld/parse.y 311866 2017-01-10 08:25:03Z mav $
+ */
+
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ctld.h"
+
+extern FILE *yyin;
+extern char *yytext;
+extern int lineno;
+
+static struct conf *conf = NULL;
+static struct auth_group *auth_group = NULL;
+static struct portal_group *portal_group = NULL;
+static struct target *target = NULL;
+static struct lun *lun = NULL;
+
+extern void yyerror(const char *);
+extern int yylex(void);
+extern void yyrestart(FILE *);
+
+%}
+
+%token ALIAS AUTH_GROUP AUTH_TYPE BACKEND BLOCKSIZE CHAP CHAP_MUTUAL
+%token CLOSING_BRACKET CTL_LUN DEBUG DEVICE_ID DEVICE_TYPE
+%token DISCOVERY_AUTH_GROUP DISCOVERY_FILTER FOREIGN
+%token INITIATOR_NAME INITIATOR_PORTAL ISNS_SERVER ISNS_PERIOD ISNS_TIMEOUT
+%token LISTEN LISTEN_ISER LUN MAXPROC OPENING_BRACKET OPTION
+%token PATH PIDFILE PORT PORTAL_GROUP REDIRECT SEMICOLON SERIAL SIZE STR
+%token TAG TARGET TIMEOUT
+
+%union
+{
+ char *str;
+}
+
+%token <str> STR
+
+%%
+
+statements:
+ |
+ statements statement
+ |
+ statements statement SEMICOLON
+ ;
+
+statement:
+ debug
+ |
+ timeout
+ |
+ maxproc
+ |
+ pidfile
+ |
+ isns_server
+ |
+ isns_period
+ |
+ isns_timeout
+ |
+ auth_group
+ |
+ portal_group
+ |
+ lun
+ |
+ target
+ ;
+
+debug: DEBUG STR
+ {
+ uint64_t tmp;
+
+ if (expand_number($2, &tmp) != 0) {
+ yyerror("invalid numeric value");
+ free($2);
+ return (1);
+ }
+
+ conf->conf_debug = tmp;
+ }
+ ;
+
+timeout: TIMEOUT STR
+ {
+ uint64_t tmp;
+
+ if (expand_number($2, &tmp) != 0) {
+ yyerror("invalid numeric value");
+ free($2);
+ return (1);
+ }
+
+ conf->conf_timeout = tmp;
+ }
+ ;
+
+maxproc: MAXPROC STR
+ {
+ uint64_t tmp;
+
+ if (expand_number($2, &tmp) != 0) {
+ yyerror("invalid numeric value");
+ free($2);
+ return (1);
+ }
+
+ conf->conf_maxproc = tmp;
+ }
+ ;
+
+pidfile: PIDFILE STR
+ {
+ if (conf->conf_pidfile_path != NULL) {
+ log_warnx("pidfile specified more than once");
+ free($2);
+ return (1);
+ }
+ conf->conf_pidfile_path = $2;
+ }
+ ;
+
+isns_server: ISNS_SERVER STR
+ {
+ int error;
+
+ error = isns_new(conf, $2);
+ free($2);
+ if (error != 0)
+ return (1);
+ }
+ ;
+
+isns_period: ISNS_PERIOD STR
+ {
+ uint64_t tmp;
+
+ if (expand_number($2, &tmp) != 0) {
+ yyerror("invalid numeric value");
+ free($2);
+ return (1);
+ }
+
+ conf->conf_isns_period = tmp;
+ }
+ ;
+
+isns_timeout: ISNS_TIMEOUT STR
+ {
+ uint64_t tmp;
+
+ if (expand_number($2, &tmp) != 0) {
+ yyerror("invalid numeric value");
+ free($2);
+ return (1);
+ }
+
+ conf->conf_isns_timeout = tmp;
+ }
+ ;
+
+auth_group: AUTH_GROUP auth_group_name
+ OPENING_BRACKET auth_group_entries CLOSING_BRACKET
+ {
+ auth_group = NULL;
+ }
+ ;
+
+auth_group_name: STR
+ {
+ /*
+ * Make it possible to redefine default
+ * auth-group. but only once.
+ */
+ if (strcmp($1, "default") == 0 &&
+ conf->conf_default_ag_defined == false) {
+ auth_group = auth_group_find(conf, $1);
+ conf->conf_default_ag_defined = true;
+ } else {
+ auth_group = auth_group_new(conf, $1);
+ }
+ free($1);
+ if (auth_group == NULL)
+ return (1);
+ }
+ ;
+
+auth_group_entries:
+ |
+ auth_group_entries auth_group_entry
+ |
+ auth_group_entries auth_group_entry SEMICOLON
+ ;
+
+auth_group_entry:
+ auth_group_auth_type
+ |
+ auth_group_chap
+ |
+ auth_group_chap_mutual
+ |
+ auth_group_initiator_name
+ |
+ auth_group_initiator_portal
+ ;
+
+auth_group_auth_type: AUTH_TYPE STR
+ {
+ int error;
+
+ error = auth_group_set_type(auth_group, $2);
+ free($2);
+ if (error != 0)
+ return (1);
+ }
+ ;
+
+auth_group_chap: CHAP STR STR
+ {
+ const struct auth *ca;
+
+ ca = auth_new_chap(auth_group, $2, $3);
+ free($2);
+ free($3);
+ if (ca == NULL)
+ return (1);
+ }
+ ;
+
+auth_group_chap_mutual: CHAP_MUTUAL STR STR STR STR
+ {
+ const struct auth *ca;
+
+ ca = auth_new_chap_mutual(auth_group, $2, $3, $4, $5);
+ free($2);
+ free($3);
+ free($4);
+ free($5);
+ if (ca == NULL)
+ return (1);
+ }
+ ;
+
+auth_group_initiator_name: INITIATOR_NAME STR
+ {
+ const struct auth_name *an;
+
+ an = auth_name_new(auth_group, $2);
+ free($2);
+ if (an == NULL)
+ return (1);
+ }
+ ;
+
+auth_group_initiator_portal: INITIATOR_PORTAL STR
+ {
+ const struct auth_portal *ap;
+
+ ap = auth_portal_new(auth_group, $2);
+ free($2);
+ if (ap == NULL)
+ return (1);
+ }
+ ;
+
+portal_group: PORTAL_GROUP portal_group_name
+ OPENING_BRACKET portal_group_entries CLOSING_BRACKET
+ {
+ portal_group = NULL;
+ }
+ ;
+
+portal_group_name: STR
+ {
+ /*
+ * Make it possible to redefine default
+ * portal-group. but only once.
+ */
+ if (strcmp($1, "default") == 0 &&
+ conf->conf_default_pg_defined == false) {
+ portal_group = portal_group_find(conf, $1);
+ conf->conf_default_pg_defined = true;
+ } else {
+ portal_group = portal_group_new(conf, $1);
+ }
+ free($1);
+ if (portal_group == NULL)
+ return (1);
+ }
+ ;
+
+portal_group_entries:
+ |
+ portal_group_entries portal_group_entry
+ |
+ portal_group_entries portal_group_entry SEMICOLON
+ ;
+
+portal_group_entry:
+ portal_group_discovery_auth_group
+ |
+ portal_group_discovery_filter
+ |
+ portal_group_foreign
+ |
+ portal_group_listen
+ |
+ portal_group_listen_iser
+ |
+ portal_group_option
+ |
+ portal_group_redirect
+ |
+ portal_group_tag
+ ;
+
+portal_group_discovery_auth_group: DISCOVERY_AUTH_GROUP STR
+ {
+ if (portal_group->pg_discovery_auth_group != NULL) {
+ log_warnx("discovery-auth-group for portal-group "
+ "\"%s\" specified more than once",
+ portal_group->pg_name);
+ return (1);
+ }
+ portal_group->pg_discovery_auth_group =
+ auth_group_find(conf, $2);
+ if (portal_group->pg_discovery_auth_group == NULL) {
+ log_warnx("unknown discovery-auth-group \"%s\" "
+ "for portal-group \"%s\"",
+ $2, portal_group->pg_name);
+ return (1);
+ }
+ free($2);
+ }
+ ;
+
+portal_group_discovery_filter: DISCOVERY_FILTER STR
+ {
+ int error;
+
+ error = portal_group_set_filter(portal_group, $2);
+ free($2);
+ if (error != 0)
+ return (1);
+ }
+ ;
+
+portal_group_foreign: FOREIGN
+ {
+
+ portal_group->pg_foreign = 1;
+ }
+ ;
+
+portal_group_listen: LISTEN STR
+ {
+ int error;
+
+ error = portal_group_add_listen(portal_group, $2, false);
+ free($2);
+ if (error != 0)
+ return (1);
+ }
+ ;
+
+portal_group_listen_iser: LISTEN_ISER STR
+ {
+ int error;
+
+ error = portal_group_add_listen(portal_group, $2, true);
+ free($2);
+ if (error != 0)
+ return (1);
+ }
+ ;
+
+portal_group_option: OPTION STR STR
+ {
+ struct option *o;
+
+ o = option_new(&portal_group->pg_options, $2, $3);
+ free($2);
+ free($3);
+ if (o == NULL)
+ return (1);
+ }
+ ;
+
+portal_group_redirect: REDIRECT STR
+ {
+ int error;
+
+ error = portal_group_set_redirection(portal_group, $2);
+ free($2);
+ if (error != 0)
+ return (1);
+ }
+ ;
+
+portal_group_tag: TAG STR
+ {
+ uint64_t tmp;
+
+ if (expand_number($2, &tmp) != 0) {
+ yyerror("invalid numeric value");
+ free($2);
+ return (1);
+ }
+
+ portal_group->pg_tag = tmp;
+ }
+ ;
+
+lun: LUN lun_name
+ OPENING_BRACKET lun_entries CLOSING_BRACKET
+ {
+ lun = NULL;
+ }
+ ;
+
+lun_name: STR
+ {
+ lun = lun_new(conf, $1);
+ free($1);
+ if (lun == NULL)
+ return (1);
+ }
+ ;
+
+target: TARGET target_name
+ OPENING_BRACKET target_entries CLOSING_BRACKET
+ {
+ target = NULL;
+ }
+ ;
+
+target_name: STR
+ {
+ target = target_new(conf, $1);
+ free($1);
+ if (target == NULL)
+ return (1);
+ }
+ ;
+
+target_entries:
+ |
+ target_entries target_entry
+ |
+ target_entries target_entry SEMICOLON
+ ;
+
+target_entry:
+ target_alias
+ |
+ target_auth_group
+ |
+ target_auth_type
+ |
+ target_chap
+ |
+ target_chap_mutual
+ |
+ target_initiator_name
+ |
+ target_initiator_portal
+ |
+ target_portal_group
+ |
+ target_port
+ |
+ target_redirect
+ |
+ target_lun
+ |
+ target_lun_ref
+ ;
+
+target_alias: ALIAS STR
+ {
+ if (target->t_alias != NULL) {
+ log_warnx("alias for target \"%s\" "
+ "specified more than once", target->t_name);
+ return (1);
+ }
+ target->t_alias = $2;
+ }
+ ;
+
+target_auth_group: AUTH_GROUP STR
+ {
+ if (target->t_auth_group != NULL) {
+ if (target->t_auth_group->ag_name != NULL)
+ log_warnx("auth-group for target \"%s\" "
+ "specified more than once", target->t_name);
+ else
+ log_warnx("cannot use both auth-group and explicit "
+ "authorisations for target \"%s\"",
+ target->t_name);
+ return (1);
+ }
+ target->t_auth_group = auth_group_find(conf, $2);
+ if (target->t_auth_group == NULL) {
+ log_warnx("unknown auth-group \"%s\" for target "
+ "\"%s\"", $2, target->t_name);
+ return (1);
+ }
+ free($2);
+ }
+ ;
+
+target_auth_type: AUTH_TYPE STR
+ {
+ int error;
+
+ if (target->t_auth_group != NULL) {
+ if (target->t_auth_group->ag_name != NULL) {
+ log_warnx("cannot use both auth-group and "
+ "auth-type for target \"%s\"",
+ target->t_name);
+ return (1);
+ }
+ } else {
+ target->t_auth_group = auth_group_new(conf, NULL);
+ if (target->t_auth_group == NULL) {
+ free($2);
+ return (1);
+ }
+ target->t_auth_group->ag_target = target;
+ }
+ error = auth_group_set_type(target->t_auth_group, $2);
+ free($2);
+ if (error != 0)
+ return (1);
+ }
+ ;
+
+target_chap: CHAP STR STR
+ {
+ const struct auth *ca;
+
+ if (target->t_auth_group != NULL) {
+ if (target->t_auth_group->ag_name != NULL) {
+ log_warnx("cannot use both auth-group and "
+ "chap for target \"%s\"",
+ target->t_name);
+ free($2);
+ free($3);
+ return (1);
+ }
+ } else {
+ target->t_auth_group = auth_group_new(conf, NULL);
+ if (target->t_auth_group == NULL) {
+ free($2);
+ free($3);
+ return (1);
+ }
+ target->t_auth_group->ag_target = target;
+ }
+ ca = auth_new_chap(target->t_auth_group, $2, $3);
+ free($2);
+ free($3);
+ if (ca == NULL)
+ return (1);
+ }
+ ;
+
+target_chap_mutual: CHAP_MUTUAL STR STR STR STR
+ {
+ const struct auth *ca;
+
+ if (target->t_auth_group != NULL) {
+ if (target->t_auth_group->ag_name != NULL) {
+ log_warnx("cannot use both auth-group and "
+ "chap-mutual for target \"%s\"",
+ target->t_name);
+ free($2);
+ free($3);
+ free($4);
+ free($5);
+ return (1);
+ }
+ } else {
+ target->t_auth_group = auth_group_new(conf, NULL);
+ if (target->t_auth_group == NULL) {
+ free($2);
+ free($3);
+ free($4);
+ free($5);
+ return (1);
+ }
+ target->t_auth_group->ag_target = target;
+ }
+ ca = auth_new_chap_mutual(target->t_auth_group,
+ $2, $3, $4, $5);
+ free($2);
+ free($3);
+ free($4);
+ free($5);
+ if (ca == NULL)
+ return (1);
+ }
+ ;
+
+target_initiator_name: INITIATOR_NAME STR
+ {
+ const struct auth_name *an;
+
+ if (target->t_auth_group != NULL) {
+ if (target->t_auth_group->ag_name != NULL) {
+ log_warnx("cannot use both auth-group and "
+ "initiator-name for target \"%s\"",
+ target->t_name);
+ free($2);
+ return (1);
+ }
+ } else {
+ target->t_auth_group = auth_group_new(conf, NULL);
+ if (target->t_auth_group == NULL) {
+ free($2);
+ return (1);
+ }
+ target->t_auth_group->ag_target = target;
+ }
+ an = auth_name_new(target->t_auth_group, $2);
+ free($2);
+ if (an == NULL)
+ return (1);
+ }
+ ;
+
+target_initiator_portal: INITIATOR_PORTAL STR
+ {
+ const struct auth_portal *ap;
+
+ if (target->t_auth_group != NULL) {
+ if (target->t_auth_group->ag_name != NULL) {
+ log_warnx("cannot use both auth-group and "
+ "initiator-portal for target \"%s\"",
+ target->t_name);
+ free($2);
+ return (1);
+ }
+ } else {
+ target->t_auth_group = auth_group_new(conf, NULL);
+ if (target->t_auth_group == NULL) {
+ free($2);
+ return (1);
+ }
+ target->t_auth_group->ag_target = target;
+ }
+ ap = auth_portal_new(target->t_auth_group, $2);
+ free($2);
+ if (ap == NULL)
+ return (1);
+ }
+ ;
+
+target_portal_group: PORTAL_GROUP STR STR
+ {
+ struct portal_group *tpg;
+ struct auth_group *tag;
+ struct port *tp;
+
+ tpg = portal_group_find(conf, $2);
+ if (tpg == NULL) {
+ log_warnx("unknown portal-group \"%s\" for target "
+ "\"%s\"", $2, target->t_name);
+ free($2);
+ free($3);
+ return (1);
+ }
+ tag = auth_group_find(conf, $3);
+ if (tag == NULL) {
+ log_warnx("unknown auth-group \"%s\" for target "
+ "\"%s\"", $3, target->t_name);
+ free($2);
+ free($3);
+ return (1);
+ }
+ tp = port_new(conf, target, tpg);
+ if (tp == NULL) {
+ log_warnx("can't link portal-group \"%s\" to target "
+ "\"%s\"", $2, target->t_name);
+ free($2);
+ return (1);
+ }
+ tp->p_auth_group = tag;
+ free($2);
+ free($3);
+ }
+ | PORTAL_GROUP STR
+ {
+ struct portal_group *tpg;
+ struct port *tp;
+
+ tpg = portal_group_find(conf, $2);
+ if (tpg == NULL) {
+ log_warnx("unknown portal-group \"%s\" for target "
+ "\"%s\"", $2, target->t_name);
+ free($2);
+ return (1);
+ }
+ tp = port_new(conf, target, tpg);
+ if (tp == NULL) {
+ log_warnx("can't link portal-group \"%s\" to target "
+ "\"%s\"", $2, target->t_name);
+ free($2);
+ return (1);
+ }
+ free($2);
+ }
+ ;
+
+target_port: PORT STR
+ {
+ struct pport *pp;
+ struct port *tp;
+
+ pp = pport_find(conf, $2);
+ if (pp == NULL) {
+ log_warnx("unknown port \"%s\" for target \"%s\"",
+ $2, target->t_name);
+ free($2);
+ return (1);
+ }
+ if (!TAILQ_EMPTY(&pp->pp_ports)) {
+ log_warnx("can't link port \"%s\" to target \"%s\", "
+ "port already linked to some target",
+ $2, target->t_name);
+ free($2);
+ return (1);
+ }
+ tp = port_new_pp(conf, target, pp);
+ if (tp == NULL) {
+ log_warnx("can't link port \"%s\" to target \"%s\"",
+ $2, target->t_name);
+ free($2);
+ return (1);
+ }
+ free($2);
+ }
+ ;
+
+target_redirect: REDIRECT STR
+ {
+ int error;
+
+ error = target_set_redirection(target, $2);
+ free($2);
+ if (error != 0)
+ return (1);
+ }
+ ;
+
+target_lun: LUN lun_number
+ OPENING_BRACKET lun_entries CLOSING_BRACKET
+ {
+ lun = NULL;
+ }
+ ;
+
+lun_number: STR
+ {
+ uint64_t tmp;
+ int ret;
+ char *name;
+
+ if (expand_number($1, &tmp) != 0) {
+ yyerror("invalid numeric value");
+ free($1);
+ return (1);
+ }
+ if (tmp >= MAX_LUNS) {
+ yyerror("LU number is too big");
+ free($1);
+ return (1);
+ }
+
+ ret = asprintf(&name, "%s,lun,%ju", target->t_name, tmp);
+ if (ret <= 0)
+ log_err(1, "asprintf");
+ lun = lun_new(conf, name);
+ if (lun == NULL)
+ return (1);
+
+ lun_set_scsiname(lun, name);
+ target->t_luns[tmp] = lun;
+ }
+ ;
+
+target_lun_ref: LUN STR STR
+ {
+ uint64_t tmp;
+
+ if (expand_number($2, &tmp) != 0) {
+ yyerror("invalid numeric value");
+ free($2);
+ free($3);
+ return (1);
+ }
+ free($2);
+ if (tmp >= MAX_LUNS) {
+ yyerror("LU number is too big");
+ free($3);
+ return (1);
+ }
+
+ lun = lun_find(conf, $3);
+ free($3);
+ if (lun == NULL)
+ return (1);
+
+ target->t_luns[tmp] = lun;
+ }
+ ;
+
+lun_entries:
+ |
+ lun_entries lun_entry
+ |
+ lun_entries lun_entry SEMICOLON
+ ;
+
+lun_entry:
+ lun_backend
+ |
+ lun_blocksize
+ |
+ lun_device_id
+ |
+ lun_device_type
+ |
+ lun_ctl_lun
+ |
+ lun_option
+ |
+ lun_path
+ |
+ lun_serial
+ |
+ lun_size
+ ;
+
+lun_backend: BACKEND STR
+ {
+ if (lun->l_backend != NULL) {
+ log_warnx("backend for lun \"%s\" "
+ "specified more than once",
+ lun->l_name);
+ free($2);
+ return (1);
+ }
+ lun_set_backend(lun, $2);
+ free($2);
+ }
+ ;
+
+lun_blocksize: BLOCKSIZE STR
+ {
+ uint64_t tmp;
+
+ if (expand_number($2, &tmp) != 0) {
+ yyerror("invalid numeric value");
+ free($2);
+ return (1);
+ }
+
+ if (lun->l_blocksize != 0) {
+ log_warnx("blocksize for lun \"%s\" "
+ "specified more than once",
+ lun->l_name);
+ return (1);
+ }
+ lun_set_blocksize(lun, tmp);
+ }
+ ;
+
+lun_device_id: DEVICE_ID STR
+ {
+ if (lun->l_device_id != NULL) {
+ log_warnx("device_id for lun \"%s\" "
+ "specified more than once",
+ lun->l_name);
+ free($2);
+ return (1);
+ }
+ lun_set_device_id(lun, $2);
+ free($2);
+ }
+ ;
+
+lun_device_type: DEVICE_TYPE STR
+ {
+ uint64_t tmp;
+
+ if (strcasecmp($2, "disk") == 0 ||
+ strcasecmp($2, "direct") == 0)
+ tmp = 0;
+ else if (strcasecmp($2, "processor") == 0)
+ tmp = 3;
+ else if (strcasecmp($2, "cd") == 0 ||
+ strcasecmp($2, "cdrom") == 0 ||
+ strcasecmp($2, "dvd") == 0 ||
+ strcasecmp($2, "dvdrom") == 0)
+ tmp = 5;
+ else if (expand_number($2, &tmp) != 0 ||
+ tmp > 15) {
+ yyerror("invalid numeric value");
+ free($2);
+ return (1);
+ }
+
+ lun_set_device_type(lun, tmp);
+ }
+ ;
+
+lun_ctl_lun: CTL_LUN STR
+ {
+ uint64_t tmp;
+
+ if (expand_number($2, &tmp) != 0) {
+ yyerror("invalid numeric value");
+ free($2);
+ return (1);
+ }
+
+ if (lun->l_ctl_lun >= 0) {
+ log_warnx("ctl_lun for lun \"%s\" "
+ "specified more than once",
+ lun->l_name);
+ return (1);
+ }
+ lun_set_ctl_lun(lun, tmp);
+ }
+ ;
+
+lun_option: OPTION STR STR
+ {
+ struct option *o;
+
+ o = option_new(&lun->l_options, $2, $3);
+ free($2);
+ free($3);
+ if (o == NULL)
+ return (1);
+ }
+ ;
+
+lun_path: PATH STR
+ {
+ if (lun->l_path != NULL) {
+ log_warnx("path for lun \"%s\" "
+ "specified more than once",
+ lun->l_name);
+ free($2);
+ return (1);
+ }
+ lun_set_path(lun, $2);
+ free($2);
+ }
+ ;
+
+lun_serial: SERIAL STR
+ {
+ if (lun->l_serial != NULL) {
+ log_warnx("serial for lun \"%s\" "
+ "specified more than once",
+ lun->l_name);
+ free($2);
+ return (1);
+ }
+ lun_set_serial(lun, $2);
+ free($2);
+ }
+ ;
+
+lun_size: SIZE STR
+ {
+ uint64_t tmp;
+
+ if (expand_number($2, &tmp) != 0) {
+ yyerror("invalid numeric value");
+ free($2);
+ return (1);
+ }
+
+ if (lun->l_size != 0) {
+ log_warnx("size for lun \"%s\" "
+ "specified more than once",
+ lun->l_name);
+ return (1);
+ }
+ lun_set_size(lun, tmp);
+ }
+ ;
+%%
+
+void
+yyerror(const char *str)
+{
+
+ log_warnx("error in configuration file at line %d near '%s': %s",
+ lineno, yytext, str);
+}
+
+static void
+check_perms(const char *path)
+{
+ struct stat sb;
+ int error;
+
+ error = stat(path, &sb);
+ if (error != 0) {
+ log_warn("stat");
+ return;
+ }
+ if (sb.st_mode & S_IWOTH) {
+ log_warnx("%s is world-writable", path);
+ } else if (sb.st_mode & S_IROTH) {
+ log_warnx("%s is world-readable", path);
+ } else if (sb.st_mode & S_IXOTH) {
+ /*
+ * Ok, this one doesn't matter, but still do it,
+ * just for consistency.
+ */
+ log_warnx("%s is world-executable", path);
+ }
+
+ /*
+ * XXX: Should we also check for owner != 0?
+ */
+}
+
+struct conf *
+conf_new_from_file(const char *path, struct conf *oldconf)
+{
+ 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);
+
+ yyin = fopen(path, "r");
+ if (yyin == NULL) {
+ log_warn("unable to open configuration file %s", path);
+ conf_delete(conf);
+ return (NULL);
+ }
+ check_perms(path);
+ lineno = 1;
+ yyrestart(yyin);
+ error = yyparse();
+ auth_group = NULL;
+ portal_group = NULL;
+ target = NULL;
+ lun = NULL;
+ fclose(yyin);
+ if (error != 0) {
+ conf_delete(conf);
+ return (NULL);
+ }
+
+ 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);
+}
Property changes on: trunk/usr.sbin/ctld/parse.y
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: trunk/usr.sbin/ctld/pdu.c
===================================================================
--- trunk/usr.sbin/ctld/pdu.c (rev 0)
+++ trunk/usr.sbin/ctld/pdu.c 2018-06-10 20:15:35 UTC (rev 10818)
@@ -0,0 +1,265 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2012 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/10/usr.sbin/ctld/pdu.c 288704 2015-10-05 07:42:05Z mav $");
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "ctld.h"
+#include "iscsi_proto.h"
+
+#ifdef ICL_KERNEL_PROXY
+#include <sys/ioctl.h>
+#endif
+
+extern bool proxy_mode;
+
+static int
+pdu_ahs_length(const struct pdu *pdu)
+{
+
+ return (pdu->pdu_bhs->bhs_total_ahs_len * 4);
+}
+
+static int
+pdu_data_segment_length(const struct pdu *pdu)
+{
+ uint32_t len = 0;
+
+ len += pdu->pdu_bhs->bhs_data_segment_len[0];
+ len <<= 8;
+ len += pdu->pdu_bhs->bhs_data_segment_len[1];
+ len <<= 8;
+ len += pdu->pdu_bhs->bhs_data_segment_len[2];
+
+ return (len);
+}
+
+static void
+pdu_set_data_segment_length(struct pdu *pdu, uint32_t len)
+{
+
+ pdu->pdu_bhs->bhs_data_segment_len[2] = len;
+ pdu->pdu_bhs->bhs_data_segment_len[1] = len >> 8;
+ pdu->pdu_bhs->bhs_data_segment_len[0] = len >> 16;
+}
+
+struct pdu *
+pdu_new(struct connection *conn)
+{
+ struct pdu *pdu;
+
+ pdu = calloc(sizeof(*pdu), 1);
+ if (pdu == NULL)
+ log_err(1, "calloc");
+
+ pdu->pdu_bhs = calloc(sizeof(*pdu->pdu_bhs), 1);
+ if (pdu->pdu_bhs == NULL)
+ log_err(1, "calloc");
+
+ pdu->pdu_connection = conn;
+
+ return (pdu);
+}
+
+struct pdu *
+pdu_new_response(struct pdu *request)
+{
+
+ return (pdu_new(request->pdu_connection));
+}
+
+#ifdef ICL_KERNEL_PROXY
+
+static void
+pdu_receive_proxy(struct pdu *pdu)
+{
+ size_t len;
+
+ assert(proxy_mode);
+
+ kernel_receive(pdu);
+
+ len = pdu_ahs_length(pdu);
+ if (len > 0)
+ log_errx(1, "protocol error: non-empty AHS");
+
+ len = pdu_data_segment_length(pdu);
+ assert(len <= MAX_DATA_SEGMENT_LENGTH);
+ pdu->pdu_data_len = len;
+}
+
+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)
+{
+ ssize_t ret;
+
+ while (len > 0) {
+ ret = read(fd, data, len);
+ if (ret < 0) {
+ if (timed_out())
+ log_errx(1, "exiting due to timeout");
+ log_err(1, "read");
+ } else if (ret == 0)
+ log_errx(1, "read: connection lost");
+ len -= ret;
+ data += ret;
+ }
+}
+
+void
+pdu_receive(struct pdu *pdu)
+{
+ size_t len, padding;
+ char dummy[4];
+
+#ifdef ICL_KERNEL_PROXY
+ if (proxy_mode)
+ return (pdu_receive_proxy(pdu));
+#endif
+
+ assert(proxy_mode == false);
+
+ pdu_read(pdu->pdu_connection->conn_socket,
+ (char *)pdu->pdu_bhs, sizeof(*pdu->pdu_bhs));
+
+ len = pdu_ahs_length(pdu);
+ if (len > 0)
+ log_errx(1, "protocol error: non-empty AHS");
+
+ len = pdu_data_segment_length(pdu);
+ if (len > 0) {
+ if (len > MAX_DATA_SEGMENT_LENGTH) {
+ log_errx(1, "protocol error: received PDU "
+ "with DataSegmentLength exceeding %d",
+ MAX_DATA_SEGMENT_LENGTH);
+ }
+
+ pdu->pdu_data_len = len;
+ pdu->pdu_data = malloc(len);
+ if (pdu->pdu_data == NULL)
+ log_err(1, "malloc");
+
+ pdu_read(pdu->pdu_connection->conn_socket,
+ (char *)pdu->pdu_data, pdu->pdu_data_len);
+
+ padding = pdu_padding(pdu);
+ if (padding != 0) {
+ assert(padding < sizeof(dummy));
+ pdu_read(pdu->pdu_connection->conn_socket,
+ (char *)dummy, padding);
+ }
+ }
+}
+
+void
+pdu_send(struct pdu *pdu)
+{
+ ssize_t ret, total_len;
+ size_t padding;
+ uint32_t zero = 0;
+ struct iovec iov[3];
+ int iovcnt;
+
+#ifdef ICL_KERNEL_PROXY
+ if (proxy_mode)
+ return (pdu_send_proxy(pdu));
+#endif
+
+ assert(proxy_mode == false);
+
+ pdu_set_data_segment_length(pdu, pdu->pdu_data_len);
+ iov[0].iov_base = pdu->pdu_bhs;
+ iov[0].iov_len = sizeof(*pdu->pdu_bhs);
+ total_len = iov[0].iov_len;
+ iovcnt = 1;
+
+ if (pdu->pdu_data_len > 0) {
+ iov[1].iov_base = pdu->pdu_data;
+ iov[1].iov_len = pdu->pdu_data_len;
+ total_len += iov[1].iov_len;
+ iovcnt = 2;
+
+ padding = pdu_padding(pdu);
+ if (padding > 0) {
+ assert(padding < sizeof(zero));
+ iov[2].iov_base = &zero;
+ iov[2].iov_len = padding;
+ total_len += iov[2].iov_len;
+ iovcnt = 3;
+ }
+ }
+
+ ret = writev(pdu->pdu_connection->conn_socket, iov, iovcnt);
+ if (ret < 0) {
+ if (timed_out())
+ log_errx(1, "exiting due to timeout");
+ log_err(1, "writev");
+ }
+ if (ret != total_len)
+ log_errx(1, "short write");
+}
+
+void
+pdu_delete(struct pdu *pdu)
+{
+
+ free(pdu->pdu_data);
+ free(pdu->pdu_bhs);
+ free(pdu);
+}
Property changes on: trunk/usr.sbin/ctld/pdu.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/ctld/token.l
===================================================================
--- trunk/usr.sbin/ctld/token.l (rev 0)
+++ trunk/usr.sbin/ctld/token.l 2018-06-10 20:15:35 UTC (rev 10818)
@@ -0,0 +1,96 @@
+/* $MidnightBSD$ */
+%{
+/*-
+ * Copyright (c) 2012 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: stable/10/usr.sbin/ctld/token.l 288810 2015-10-05 11:30:18Z mav $
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "y.tab.h"
+
+int lineno;
+
+#define YY_DECL int yylex(void)
+extern int yylex(void);
+
+%}
+
+%option noinput
+%option nounput
+
+%%
+alias { return ALIAS; }
+auth-group { return AUTH_GROUP; }
+auth-type { return AUTH_TYPE; }
+backend { return BACKEND; }
+blocksize { return BLOCKSIZE; }
+chap { return CHAP; }
+chap-mutual { return CHAP_MUTUAL; }
+ctl-lun { return CTL_LUN; }
+debug { return DEBUG; }
+device-id { return DEVICE_ID; }
+device-type { return DEVICE_TYPE; }
+discovery-auth-group { return DISCOVERY_AUTH_GROUP; }
+discovery-filter { return DISCOVERY_FILTER; }
+foreign { return FOREIGN; }
+initiator-name { return INITIATOR_NAME; }
+initiator-portal { return INITIATOR_PORTAL; }
+listen { return LISTEN; }
+listen-iser { return LISTEN_ISER; }
+lun { return LUN; }
+maxproc { return MAXPROC; }
+option { return OPTION; }
+path { return PATH; }
+pidfile { return PIDFILE; }
+isns-server { return ISNS_SERVER; }
+isns-period { return ISNS_PERIOD; }
+isns-timeout { return ISNS_TIMEOUT; }
+port { return PORT; }
+portal-group { return PORTAL_GROUP; }
+redirect { return REDIRECT; }
+serial { return SERIAL; }
+size { return SIZE; }
+tag { return TAG; }
+target { return TARGET; }
+timeout { return TIMEOUT; }
+\"[^"]+\" { yylval.str = strndup(yytext + 1,
+ strlen(yytext) - 2); return STR; }
+[a-zA-Z0-9\.\- at _/\:\[\]]+ { yylval.str = strdup(yytext); return STR; }
+\{ { return OPENING_BRACKET; }
+\} { return CLOSING_BRACKET; }
+#.*$ /* ignore comments */;
+\r\n { lineno++; }
+\n { lineno++; }
+; { return SEMICOLON; }
+[ \t]+ /* ignore whitespace */;
+. { yylval.str = strdup(yytext); return STR; }
+%%
Property changes on: trunk/usr.sbin/ctld/token.l
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
More information about the Midnightbsd-cvs
mailing list