Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 25 Oct 2014 12:50:26 +0000 (UTC)
From:      Alexander Motin <mav@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r273635 - head/usr.sbin/ctld
Message-ID:  <201410251250.s9PCoQaL089386@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mav
Date: Sat Oct 25 12:50:26 2014
New Revision: 273635
URL: https://svnweb.freebsd.org/changeset/base/273635

Log:
  Add basic iSNS client to the iSCSI target.
  
  This makes ctld(8) register its iSCSI targets and portals on configured
  iSNS servers to allow initiators find them without active discovery.
  
  Fetching of allowed initiators from iSNS is not implemented now, so target
  ACLs still should be configured manually.
  
  Reviewed by:	trasz@
  MFC after:	1 month
  Sponsored by:	iXsystems, Inc.

Added:
  head/usr.sbin/ctld/isns.c   (contents, props changed)
  head/usr.sbin/ctld/isns.h   (contents, props changed)
Modified:
  head/usr.sbin/ctld/Makefile
  head/usr.sbin/ctld/ctl.conf.5
  head/usr.sbin/ctld/ctld.c
  head/usr.sbin/ctld/ctld.h
  head/usr.sbin/ctld/parse.y
  head/usr.sbin/ctld/token.l

Modified: head/usr.sbin/ctld/Makefile
==============================================================================
--- head/usr.sbin/ctld/Makefile	Sat Oct 25 09:25:29 2014	(r273634)
+++ head/usr.sbin/ctld/Makefile	Sat Oct 25 12:50:26 2014	(r273635)
@@ -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

Modified: head/usr.sbin/ctld/ctl.conf.5
==============================================================================
--- head/usr.sbin/ctld/ctl.conf.5	Sat Oct 25 09:25:29 2014	(r273634)
+++ head/usr.sbin/ctld/ctl.conf.5	Sat Oct 25 12:50:26 2014	(r273635)
@@ -27,7 +27,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd October 22, 2014
+.Dd October 23, 2014
 .Dt CTL.CONF 5
 .Os
 .Sh NAME
@@ -106,6 +106,13 @@ The timeout for login sessions, after wh
 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.
+.It Ic isns-timeout Ar seconds
+Timeout for iSNS requests.
 .El
 .Ss auth-group Context
 .Bl -tag -width indent

Modified: head/usr.sbin/ctld/ctld.c
==============================================================================
--- head/usr.sbin/ctld/ctld.c	Sat Oct 25 09:25:29 2014	(r273634)
+++ head/usr.sbin/ctld/ctld.c	Sat Oct 25 12:50:26 2014	(r273635)
@@ -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);
 }
@@ -644,47 +651,28 @@ portal_group_find(const struct conf *con
 	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
@@ -696,11 +684,11 @@ portal_group_add_listen(struct portal_gr
 		}
 		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;
 		}
@@ -710,11 +698,23 @@ portal_group_add_listen(struct portal_gr
 	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);
 	}
@@ -727,6 +727,258 @@ portal_group_add_listen(struct portal_gr
 	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)
 {
@@ -1276,6 +1528,7 @@ conf_apply(struct conf *oldconf, struct 
 	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;
@@ -1313,6 +1566,16 @@ conf_apply(struct conf *oldconf, struct 
 		}
 	}
 
+	/* 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
@@ -1342,10 +1605,8 @@ conf_apply(struct conf *oldconf, struct 
 					    oldlun->l_ctl_lun);
 					cumulated_error++;
 				}
-				lun_delete(oldlun);
 			}
 			kernel_port_remove(oldtarg);
-			target_delete(oldtarg);
 			continue;
 		}
 
@@ -1368,7 +1629,6 @@ conf_apply(struct conf *oldconf, struct 
 					    oldlun->l_ctl_lun);
 					cumulated_error++;
 				}
-				lun_delete(oldlun);
 				continue;
 			}
 
@@ -1594,6 +1854,19 @@ conf_apply(struct conf *oldconf, struct 
 		}
 	}
 
+	/* (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);
 }
 
@@ -1605,7 +1878,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
@@ -1625,19 +1898,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)
@@ -1647,12 +1936,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");
@@ -1738,7 +2025,7 @@ handle_connection(struct portal *portal,
 	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) {
@@ -1785,7 +2072,7 @@ main_loop(struct conf *conf, bool dont_f
 	pidfile_write(conf->conf_pidfh);
 
 	for (;;) {
-		if (sighup_received || sigterm_received)
+		if (sighup_received || sigterm_received || timed_out())
 			return;
 
 #ifdef ICL_KERNEL_PROXY
@@ -1909,6 +2196,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;
@@ -1968,6 +2256,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) {
@@ -2003,12 +2295,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 */

Modified: head/usr.sbin/ctld/ctld.h
==============================================================================
--- head/usr.sbin/ctld/ctld.h	Sat Oct 25 09:25:29 2014	(r273634)
+++ head/usr.sbin/ctld/ctld.h	Sat Oct 25 12:50:26 2014	(r273635)
@@ -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;
@@ -281,6 +291,12 @@ struct portal_group	*portal_group_find(c
 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,
@@ -358,6 +374,7 @@ void			log_debugx(const char *, ...) __p
 
 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 */

Added: head/usr.sbin/ctld/isns.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/usr.sbin/ctld/isns.c	Sat Oct 25 12:50:26 2014	(r273635)
@@ -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 == 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)]));
+}

Added: head/usr.sbin/ctld/isns.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/usr.sbin/ctld/isns.h	Sat Oct 25 12:50:26 2014	(r273635)
@@ -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[];
+};
+

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201410251250.s9PCoQaL089386>