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:
parent
ace7dfdc9c
commit
35a0788239
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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; }
|
||||
|
Loading…
Reference in New Issue
Block a user