diff options
Diffstat (limited to 'usr.sbin/ctld/ctld.c')
-rw-r--r-- | usr.sbin/ctld/ctld.c | 393 |
1 files changed, 349 insertions, 44 deletions
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 */ |