summaryrefslogtreecommitdiffstats
path: root/usr.sbin/ctld
diff options
context:
space:
mode:
authortrasz <trasz@FreeBSD.org>2014-12-09 11:50:50 +0000
committertrasz <trasz@FreeBSD.org>2014-12-09 11:50:50 +0000
commit35a0788239e793d9b8682bd8b4cdac84beaf2a4c (patch)
tree81323893c316c2855a80e58fed79a68e429d6df1 /usr.sbin/ctld
parentace7dfdc9c44f44bf73a51f521260c0bf2b39579 (diff)
downloadFreeBSD-src-35a0788239e793d9b8682bd8b4cdac84beaf2a4c.zip
FreeBSD-src-35a0788239e793d9b8682bd8b4cdac84beaf2a4c.tar.gz
MFC r274308:
Add support for sending redirections to iSCSI target. MFC r274309: Fix several nits in redirection handling - don't use wrong CSG, and avoid use-after-free. Sponsored by: The FreeBSD Foundation
Diffstat (limited to 'usr.sbin/ctld')
-rw-r--r--usr.sbin/ctld/ctl.conf.518
-rw-r--r--usr.sbin/ctld/ctld.c56
-rw-r--r--usr.sbin/ctld/ctld.h6
-rw-r--r--usr.sbin/ctld/login.c80
-rw-r--r--usr.sbin/ctld/parse.y29
-rw-r--r--usr.sbin/ctld/token.l1
6 files changed, 183 insertions, 7 deletions
diff --git a/usr.sbin/ctld/ctl.conf.5 b/usr.sbin/ctld/ctl.conf.5
index db68d78..9df9e60 100644
--- a/usr.sbin/ctld/ctl.conf.5
+++ b/usr.sbin/ctld/ctl.conf.5
@@ -27,7 +27,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd November 8, 2014
+.Dd November 9, 2014
.Dt CTL.CONF 5
.Os
.Sh NAME
@@ -218,6 +218,17 @@ An IPv4 or IPv6 address and port to listen on for incoming connections.
.\".It Ic listen-iser Ar address
.\"An IPv4 or IPv6 address and port to listen on for incoming connections
.\"using iSER (iSCSI over RDMA) protocol.
+.It Ic redirect Aq Ar address
+IPv4 or IPv6 address to redirect initiators to.
+When configured, all initiators attempting to connect to portal
+belonging to this
+.Sy portal-group
+will get redirected using "Target moved temporarily" login response.
+Redirection happens before authentication and any
+.Sy initiator-name
+or
+.Sy initiator-portal
+checks are skipped.
.El
.Ss target Context
.Bl -tag -width indent
@@ -296,6 +307,11 @@ The default portal group is
.Qq Ar default ,
which makes the target available
on TCP port 3260 on all configured IPv4 and IPv6 addresses.
+.It Ic redirect Aq Ar address
+IPv4 or IPv6 address to redirect initiators to.
+When configured, all initiators attempting to connect to this target
+will get redirected using "Target moved temporarily" login response.
+Redirection happens after successful authentication.
.It Ic lun Ar number
Create a
.Sy lun
diff --git a/usr.sbin/ctld/ctld.c b/usr.sbin/ctld/ctld.c
index a480b2e..bbf8e7d 100644
--- a/usr.sbin/ctld/ctld.c
+++ b/usr.sbin/ctld/ctld.c
@@ -622,6 +622,7 @@ portal_group_delete(struct portal_group *pg)
TAILQ_FOREACH_SAFE(portal, &pg->pg_portals, p_next, tmp)
portal_delete(portal);
free(pg->pg_name);
+ free(pg->pg_redirection);
free(pg);
}
@@ -1000,6 +1001,22 @@ portal_group_set_filter(struct portal_group *pg, const char *str)
return (0);
}
+int
+portal_group_set_redirection(struct portal_group *pg, const char *addr)
+{
+
+ if (pg->pg_redirection != NULL) {
+ log_warnx("cannot set redirection to \"%s\" for "
+ "portal-group \"%s\"; already defined",
+ addr, pg->pg_name);
+ return (1);
+ }
+
+ pg->pg_redirection = checked_strdup(addr);
+
+ return (0);
+}
+
static bool
valid_hex(const char ch)
{
@@ -1144,6 +1161,7 @@ target_delete(struct target *targ)
TAILQ_FOREACH_SAFE(lun, &targ->t_luns, l_next, tmp)
lun_delete(lun);
free(targ->t_name);
+ free(targ->t_redirection);
free(targ);
}
@@ -1160,6 +1178,22 @@ target_find(struct conf *conf, const char *name)
return (NULL);
}
+int
+target_set_redirection(struct target *target, const char *addr)
+{
+
+ if (target->t_redirection != NULL) {
+ log_warnx("cannot set redirection to \"%s\" for "
+ "target \"%s\"; already defined",
+ addr, target->t_name);
+ return (1);
+ }
+
+ target->t_redirection = checked_strdup(addr);
+
+ return (0);
+}
+
struct lun *
lun_new(struct target *targ, int lun_id)
{
@@ -1486,10 +1520,15 @@ conf_verify(struct conf *conf)
return (error);
found = true;
}
- if (!found) {
+ if (!found && targ->t_redirection == NULL) {
log_warnx("no LUNs defined for target \"%s\"",
targ->t_name);
}
+ if (found && targ->t_redirection != NULL) {
+ log_debugx("target \"%s\" contains luns, "
+ " but configured for redirection",
+ targ->t_name);
+ }
}
TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) {
assert(pg->pg_name != NULL);
@@ -1506,13 +1545,22 @@ conf_verify(struct conf *conf)
if (targ->t_portal_group == pg)
break;
}
- if (targ == NULL) {
+ if (pg->pg_redirection != NULL) {
+ if (targ != NULL) {
+ log_debugx("portal-group \"%s\" assigned "
+ "to target \"%s\", but configured "
+ "for redirection",
+ pg->pg_name, targ->t_name);
+ }
+ pg->pg_unassigned = false;
+ } else if (targ != NULL) {
+ pg->pg_unassigned = false;
+ } else {
if (strcmp(pg->pg_name, "default") != 0)
log_warnx("portal-group \"%s\" not assigned "
"to any target", pg->pg_name);
pg->pg_unassigned = true;
- } else
- pg->pg_unassigned = false;
+ }
}
TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) {
if (ag->ag_name == NULL)
diff --git a/usr.sbin/ctld/ctld.h b/usr.sbin/ctld/ctld.h
index e7b3642..600bd30 100644
--- a/usr.sbin/ctld/ctld.h
+++ b/usr.sbin/ctld/ctld.h
@@ -117,6 +117,7 @@ struct portal_group {
int pg_discovery_filter;
bool pg_unassigned;
TAILQ_HEAD(, portal) pg_portals;
+ char *pg_redirection;
uint16_t pg_tag;
};
@@ -151,6 +152,7 @@ struct target {
struct portal_group *t_portal_group;
char *t_name;
char *t_alias;
+ char *t_redirection;
};
struct isns {
@@ -301,6 +303,8 @@ int portal_group_add_listen(struct portal_group *pg,
const char *listen, bool iser);
int portal_group_set_filter(struct portal_group *pg,
const char *filter);
+int portal_group_set_redirection(struct portal_group *pg,
+ const char *addr);
int isns_new(struct conf *conf, const char *addr);
void isns_delete(struct isns *is);
@@ -312,6 +316,8 @@ struct target *target_new(struct conf *conf, const char *name);
void target_delete(struct target *target);
struct target *target_find(struct conf *conf,
const char *name);
+int target_set_redirection(struct target *target,
+ const char *addr);
struct lun *lun_new(struct target *target, int lun_id);
void lun_delete(struct lun *lun);
diff --git a/usr.sbin/ctld/login.c b/usr.sbin/ctld/login.c
index c59cea0..fc41f51 100644
--- a/usr.sbin/ctld/login.c
+++ b/usr.sbin/ctld/login.c
@@ -613,13 +613,72 @@ login_negotiate_key(struct pdu *request, const char *name,
}
static void
+login_redirect(struct pdu *request, const char *target_address)
+{
+ struct pdu *response;
+ struct iscsi_bhs_login_response *bhslr2;
+ struct keys *response_keys;
+
+ response = login_new_response(request);
+ login_set_csg(response, login_csg(request));
+ bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs;
+ bhslr2->bhslr_status_class = 0x01;
+ bhslr2->bhslr_status_detail = 0x01;
+
+ response_keys = keys_new();
+ keys_add(response_keys, "TargetAddress", target_address);
+
+ keys_save(response_keys, response);
+ pdu_send(response);
+ pdu_delete(response);
+ keys_delete(response_keys);
+}
+
+static bool
+login_portal_redirect(struct connection *conn, struct pdu *request)
+{
+ const struct portal_group *pg;
+
+ pg = conn->conn_portal->p_portal_group;
+ if (pg->pg_redirection == NULL)
+ return (false);
+
+ log_debugx("portal-group \"%s\" configured to redirect to %s",
+ pg->pg_name, pg->pg_redirection);
+ login_redirect(request, pg->pg_redirection);
+
+ return (true);
+}
+
+static bool
+login_target_redirect(struct connection *conn, struct pdu *request)
+{
+ const char *target_address;
+
+ assert(conn->conn_portal->p_portal_group->pg_redirection == NULL);
+
+ if (conn->conn_target == NULL)
+ return (false);
+
+ target_address = conn->conn_target->t_redirection;
+ if (target_address == NULL)
+ return (false);
+
+ log_debugx("target \"%s\" configured to redirect to %s",
+ conn->conn_target->t_name, target_address);
+ login_redirect(request, target_address);
+
+ return (true);
+}
+
+static void
login_negotiate(struct connection *conn, struct pdu *request)
{
struct pdu *response;
struct iscsi_bhs_login_response *bhslr2;
struct keys *request_keys, *response_keys;
int i;
- bool skipped_security;
+ bool redirected, skipped_security;
if (request == NULL) {
log_debugx("beginning operational parameter negotiation; "
@@ -629,6 +688,18 @@ login_negotiate(struct connection *conn, struct pdu *request)
} else
skipped_security = true;
+ /*
+ * RFC 3720, 10.13.5. Status-Class and Status-Detail, says
+ * the redirection SHOULD be accepted by the initiator before
+ * authentication, but MUST be be accepted afterwards; that's
+ * why we're doing it here and not earlier.
+ */
+ redirected = login_target_redirect(conn, request);
+ if (redirected) {
+ log_debugx("initiator redirected; exiting");
+ exit(0);
+ }
+
request_keys = keys_new();
keys_load(request_keys, request);
@@ -680,6 +751,7 @@ login(struct connection *conn)
struct portal_group *pg;
const char *initiator_name, *initiator_alias, *session_type,
*target_name, *auth_method;
+ bool redirected;
/*
* Handle the initial Login Request - figure out required authentication
@@ -722,6 +794,12 @@ login(struct connection *conn)
*/
setproctitle("%s (%s)", conn->conn_initiator_addr, conn->conn_initiator_name);
+ redirected = login_portal_redirect(conn, request);
+ if (redirected) {
+ log_debugx("initiator redirected; exiting");
+ exit(0);
+ }
+
initiator_alias = keys_find(request_keys, "InitiatorAlias");
if (initiator_alias != NULL)
conn->conn_initiator_alias = checked_strdup(initiator_alias);
diff --git a/usr.sbin/ctld/parse.y b/usr.sbin/ctld/parse.y
index c801ce6..a6519dd 100644
--- a/usr.sbin/ctld/parse.y
+++ b/usr.sbin/ctld/parse.y
@@ -61,7 +61,8 @@ extern void yyrestart(FILE *);
%token CLOSING_BRACKET DEBUG DEVICE_ID DISCOVERY_AUTH_GROUP DISCOVERY_FILTER
%token INITIATOR_NAME INITIATOR_PORTAL ISNS_SERVER ISNS_PERIOD ISNS_TIMEOUT
%token LISTEN LISTEN_ISER LUN MAXPROC OPENING_BRACKET OPTION
-%token PATH PIDFILE PORTAL_GROUP SEMICOLON SERIAL SIZE STR TARGET TIMEOUT
+%token PATH PIDFILE PORTAL_GROUP REDIRECT SEMICOLON SERIAL SIZE STR
+%token TARGET TIMEOUT
%union
{
@@ -338,6 +339,8 @@ portal_group_entry:
portal_group_listen
|
portal_group_listen_iser
+ |
+ portal_group_redirect
;
portal_group_discovery_auth_group: DISCOVERY_AUTH_GROUP STR
@@ -393,6 +396,17 @@ portal_group_listen_iser: LISTEN_ISER STR
}
;
+portal_group_redirect: REDIRECT STR
+ {
+ int error;
+
+ error = portal_group_set_redirection(portal_group, $2);
+ free($2);
+ if (error != 0)
+ return (1);
+ }
+ ;
+
target: TARGET target_name
OPENING_BRACKET target_entries CLOSING_BRACKET
{
@@ -433,6 +447,8 @@ target_entry:
|
target_portal_group
|
+ target_redirect
+ |
target_lun
;
@@ -635,6 +651,17 @@ target_portal_group: PORTAL_GROUP STR
}
;
+target_redirect: REDIRECT STR
+ {
+ int error;
+
+ error = target_set_redirection(target, $2);
+ free($2);
+ if (error != 0)
+ return (1);
+ }
+ ;
+
target_lun: LUN lun_number
OPENING_BRACKET lun_entries CLOSING_BRACKET
{
diff --git a/usr.sbin/ctld/token.l b/usr.sbin/ctld/token.l
index 822d1ac..d4bf823 100644
--- a/usr.sbin/ctld/token.l
+++ b/usr.sbin/ctld/token.l
@@ -72,6 +72,7 @@ isns-server { return ISNS_SERVER; }
isns-period { return ISNS_PERIOD; }
isns-timeout { return ISNS_TIMEOUT; }
portal-group { return PORTAL_GROUP; }
+redirect { return REDIRECT; }
serial { return SERIAL; }
size { return SIZE; }
target { return TARGET; }
OpenPOWER on IntegriCloud