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
This commit is contained in:
trasz 2014-12-09 11:50:50 +00:00
parent ace7dfdc9c
commit 35a0788239
6 changed files with 183 additions and 7 deletions

View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -612,6 +612,65 @@ 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)
{
@ -619,7 +678,7 @@ login_negotiate(struct connection *conn, struct pdu *request)
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);

View File

@ -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
{

View File

@ -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; }