diff options
-rw-r--r-- | usr.sbin/ctld/Makefile | 3 | ||||
-rw-r--r-- | usr.sbin/ctld/ctl.conf.5 | 11 | ||||
-rw-r--r-- | usr.sbin/ctld/ctld.c | 393 | ||||
-rw-r--r-- | usr.sbin/ctld/ctld.h | 17 | ||||
-rw-r--r-- | usr.sbin/ctld/isns.c | 258 | ||||
-rw-r--r-- | usr.sbin/ctld/isns.h | 92 | ||||
-rw-r--r-- | usr.sbin/ctld/parse.y | 30 | ||||
-rw-r--r-- | usr.sbin/ctld/token.l | 3 |
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; } |