diff --git a/iscsi-scst/README b/iscsi-scst/README index eb75dbbd3..f9ed82c11 100644 --- a/iscsi-scst/README +++ b/iscsi-scst/README @@ -253,6 +253,15 @@ Each target subdirectory contains the following entries: until rel_tgt_id becomes unique. This attribute initialized unique by SCST by default. + - redirect - allows to temporarily or permanently redirect login to the + target to another portal. Discovery sessions will not be impacted, + but normal sessions will be redirected before security negotiation. + The destination should be specified using format "[:port] temp|perm". + IPv6 addresses need to be enclosed in [] brackets. To remove + redirection, provide an empty string. For example: + echo "10.170.77.2:32600 temp" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/redirect + will temporarily redirect login to portal 10.170.77.2 and port 32600. + - tid - TID of this target. Subdirectory "sessions" contains one subdirectory for each connected @@ -525,6 +534,7 @@ both iSCSI-SCST targets will look like: | | | | `-- read_only | | | `-- mgmt | | |-- per_portal_acl +| | |-- redirect | | |-- rel_tgt_id | | |-- sessions | | | `-- iqn.2005-03.org.open-iscsi:cacdcd2520 @@ -586,6 +596,7 @@ both iSCSI-SCST targets will look like: | | | | `-- read_only | | | `-- mgmt | | |-- per_portal_acl +| | |-- redirect | | |-- rel_tgt_id | | |-- sessions | | | `-- iqn.2005-03.org.open-iscsi:cacdcd2520 diff --git a/iscsi-scst/README_in-tree b/iscsi-scst/README_in-tree index ca5ef7a12..4c8f94117 100644 --- a/iscsi-scst/README_in-tree +++ b/iscsi-scst/README_in-tree @@ -150,6 +150,15 @@ Each target subdirectory contains the following entries: until rel_tgt_id becomes unique. This attribute initialized unique by SCST by default. + - redirect - allows to temporarily or permanently redirect login to the + target to another portal. Discovery sessions will not be impacted, + but normal sessions will be redirected before security negotiation. + The destination should be specified using format "[:port] temp|perm". + IPv6 addresses need to be enclosed in [] brackets. To remove + redirection, provide an empty string. For example: + echo "10.170.77.2:32600 temp" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/redirect + will temporarily redirect login to portal 10.170.77.2 and port 32600. + - tid - TID of this target. Subdirectory "sessions" contains one subdirectory for each connected @@ -422,6 +431,7 @@ both iSCSI-SCST targets will look like: | | | | `-- read_only | | | `-- mgmt | | |-- per_portal_acl +| | |-- redirect | | |-- rel_tgt_id | | |-- sessions | | | `-- iqn.2005-03.org.open-iscsi:cacdcd2520 @@ -483,6 +493,7 @@ both iSCSI-SCST targets will look like: | | | | `-- read_only | | | `-- mgmt | | |-- per_portal_acl +| | |-- redirect | | |-- rel_tgt_id | | |-- sessions | | | `-- iqn.2005-03.org.open-iscsi:cacdcd2520 diff --git a/iscsi-scst/usr/config.c b/iscsi-scst/usr/config.c index e7920189e..2a2210571 100644 --- a/iscsi-scst/usr/config.c +++ b/iscsi-scst/usr/config.c @@ -996,8 +996,8 @@ int config_parse_main(const char *data, u32 cookie) if (res < 0) continue; } else if (global_section && - (!strcasecmp(p, ISCSI_ISNS_SERVER_PARAM_NAME) || - !strcasecmp(p, ISCSI_ISNS_ACCESS_CONTROL_PARAM_NAME))) + (!strcasecmp(p, ISCSI_ISNS_SERVER_ATTR_NAME) || + !strcasecmp(p, ISCSI_ISNS_ACCESS_CONTROL_ATTR_NAME))) continue; else { log_error("Unknown or unexpected param: %s\n", p); @@ -1041,9 +1041,9 @@ static int config_isns_load(const char *config) p = config_sep_string(&q); if ((*p == '\0') || (*p == '#')) continue; - if (!strcasecmp(p, ISCSI_ISNS_SERVER_PARAM_NAME)) { + if (!strcasecmp(p, ISCSI_ISNS_SERVER_ATTR_NAME)) { isns_server = strdup(config_sep_string(&q)); - } else if (!strcasecmp(p, ISCSI_ISNS_ACCESS_CONTROL_PARAM_NAME)) { + } else if (!strcasecmp(p, ISCSI_ISNS_ACCESS_CONTROL_ATTR_NAME)) { char *str = config_sep_string(&q); if (!strcasecmp(str, "No")) isns_access_control = 0; @@ -1079,7 +1079,7 @@ int config_load(const char *config_name) } else { err = -errno; log_error("Open config file %s failed: %s", cname, - strerror(err)); + get_error_str(err)); goto out; } } @@ -1087,21 +1087,21 @@ int config_load(const char *config_name) size = lseek(config, 0, SEEK_END); if (size < 0) { err = -errno; - log_error("lseek() failed: %s", strerror(err)); + log_error("lseek() failed: %s", get_error_str(err)); goto out_close; } buf = malloc(size+1); if (buf == NULL) { err = -ENOMEM; - log_error("malloc() failed: %s", strerror(err)); + log_error("malloc() failed: %s", get_error_str(err)); goto out_close; } rc = lseek(config, 0, SEEK_SET); if (rc < 0) { err = -errno; - log_error("lseek() failed: %s", strerror(err)); + log_error("lseek() failed: %s", get_error_str(err)); goto out_free; } @@ -1110,7 +1110,7 @@ int config_load(const char *config_name) rc = read(config, &buf[i], size - i); if (rc < 0) { err = -errno; - log_error("read() failed: %s", strerror(err)); + log_error("read() failed: %s", get_error_str(err)); goto out_free; } else if (rc == 0) break; diff --git a/iscsi-scst/usr/ctldev.c b/iscsi-scst/usr/ctldev.c index c7f8d68bf..fabf5d254 100644 --- a/iscsi-scst/usr/ctldev.c +++ b/iscsi-scst/usr/ctldev.c @@ -88,7 +88,7 @@ int kernel_open(void) if (err != 0) { err = -errno; log_error("Unable to register: %s. Incompatible version of the " - "kernel module?\n", strerror(errno)); + "kernel module?\n", get_error_str(errno)); goto out_close; } else { log_debug(0, "max_data_seg_len %d, max_queued_cmds %d, ", @@ -121,7 +121,7 @@ int kernel_target_create(struct target *target, u32 *tid, u32 cookie) info.tid = (tid != NULL) ? *tid : 0; info.cookie = cookie; - info.attrs_num = 1; + info.attrs_num = 2; for (j = 0; j < session_key_last; j++) { if (session_keys[j].show_in_sysfs) @@ -151,8 +151,13 @@ int kernel_target_create(struct target *target, u32 *tid, u32 cookie) i = 0; kern_attrs[i].mode = 0644; - strlcpy(kern_attrs[i].name, ISCSI_PER_PORTAL_ACL, - sizeof(ISCSI_PER_PORTAL_ACL)); + strlcpy(kern_attrs[i].name, ISCSI_PER_PORTAL_ACL_ATTR_NAME, + sizeof(ISCSI_PER_PORTAL_ACL_ATTR_NAME)); + i++; + + kern_attrs[i].mode = 0644; + strlcpy(kern_attrs[i].name, ISCSI_TARGET_REDIRECTION_ATTR_NAME, + sizeof(ISCSI_TARGET_REDIRECTION_ATTR_NAME)); i++; for (j = 0; j < session_key_last; j++) { @@ -196,7 +201,7 @@ int kernel_target_create(struct target *target, u32 *tid, u32 cookie) if ((err = ioctl(ctrl_fd, ADD_TARGET, &info)) < 0) { err = -errno; log_error("Can't create target %s: %s\n", target->name, - strerror(errno)); + get_error_str(errno)); } else { target->tid = err; if (tid != NULL) @@ -337,7 +342,7 @@ int kernel_params_get(u32 tid, u64 sid, int type, struct iscsi_param *params) if ((err = ioctl(ctrl_fd, ISCSI_PARAM_GET, &info)) < 0) { err = -errno; log_debug(1, "Can't get session params for session 0x%" PRIx64 - " (tid %u, err %d): %s\n", sid, tid, err, strerror(errno)); + " (tid %u, err %d): %s\n", sid, tid, err, get_error_str(errno)); } if (type == key_session) @@ -380,7 +385,7 @@ int kernel_params_set(u32 tid, u64 sid, int type, u32 partial, err = -errno; log_error("Can't set session params for session 0x%" PRIx64 " (tid %u, type %d, partial %d, err %d): %s\n", sid, - tid, type, partial, err, strerror(errno)); + tid, type, partial, err, get_error_str(errno)); } out: @@ -429,7 +434,7 @@ int kernel_session_create(struct connection *conn) res = -errno; log_error("Can't create sess 0x%" PRIx64 " (tid %d, " "initiator %s): %s\n", conn->sess->sid.id64, conn->tid, - conn->sess->initiator, strerror(errno)); + conn->sess->initiator, get_error_str(errno)); } out: @@ -450,7 +455,7 @@ int kernel_session_destroy(u32 tid, u64 sid) if (res < 0) { res = -errno; log_debug(2, "Can't destroy sess 0x%" PRIx64 " (tid %d): %s\n", - sid, tid, strerror(errno)); + sid, tid, get_error_str(errno)); } return res; @@ -475,7 +480,7 @@ int kernel_conn_create(u32 tid, u64 sid, u32 cid, u32 stat_sn, u32 exp_stat_sn, if (res < 0) { res = -errno; log_error("Can't create conn %x (sess 0x%" PRIx64 ", tid %d): %s\n", - cid, sid, tid, strerror(errno)); + cid, sid, tid, get_error_str(errno)); } return res; diff --git a/iscsi-scst/usr/event.c b/iscsi-scst/usr/event.c index 4d4b4fd08..837ea177c 100644 --- a/iscsi-scst/usr/event.c +++ b/iscsi-scst/usr/event.c @@ -25,10 +25,10 @@ #include #include #include - #include #include #include +#include #include @@ -133,7 +133,7 @@ static int send_mgmt_cmd_res(u32 tid, u32 cookie, u32 req_cmd, int result, if (res != 0) { res = -errno; log_error("Can't send mgmt reply (cookie %d, result %d, " - "res %d): %s\n", cookie, result, res, strerror(errno)); + "res %d): %s\n", cookie, result, res, get_error_str(errno)); } return res; @@ -169,7 +169,7 @@ static int handle_e_add_target(int fd, const struct iscsi_kern_event *event) if ((errno == EINTR) || (errno == EAGAIN)) continue; log_error("read netlink fd (%d) failed: %s", fd, - strerror(errno)); + get_error_str(errno)); send_mgmt_cmd_res(0, event->cookie, E_ADD_TARGET, -errno, NULL); exit(1); } @@ -185,7 +185,7 @@ static int handle_e_add_target(int fd, const struct iscsi_kern_event *event) if ((errno == EINTR) || (errno == EAGAIN)) continue; log_error("read netlink fd (%d) failed: %s", fd, - strerror(errno)); + get_error_str(errno)); send_mgmt_cmd_res(0, event->cookie, E_ADD_TARGET, -errno, NULL); exit(1); } @@ -297,9 +297,9 @@ static int handle_add_attr(struct target *target, char *p, u32 cookie) dir = params_index_by_name_numwild(pp, user_keys); if (dir >= 0) res = handle_add_user(target, dir, pp, p, cookie); - else if (strncasecmp_numwild(ISCSI_ALLOWED_PORTAL_NAME, pp) == 0) + else if (strncasecmp_numwild(ISCSI_ALLOWED_PORTAL_ATTR_NAME, pp) == 0) res = __handle_add_attr(target, &target->allowed_portals, - ISCSI_ALLOWED_PORTAL_NAME, p, 1, cookie); + ISCSI_ALLOWED_PORTAL_ATTR_NAME, p, 1, cookie); else { log_error("Syntax error at %s", pp); res = -EINVAL; @@ -365,7 +365,7 @@ static int handle_del_attr(struct target *target, char *p, u32 cookie) dir = params_index_by_name_numwild(pp, user_keys); if (dir >= 0) res = handle_del_user(target, dir, p, cookie); - else if (strncasecmp_numwild(ISCSI_ALLOWED_PORTAL_NAME, pp) == 0) + else if (strncasecmp_numwild(ISCSI_ALLOWED_PORTAL_ATTR_NAME, pp) == 0) res = __handle_del_attr(target, &target->allowed_portals, p, cookie); else { @@ -406,7 +406,7 @@ static int handle_e_mgmt_cmd(int fd, const struct iscsi_kern_event *event) if ((errno == EINTR) || (errno == EAGAIN)) continue; log_error("read netlink fd (%d) failed: %s", fd, - strerror(errno)); + get_error_str(errno)); send_mgmt_cmd_res(0, event->cookie, E_MGMT_CMD, -errno, NULL); exit(1); } @@ -495,7 +495,7 @@ static int handle_e_get_attr_value(int fd, const struct iscsi_kern_event *event) if ((errno == EINTR) || (errno == EAGAIN)) continue; log_error("read netlink fd (%d) failed: %s", fd, - strerror(errno)); + get_error_str(errno)); send_mgmt_cmd_res(0, event->cookie, E_GET_ATTR_VALUE, -errno, NULL); exit(1); } @@ -547,7 +547,7 @@ static int handle_e_get_attr_value(int fd, const struct iscsi_kern_event *event) snprintf(res_str, sizeof(res_str), "%s %s\n", ISCSI_USER_NAME(user), ISCSI_USER_PASS(user)); add_key_mark(res_str, sizeof(res_str), 0); - } else if (strncasecmp_numwild(ISCSI_ALLOWED_PORTAL_NAME, pp) == 0) { + } else if (strncasecmp_numwild(ISCSI_ALLOWED_PORTAL_ATTR_NAME, pp) == 0) { struct iscsi_attr *portal; if (target == NULL) { @@ -573,7 +573,7 @@ static int handle_e_get_attr_value(int fd, const struct iscsi_kern_event *event) goto out_free; } snprintf(res_str, sizeof(res_str), "%d\n", iscsi_enabled); - } else if (strcasecmp(ISCSI_PER_PORTAL_ACL, pp) == 0) { + } else if (strcasecmp(ISCSI_PER_PORTAL_ACL_ATTR_NAME, pp) == 0) { if (target == NULL) { log_error("Target expected for attr %s", pp); res = -EINVAL; @@ -582,7 +582,26 @@ static int handle_e_get_attr_value(int fd, const struct iscsi_kern_event *event) snprintf(res_str, sizeof(res_str), "%d\n", target->per_portal_acl); if (target->per_portal_acl) add_key_mark(res_str, sizeof(res_str), 0); - }else if (strcasecmp(ISCSI_ISNS_SERVER_PARAM_NAME, pp) == 0) { + } else if (strcasecmp(ISCSI_TARGET_REDIRECTION_ATTR_NAME, pp) == 0) { + if (target == NULL) { + log_error("Target expected for attr %s", pp); + res = -EINVAL; + goto out_free; + } + if (strlen(target->redirect.addr) != 0) { + const char *type = (target->redirect.type == ISCSI_STATUS_TGT_MOVED_TEMP) ? + ISCSI_TARGET_REDIRECTION_VALUE_TEMP : + ISCSI_TARGET_REDIRECTION_VALUE_PERM; + if (target->redirect.port != ISCSI_LISTEN_PORT) + snprintf(res_str, sizeof(res_str), "%s:%d %s\n", + target->redirect.addr, target->redirect.port, type); + else + snprintf(res_str, sizeof(res_str), "%s %s\n", + target->redirect.addr, type); + add_key_mark(res_str, sizeof(res_str), 0); + } else + *res_str = '\0'; + } else if (strcasecmp(ISCSI_ISNS_SERVER_ATTR_NAME, pp) == 0) { if (target != NULL) { log_error("Not NULL target %s for global attribute %s", target->name, pp); @@ -611,6 +630,99 @@ out: return res; } +static int handle_target_redirect(struct target *target, char *p) +{ + int res = 0; + char *addr, *type, *t, *port; + int port_num = ISCSI_LISTEN_PORT; + int type_num; + union { + struct in_addr ia4; + struct in6_addr ia6; + } ia; + + addr = config_sep_string(&p); + if (*addr == '\0') { + log_info("Target redirection for %s cleared", target->name); + target->redirect.addr[0] = '\0'; + goto out; + } + + type = config_sep_string(&p); + if (*type == '\0') { + log_error("%s", "Redirection type required"); + res = -EINVAL; + goto out; + } + + t = config_sep_string(&p); + if (*t != '\0') { + log_error("%s", "Too many arguments for redirection"); + res = -EINVAL; + goto out; + } + + t = strrchr(addr, ']'); + if (t != NULL) + port = strchr(t, ':'); + else + port = strrchr(addr, ':'); + if (port != NULL) { + *port = '\0'; + port++; + port_num = strtol(port, (char **) NULL, 10); + if ((port_num <= 0) || (errno == EINVAL)) { + log_error("Invalid port %s", port); + res = -EINVAL; + goto out; + } + } + + if (strlen(addr) >= sizeof(target->redirect.addr)) { + log_error("Too long addr %s, max allowed %d", addr, + sizeof(target->redirect.addr)-1); + res = -ERANGE; + goto out; + } + + if (inet_pton(AF_INET, addr, &ia) != 1) { + char tmp[sizeof(target->redirect.addr)]; + if (*addr == '[') + t = addr+1; + else + t = addr; + strlcpy(tmp, t, strchrnul(t, ']')-t+1); + if (inet_pton(AF_INET6, tmp, &ia) != 1) { + log_error("Invalid addr %s", addr); + res = -EINVAL; + goto out; + } + } + + if (strcasecmp(type, ISCSI_TARGET_REDIRECTION_VALUE_TEMP) == 0) { + log_debug(1, "Temporary redirection"); + type_num = ISCSI_STATUS_TGT_MOVED_TEMP; + } else if (strcasecmp(type, ISCSI_TARGET_REDIRECTION_VALUE_PERM) == 0) { + log_debug(1, "Permament redirection"); + type_num = ISCSI_STATUS_TGT_MOVED_PERM; + } else { + log_error("Invalid redirection type %s", type); + res = -EINVAL; + goto out; + } + + log_info("Target %s %s redirected to %s:%d", target->name, + (type_num == ISCSI_STATUS_TGT_MOVED_TEMP) ? "temporarily" : "permanently", + addr, port_num); + + strcpy(target->redirect.addr, addr); + target->redirect.port = port_num; + target->redirect.type = type_num; + +out: + return res; +} + static int handle_e_set_attr_value(int fd, const struct iscsi_kern_event *event) { int res = 0, rc, idx; @@ -646,7 +758,7 @@ static int handle_e_set_attr_value(int fd, const struct iscsi_kern_event *event) if ((errno == EINTR) || (errno == EAGAIN)) continue; log_error("read netlink fd (%d) failed: %s", fd, - strerror(errno)); + get_error_str(errno)); send_mgmt_cmd_res(0, event->cookie, E_SET_ATTR_VALUE, -errno, NULL); exit(1); } @@ -661,7 +773,7 @@ static int handle_e_set_attr_value(int fd, const struct iscsi_kern_event *event) if ((errno == EINTR) || (errno == EAGAIN)) continue; log_error("read netlink fd (%d) failed: %s", fd, - strerror(errno)); + get_error_str(errno)); send_mgmt_cmd_res(0, event->cookie, E_SET_ATTR_VALUE, -errno, NULL); exit(1); } @@ -762,7 +874,7 @@ static int handle_e_set_attr_value(int fd, const struct iscsi_kern_event *event) res = account_replace(target, idx, pp, p); if (res != 0) goto out_free; - } else if (strncasecmp_numwild(ISCSI_ALLOWED_PORTAL_NAME, pp) == 0) { + } else if (strncasecmp_numwild(ISCSI_ALLOWED_PORTAL_ATTR_NAME, pp) == 0) { struct iscsi_attr *portal; if (target == NULL) { @@ -798,7 +910,7 @@ static int handle_e_set_attr_value(int fd, const struct iscsi_kern_event *event) res = -EINVAL; goto out_free; } - } else if (strcasecmp(ISCSI_PER_PORTAL_ACL, pp) == 0) { + } else if (strcasecmp(ISCSI_PER_PORTAL_ACL_ATTR_NAME, pp) == 0) { if (target == NULL) { log_error("Target expected for attr %s", pp); res = -EINVAL; @@ -814,7 +926,16 @@ static int handle_e_set_attr_value(int fd, const struct iscsi_kern_event *event) res = -EINVAL; goto out_free; } - }else if (strcasecmp(ISCSI_ISNS_SERVER_PARAM_NAME, pp) == 0) { + } else if (strcasecmp(ISCSI_TARGET_REDIRECTION_ATTR_NAME, pp) == 0) { + if (target == NULL) { + log_error("Target expected for attr %s", pp); + res = -EINVAL; + goto out_free; + } + res = handle_target_redirect(target, p); + if (res != 0) + goto out_free; + } else if (strcasecmp(ISCSI_ISNS_SERVER_ATTR_NAME, pp) == 0) { if (target != NULL) { log_error("Not NULL target %s for global attribute %s", target->name, pp); @@ -915,7 +1036,7 @@ retry: return EAGAIN; if (errno == EINTR) goto retry; - log_error("read netlink fd (%d) failed: %s", fd, strerror(errno)); + log_error("read netlink fd (%d) failed: %s", fd, get_error_str(errno)); exit(1); } diff --git a/iscsi-scst/usr/iscsi_scstd.c b/iscsi-scst/usr/iscsi_scstd.c index 36ce1e4ed..babb6bfb7 100644 --- a/iscsi-scst/usr/iscsi_scstd.c +++ b/iscsi-scst/usr/iscsi_scstd.c @@ -98,9 +98,9 @@ static void set_non_blocking(int fd) if (res != -1) { res = fcntl(fd, F_SETFL, res | O_NONBLOCK); if (res) - log_warning("unable to set fd flags (%s)!", strerror(errno)); + log_warning("unable to set fd flags (%s)!", get_error_str(errno)); } else - log_warning("unable to get fd flags (%s)!", strerror(errno)); + log_warning("unable to get fd flags (%s)!", get_error_str(errno)); } static void sock_set_keepalive(int sock, int timeout) @@ -109,22 +109,22 @@ static void sock_set_keepalive(int sock, int timeout) int opt = 2; if (setsockopt(sock, SOL_TCP, TCP_KEEPCNT, &opt, sizeof(opt))) - log_warning("unable to set TCP_KEEPCNT on server socket (%s)!", strerror(errno)); + log_warning("unable to set TCP_KEEPCNT on server socket (%s)!", get_error_str(errno)); if (setsockopt(sock, SOL_TCP, TCP_KEEPIDLE, &timeout, sizeof(timeout))) - log_warning("unable to set TCP_KEEPIDLE on server socket (%s)!", strerror(errno)); + log_warning("unable to set TCP_KEEPIDLE on server socket (%s)!", get_error_str(errno)); opt = 3; if (setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, &opt, sizeof(opt))) - log_warning("unable to set KEEPINTVL on server socket (%s)!", strerror(errno)); + log_warning("unable to set KEEPINTVL on server socket (%s)!", get_error_str(errno)); opt = 1; if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt))) - log_warning("unable to set SO_KEEPALIVE on server socket (%s)!", strerror(errno)); + log_warning("unable to set SO_KEEPALIVE on server socket (%s)!", get_error_str(errno)); } } -const char *get_EAI_error_str(int error) +const char *get_error_str(int error) { if (error == EAI_SYSTEM) return strerror(errno); @@ -148,7 +148,7 @@ static void create_listen_socket(struct pollfd *array) rc = getaddrinfo(server_address, servname, &hints, &res0); if (rc != 0) { log_error("Unable to get address info (%s)!", - get_EAI_error_str(rc)); + get_error_str(rc)); exit(1); } @@ -157,7 +157,7 @@ static void create_listen_socket(struct pollfd *array) sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sock < 0) { log_error("Unable to create server socket (%s) %d %d %d!", - strerror(errno), res->ai_family, + get_error_str(errno), res->ai_family, res->ai_socktype, res->ai_protocol); continue; } @@ -167,23 +167,23 @@ static void create_listen_socket(struct pollfd *array) opt = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) log_warning("Unable to set SO_REUSEADDR on server socket (%s)!", - strerror(errno)); + get_error_str(errno)); opt = 1; if (res->ai_family == AF_INET6 && setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt))) { - log_error("Unable to restrict IPv6 socket (%s)", strerror(errno)); + log_error("Unable to restrict IPv6 socket (%s)", get_error_str(errno)); close(sock); continue; } if (bind(sock, res->ai_addr, res->ai_addrlen)) { - log_error("Unable to bind server socket (%s)!", strerror(errno)); + log_error("Unable to bind server socket (%s)!", get_error_str(errno)); close(sock); continue; } if (listen(sock, INCOMING_MAX)) { - log_error("Unable to listen to server socket (%s)!", strerror(errno)); + log_error("Unable to listen to server socket (%s)!", get_error_str(errno)); close(sock); continue; } @@ -231,7 +231,8 @@ static void accept_connection(int listen) case ENETUNREACH: break; default: - perror("accept(incoming_socket) failed"); + log_error("accept(incoming_socket) failed: %s", + get_error_str(errno)); exit(1); } goto out; @@ -240,7 +241,7 @@ static void accept_connection(int listen) namesize = sizeof(to); rc = getsockname(fd, &to.sa, &namesize); if (rc != 0) { - perror("getsockname() failed"); + log_error("getsockname() failed: %s", get_error_str(errno)); goto out_close; } @@ -249,7 +250,7 @@ static void accept_connection(int listen) NI_NUMERICHOST | NI_NUMERICSERV); if (rc != 0) { log_error("Target portal getnameinfo() failed: %s!", - get_EAI_error_str(rc)); + get_error_str(rc)); goto out_close; } @@ -258,7 +259,7 @@ static void accept_connection(int listen) sizeof(initiator_port), NI_NUMERICHOST | NI_NUMERICSERV); if (rc != 0) { log_error("Initiator getnameinfo() failed: %s!", - get_EAI_error_str(rc)); + get_error_str(rc)); goto out_close; } @@ -526,7 +527,7 @@ static void event_loop(void) "of iSCSI-SCST.", __FUNCTION__); else log_error("%s: poll() failed: %s", __FUNCTION__, - strerror(errno)); + get_error_str(errno)); exit(1); } @@ -706,7 +707,7 @@ int main(int argc, char **argv) init_max_params(); #ifndef CONFIG_SCST_PROC - err = kernel_attr_add(NULL, ISCSI_ISNS_SERVER_PARAM_NAME, + err = kernel_attr_add(NULL, ISCSI_ISNS_SERVER_ATTR_NAME, S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR, 0); if (err != 0) exit(err); @@ -778,10 +779,10 @@ int main(int argc, char **argv) exit(1); if (gid && setgid(gid) < 0) - perror("setgid failed"); + log_error("setgid failed: %s", get_error_str(errno)); if (uid && setuid(uid) < 0) - perror("setuid failed"); + log_error("setuid failed: %s", get_error_str(errno)); event_loop(); diff --git a/iscsi-scst/usr/iscsid.c b/iscsi-scst/usr/iscsid.c index e6c2b380d..8f47b1bee 100644 --- a/iscsi-scst/usr/iscsid.c +++ b/iscsi-scst/usr/iscsid.c @@ -558,6 +558,20 @@ static void login_start(struct connection *conn) return; } + if (target_redirected(target, conn)) { + struct iscsi_login_rsp_hdr *rsp = + (struct iscsi_login_rsp_hdr *)&conn->rsp.bhs; + + log_debug(1, "Redirecting target %s login to %s:%d", + target->name, target->redirect.addr, + target->redirect.port); + + rsp->status_class = ISCSI_STATUS_REDIRECT; + rsp->status_detail = target->redirect.type; + conn->state = STATE_EXIT; + return; + } + conn->tid = target->tid; if (!config_initiator_access_allowed(conn->tid, conn->fd) || @@ -574,7 +588,7 @@ static void login_start(struct connection *conn) if (err != 0) { log_error("Can't get session params for session 0x%" PRIx64 " (err %d): %s\n", conn->sid.id64, err, - strerror(-err)); + get_error_str(-err)); login_rsp_tgt_err(conn, ISCSI_STATUS_TARGET_ERROR); return; } @@ -991,7 +1005,7 @@ int cmnd_execute(struct connection *conn) } cmnd_exec_login(conn); login_rsp = (struct iscsi_login_rsp_hdr *) &conn->rsp.bhs; - if (login_rsp->status_class) + if (login_rsp->status_class && login_rsp->status_class != ISCSI_STATUS_REDIRECT) conn_free_rsp_buf_list(conn); break; case ISCSI_OP_TEXT_CMD: diff --git a/iscsi-scst/usr/iscsid.h b/iscsi-scst/usr/iscsid.h index cd9c0624f..fa84bba9f 100644 --- a/iscsi-scst/usr/iscsid.h +++ b/iscsi-scst/usr/iscsid.h @@ -21,6 +21,7 @@ #include #include #include +#include #include "types.h" #include "iscsi_hdr.h" @@ -190,6 +191,12 @@ struct target { char *alias; unsigned int sessions_count; + struct redirect_addr { + char addr[NI_MAXHOST + 1]; + int port; + u8 type; /* one of ISCSI_STATUS_TGT_MOVED_* constants */ + } redirect; + struct __qelem target_in_accounts; struct __qelem target_out_accounts; @@ -235,7 +242,7 @@ extern void conn_free_rsp_buf_list(struct connection *conn); extern uint16_t server_port; extern struct iscsi_init_params iscsi_init_params; extern void isns_set_fd(int isns, int scn_listen, int scn); -extern const char *get_EAI_error_str(int error); +extern const char *get_error_str(int error); /* iscsid.c */ extern int iscsi_enabled; @@ -288,6 +295,7 @@ extern int target_portal_allowed(struct target *target, extern const char *iscsi_make_full_initiator_name(int per_portal_acl, const char *initiator_name, const char *target_portal, char *buf, int size); +extern bool target_redirected(struct target *target, struct connection *conn); /* message.c */ extern int iscsi_adm_request_listen(void); diff --git a/iscsi-scst/usr/isns.c b/iscsi-scst/usr/isns.c index 9e29fc5e7..cfd3e8984 100644 --- a/iscsi-scst/usr/isns.c +++ b/iscsi-scst/usr/isns.c @@ -100,14 +100,14 @@ static int isns_get_ip(int fd) err = getsockname(fd, &lss.sa, &slen); if (err) { - log_error("getsockname error: %s!", strerror(err)); + log_error("getsockname error: %s!", get_error_str(err)); return err; } err = getnameinfo(&lss.sa, sizeof(lss), eid, sizeof(eid), NULL, 0, 0); if (err) { - log_error("getnameinfo error: %s!", get_EAI_error_str(err)); + log_error("getnameinfo error: %s!", get_error_str(err)); return err; } @@ -138,7 +138,7 @@ static int isns_connect(void) fd = socket(ss.ss_family, SOCK_STREAM, IPPROTO_TCP); if (fd < 0) { - log_error("unable to create (%s) %d!", strerror(errno), + log_error("unable to create (%s) %d!", get_error_str(errno), ss.ss_family); return -errno; } @@ -149,7 +149,7 @@ static int isns_connect(void) */ err = connect(fd, (struct sockaddr *) &ss, sizeof(ss)); if (err < 0) { - log_error("unable to connect (%s) %d!", strerror(errno), + log_error("unable to connect (%s) %d!", get_error_str(errno), ss.ss_family); close(fd); return -errno; @@ -226,7 +226,7 @@ static int isns_scn_deregister(char *name) err = write(isns_fd, buf, length + sizeof(struct isns_hdr)); if (err < 0) - log_error("%s %d: %s", __func__, __LINE__, strerror(errno)); + log_error("%s %d: %s", __func__, __LINE__, get_error_str(errno)); return 0; } @@ -285,7 +285,7 @@ static int isns_scn_register(void) err = write(isns_fd, buf, length + sizeof(struct isns_hdr)); if (err < 0) - log_error("%s %d: %s", __func__, __LINE__, strerror(errno)); + log_error("%s %d: %s", __func__, __LINE__, get_error_str(errno)); return 0; } @@ -340,7 +340,7 @@ static int isns_attr_query(char *name) err = write(isns_fd, buf, length + sizeof(struct isns_hdr)); if (err < 0) - log_error("%s %d: %s", __func__, __LINE__, strerror(errno)); + log_error("%s %d: %s", __func__, __LINE__, get_error_str(errno)); return 0; } @@ -378,7 +378,7 @@ static int isns_deregister(void) err = write(isns_fd, buf, length + sizeof(struct isns_hdr)); if (err < 0) - log_error("%s %d: %s", __func__, __LINE__, strerror(errno)); + log_error("%s %d: %s", __func__, __LINE__, get_error_str(errno)); return 0; } @@ -443,7 +443,7 @@ int isns_target_register(char *name) err = write(isns_fd, buf, length + sizeof(struct isns_hdr)); if (err < 0) - log_error("%s %d: %s", __func__, __LINE__, strerror(errno)); + log_error("%s %d: %s", __func__, __LINE__, get_error_str(errno)); if (scn_listen_port) isns_scn_register(); @@ -504,7 +504,7 @@ int isns_target_deregister(char *name) err = write(isns_fd, buf, length + sizeof(struct isns_hdr)); if (err < 0) - log_error("%s %d: %s", __func__, __LINE__, strerror(errno)); + log_error("%s %d: %s", __func__, __LINE__, get_error_str(errno)); return 0; } @@ -786,7 +786,7 @@ static int scn_accept_connection(void) fd = accept(scn_listen_fd, &from.sa, &slen); if (fd < 0) { log_error("%s %d: accept error: %s", __func__, __LINE__, - strerror(errno)); + get_error_str(errno)); return -errno; } log_error("Accept scn connection %d", fd); @@ -794,7 +794,7 @@ static int scn_accept_connection(void) err = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)); if (err) log_error("%s %d: %s\n", __func__, __LINE__, - strerror(errno)); + get_error_str(errno)); /* not critical, so ignore. */ scn_fd = fd; @@ -824,7 +824,7 @@ static void send_scn_rsp(char *name, uint16_t transaction) err = write(scn_fd, buf, length + sizeof(struct isns_hdr)); if (err < 0) - log_error("%s %d: %s", __func__, __LINE__, strerror(errno)); + log_error("%s %d: %s", __func__, __LINE__, get_error_str(errno)); } int isns_scn_handle(int is_accept) @@ -882,7 +882,7 @@ static int scn_init(void) fd = socket(ss.ss_family, SOCK_STREAM, IPPROTO_TCP); if (fd < 0) { - log_error("%s %d: %s\n", __func__, __LINE__, strerror(errno)); + log_error("%s %d: %s\n", __func__, __LINE__, get_error_str(errno)); err = -errno; goto out; } @@ -892,21 +892,21 @@ static int scn_init(void) err = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)); if (err) { log_error("%s %d: %s\n", __func__, __LINE__, - strerror(errno)); + get_error_str(errno)); goto out_close; } } err = listen(fd, 5); if (err) { - log_error("%s %d: %s\n", __func__, __LINE__, strerror(errno)); + log_error("%s %d: %s\n", __func__, __LINE__, get_error_str(errno)); goto out_close; } slen = sizeof(lss); err = getsockname(fd, (struct sockaddr *) &lss, &slen); if (err) { - log_error("%s %d: %s\n", __func__, __LINE__, strerror(errno)); + log_error("%s %d: %s\n", __func__, __LINE__, get_error_str(errno)); goto out_close; } @@ -941,7 +941,7 @@ int isns_init(void) hints.ai_socktype = SOCK_STREAM; err = getaddrinfo(isns_server, (char *)&port, &hints, &res); if (err) { - log_error("getaddrinfo error: %s, %s", get_EAI_error_str(err), + log_error("getaddrinfo error: %s, %s", get_error_str(err), isns_server); goto out; } diff --git a/iscsi-scst/usr/param.h b/iscsi-scst/usr/param.h index 6723ace33..c574c8e35 100644 --- a/iscsi-scst/usr/param.h +++ b/iscsi-scst/usr/param.h @@ -16,11 +16,14 @@ #ifndef PARAMS_H #define PARAMS_H -#define ISCSI_ISNS_SERVER_PARAM_NAME "iSNSServer" -#define ISCSI_ISNS_ACCESS_CONTROL_PARAM_NAME "iSNSAccessControl" +#define ISCSI_ISNS_SERVER_ATTR_NAME "iSNSServer" +#define ISCSI_ISNS_ACCESS_CONTROL_ATTR_NAME "iSNSAccessControl" #define ISCSI_ENABLED_ATTR_NAME "enabled" -#define ISCSI_ALLOWED_PORTAL_NAME "allowed_portal" -#define ISCSI_PER_PORTAL_ACL "per_portal_acl" +#define ISCSI_ALLOWED_PORTAL_ATTR_NAME "allowed_portal" +#define ISCSI_PER_PORTAL_ACL_ATTR_NAME "per_portal_acl" +#define ISCSI_TARGET_REDIRECTION_ATTR_NAME "redirect" +#define ISCSI_TARGET_REDIRECTION_VALUE_TEMP "temp" +#define ISCSI_TARGET_REDIRECTION_VALUE_PERM "perm" struct iscsi_key; diff --git a/iscsi-scst/usr/target.c b/iscsi-scst/usr/target.c index 886b732cf..23920e676 100644 --- a/iscsi-scst/usr/target.c +++ b/iscsi-scst/usr/target.c @@ -425,3 +425,52 @@ int target_add(struct target *target, u32 *tid, u32 cookie) out: return err; } + +bool target_redirected(struct target *target, struct connection *conn) +{ + bool res = false, rc; + union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + } sa; + socklen_t slen = sizeof(sa); + char tmp[NI_MAXHOST + 1]; + char addr[NI_MAXHOST + 3]; + char redirect[NI_MAXHOST + NI_MAXSERV + 4]; + char *p; + + if (strlen(target->redirect.addr) == 0) + goto out; + + rc = getsockname(conn->fd, (struct sockaddr *)&sa.sa, &slen); + if (rc != 0) { + log_error("getsockname() failed: %s", get_error_str(errno)); + goto out; + } + + rc = getnameinfo(&sa.sa, sizeof(sa), tmp, sizeof(tmp), NULL, 0, NI_NUMERICHOST); + if (rc != 0) { + log_error("getnameinfo() failed: %s", get_error_str(errno)); + goto out; + } + + if ((p = strrchr(tmp, '%'))) + *p = '\0'; + + if (sa.sa.sa_family == AF_INET6) + snprintf(addr, sizeof(addr), "[%s]", tmp); + else + snprintf(addr, sizeof(addr), "%s", tmp); + + snprintf(redirect, sizeof(redirect), "%s:%d", target->redirect.addr, + target->redirect.port); + + if (strcmp(target->redirect.addr, addr)) { + text_key_add(conn, "TargetAddress", redirect); + res = true; + } + +out: + return res; +}