summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--usr.sbin/ctld/Makefile3
-rw-r--r--usr.sbin/ctld/ctl.conf.511
-rw-r--r--usr.sbin/ctld/ctld.c393
-rw-r--r--usr.sbin/ctld/ctld.h17
-rw-r--r--usr.sbin/ctld/isns.c258
-rw-r--r--usr.sbin/ctld/isns.h92
-rw-r--r--usr.sbin/ctld/parse.y30
-rw-r--r--usr.sbin/ctld/token.l3
8 files changed, 761 insertions, 46 deletions
diff --git a/usr.sbin/ctld/Makefile b/usr.sbin/ctld/Makefile
index 09b89cb..38ee0e8 100644
--- a/usr.sbin/ctld/Makefile
+++ b/usr.sbin/ctld/Makefile
@@ -1,7 +1,8 @@
# $FreeBSD$
PROG= ctld
-SRCS= chap.c ctld.c discovery.c kernel.c keys.c log.c login.c parse.y pdu.c token.l y.tab.h
+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
diff --git a/usr.sbin/ctld/ctl.conf.5 b/usr.sbin/ctld/ctl.conf.5
index 9cc6ee4..b8980b1 100644
--- a/usr.sbin/ctld/ctl.conf.5
+++ b/usr.sbin/ctld/ctl.conf.5
@@ -27,7 +27,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd October 22, 2014
+.Dd October 28, 2014
.Dt CTL.CONF 5
.Os
.Sh NAME
@@ -106,6 +106,15 @@ 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
diff --git a/usr.sbin/ctld/ctld.c b/usr.sbin/ctld/ctld.c
index c8ca4a9..bc6e640 100644
--- a/usr.sbin/ctld/ctld.c
+++ b/usr.sbin/ctld/ctld.c
@@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$");
#include <unistd.h>
#include "ctld.h"
+#include "isns.h"
bool proxy_mode = false;
@@ -89,7 +90,10 @@ conf_new(void)
TAILQ_INIT(&conf->conf_targets);
TAILQ_INIT(&conf->conf_auth_groups);
TAILQ_INIT(&conf->conf_portal_groups);
+ 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;
@@ -103,6 +107,7 @@ conf_delete(struct conf *conf)
struct target *targ, *tmp;
struct auth_group *ag, *cagtmp;
struct portal_group *pg, *cpgtmp;
+ struct isns *is, *istmp;
assert(conf->conf_pidfh == NULL);
@@ -112,6 +117,8 @@ conf_delete(struct conf *conf)
auth_group_delete(ag);
TAILQ_FOREACH_SAFE(pg, &conf->conf_portal_groups, pg_next, cpgtmp)
portal_group_delete(pg);
+ TAILQ_FOREACH_SAFE(is, &conf->conf_isns, i_next, istmp)
+ isns_delete(is);
free(conf->conf_pidfile_path);
free(conf);
}
@@ -619,47 +626,28 @@ portal_group_find(const struct conf *conf, const char *name)
return (NULL);
}
-int
-portal_group_add_listen(struct portal_group *pg, const char *value, bool iser)
+static int
+parse_addr_port(char *arg, const char *def_port, struct addrinfo **ai)
{
struct addrinfo hints;
- struct portal *portal;
- char *addr, *ch, *arg;
+ char *addr, *ch;
const char *port;
int error, colons = 0;
- portal = portal_new(pg);
- portal->p_listen = checked_strdup(value);
- portal->p_iser = iser;
-
- arg = portal->p_listen;
- if (arg[0] == '\0') {
- log_warnx("empty listen address");
- portal_delete(portal);
- return (1);
- }
if (arg[0] == '[') {
/*
* IPv6 address in square brackets, perhaps with port.
*/
arg++;
addr = strsep(&arg, "]");
- if (arg == NULL) {
- log_warnx("invalid listen address %s",
- portal->p_listen);
- portal_delete(portal);
+ if (arg == NULL)
return (1);
- }
if (arg[0] == '\0') {
- port = "3260";
+ port = def_port;
} else if (arg[0] == ':') {
port = arg + 1;
- } else {
- log_warnx("invalid listen address %s",
- portal->p_listen);
- portal_delete(portal);
+ } else
return (1);
- }
} else {
/*
* Either IPv6 address without brackets - and without
@@ -671,11 +659,11 @@ portal_group_add_listen(struct portal_group *pg, const char *value, bool iser)
}
if (colons > 1) {
addr = arg;
- port = "3260";
+ port = def_port;
} else {
addr = strsep(&arg, ":");
if (arg == NULL)
- port = "3260";
+ port = def_port;
else
port = arg;
}
@@ -685,11 +673,23 @@ portal_group_add_listen(struct portal_group *pg, const char *value, bool iser)
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
+ error = getaddrinfo(addr, port, &hints, ai);
+ if (error != 0)
+ return (1);
+ return (0);
+}
- error = getaddrinfo(addr, port, &hints, &portal->p_ai);
- if (error != 0) {
- log_warnx("getaddrinfo for %s failed: %s",
- portal->p_listen, gai_strerror(error));
+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);
}
@@ -702,6 +702,258 @@ portal_group_add_listen(struct portal_group *pg, const char *value, bool iser)
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 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);
+ pg = target->t_portal_group;
+ 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, 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));
+
+ if (oldisns == NULL || TAILQ_EMPTY(&oldisns->i_conf->conf_targets))
+ oldisns = isns;
+ res = isns_do_deregister(oldisns, s, hostname);
+ res = 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) {
+ res = isns_do_deregister(isns, s, hostname);
+ res = 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, 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)
+ return;
+ gethostname(hostname, sizeof(hostname));
+
+ res = isns_do_deregister(isns, s, hostname);
+ close(s);
+ set_timeout(0, false);
+}
+
static bool
valid_hex(const char ch)
{
@@ -1251,6 +1503,7 @@ conf_apply(struct conf *oldconf, struct conf *newconf)
struct lun *oldlun, *newlun, *tmplun;
struct portal_group *oldpg, *newpg;
struct portal *oldp, *newp;
+ struct isns *oldns, *newns;
pid_t otherpid;
int changed, cumulated_error = 0, error;
int one = 1;
@@ -1288,6 +1541,16 @@ conf_apply(struct conf *oldconf, struct conf *newconf)
}
}
+ /* 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
@@ -1317,10 +1580,8 @@ conf_apply(struct conf *oldconf, struct conf *newconf)
oldlun->l_ctl_lun);
cumulated_error++;
}
- lun_delete(oldlun);
}
kernel_port_remove(oldtarg);
- target_delete(oldtarg);
continue;
}
@@ -1343,7 +1604,6 @@ conf_apply(struct conf *oldconf, struct conf *newconf)
oldlun->l_ctl_lun);
cumulated_error++;
}
- lun_delete(oldlun);
continue;
}
@@ -1569,6 +1829,19 @@ conf_apply(struct conf *oldconf, struct conf *newconf)
}
}
+ /* (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);
}
@@ -1580,7 +1853,7 @@ timed_out(void)
}
static void
-sigalrm_handler(int dummy __unused)
+sigalrm_handler_fatal(int dummy __unused)
{
/*
* It would be easiest to just log an error and exit. We can't
@@ -1600,19 +1873,35 @@ sigalrm_handler(int dummy __unused)
}
static void
-set_timeout(const struct conf *conf)
+sigalrm_handler(int dummy __unused)
+{
+
+ sigalrm_received = true;
+}
+
+void
+set_timeout(int timeout, int fatal)
{
struct sigaction sa;
struct itimerval itv;
int error;
- if (conf->conf_timeout <= 0) {
+ 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));
- sa.sa_handler = sigalrm_handler;
+ 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)
@@ -1622,12 +1911,10 @@ set_timeout(const struct conf *conf)
* 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 = conf->conf_timeout;
-
- log_debugx("setting session timeout to %d seconds",
- conf->conf_timeout);
+ itv.it_value.tv_sec = timeout;
error = setitimer(ITIMER_REAL, &itv, NULL);
if (error != 0)
log_err(1, "setitimer");
@@ -1713,7 +2000,7 @@ handle_connection(struct portal *portal, int fd,
setproctitle("%s", host);
conn = connection_new(portal, fd, host, client_sa);
- set_timeout(conf);
+ set_timeout(conf->conf_timeout, true);
kernel_capsicate();
login(conn);
if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) {
@@ -1760,7 +2047,7 @@ main_loop(struct conf *conf, bool dont_fork)
pidfile_write(conf->conf_pidfh);
for (;;) {
- if (sighup_received || sigterm_received)
+ if (sighup_received || sigterm_received || timed_out())
return;
#ifdef ICL_KERNEL_PROXY
@@ -1884,6 +2171,7 @@ 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;
@@ -1943,6 +2231,10 @@ main(int argc, char **argv)
}
}
+ /* 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) {
@@ -1978,12 +2270,25 @@ main(int argc, char **argv)
error = conf_apply(oldconf, newconf);
if (error != 0)
log_warnx("failed to apply configuration");
+ conf_delete(oldconf);
+ oldconf = NULL;
log_warnx("exiting on signal");
exit(0);
} else {
nchildren -= wait_for_children(false);
assert(nchildren >= 0);
+ if (timed_out()) {
+ set_timeout(0, false);
+ TAILQ_FOREACH(newns, &newconf->conf_isns, i_next)
+ isns_check(newns);
+ /* Schedule iSNS update */
+ if (!TAILQ_EMPTY(&newconf->conf_isns)) {
+ set_timeout((newconf->conf_isns_period
+ + 2) / 3,
+ false);
+ }
+ }
}
}
/* NOTREACHED */
diff --git a/usr.sbin/ctld/ctld.h b/usr.sbin/ctld/ctld.h
index e0eb913..2d268a0 100644
--- a/usr.sbin/ctld/ctld.h
+++ b/usr.sbin/ctld/ctld.h
@@ -146,11 +146,21 @@ struct target {
char *t_alias;
};
+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(, target) conf_targets;
TAILQ_HEAD(, auth_group) conf_auth_groups;
TAILQ_HEAD(, portal_group) conf_portal_groups;
+ TAILQ_HEAD(, isns) conf_isns;
+ int conf_isns_period;
+ int conf_isns_timeout;
int conf_debug;
int conf_timeout;
int conf_maxproc;
@@ -277,6 +287,12 @@ struct portal_group *portal_group_find(const struct conf *conf,
int portal_group_add_listen(struct portal_group *pg,
const char *listen, bool iser);
+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 target *target_new(struct conf *conf, const char *name);
void target_delete(struct target *target);
struct target *target_find(struct conf *conf,
@@ -354,6 +370,7 @@ 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 */
diff --git a/usr.sbin/ctld/isns.c b/usr.sbin/ctld/isns.c
new file mode 100644
index 0000000..11eef3e0
--- /dev/null
+++ b/usr.sbin/ctld/isns.c
@@ -0,0 +1,258 @@
+/*-
+ * Copyright (c) 2014 Alexander Motin <mav@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$");
+
+#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 <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"
+
+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)]));
+}
diff --git a/usr.sbin/ctld/isns.h b/usr.sbin/ctld/isns.h
new file mode 100644
index 0000000..00e6b50
--- /dev/null
+++ b/usr.sbin/ctld/isns.h
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 2014 Alexander Motin <mav@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$
+ */
+
+#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 */
diff --git a/usr.sbin/ctld/parse.y b/usr.sbin/ctld/parse.y
index 3acd480..06ab7de 100644
--- a/usr.sbin/ctld/parse.y
+++ b/usr.sbin/ctld/parse.y
@@ -61,6 +61,7 @@ extern void yyrestart(FILE *);
%token CLOSING_BRACKET DEBUG DEVICE_ID DISCOVERY_AUTH_GROUP INITIATOR_NAME
%token INITIATOR_PORTAL LISTEN LISTEN_ISER LUN MAXPROC NUM OPENING_BRACKET
%token OPTION PATH PIDFILE PORTAL_GROUP SERIAL SIZE STR TARGET TIMEOUT
+%token ISNS_SERVER ISNS_PERIOD ISNS_TIMEOUT
%union
{
@@ -87,6 +88,12 @@ statement:
|
pidfile
|
+ isns_server
+ |
+ isns_period
+ |
+ isns_timeout
+ |
auth_group
|
portal_group
@@ -123,6 +130,29 @@ pidfile: PIDFILE STR
}
;
+isns_server: ISNS_SERVER STR
+ {
+ int error;
+
+ error = isns_new(conf, $2);
+ free($2);
+ if (error != 0)
+ return (1);
+ }
+ ;
+
+isns_period: ISNS_PERIOD NUM
+ {
+ conf->conf_isns_period = $2;
+ }
+ ;
+
+isns_timeout: ISNS_TIMEOUT NUM
+ {
+ conf->conf_isns_timeout = $2;
+ }
+ ;
+
auth_group: AUTH_GROUP auth_group_name
OPENING_BRACKET auth_group_entries CLOSING_BRACKET
{
diff --git a/usr.sbin/ctld/token.l b/usr.sbin/ctld/token.l
index a47bf9a..4522fea 100644
--- a/usr.sbin/ctld/token.l
+++ b/usr.sbin/ctld/token.l
@@ -67,6 +67,9 @@ 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; }
portal-group { return PORTAL_GROUP; }
serial { return SERIAL; }
size { return SIZE; }
OpenPOWER on IntegriCloud