freebsd-nq/usr.sbin/ctld/parse.y
Edward Tomasz Napierala 07b49a3eed Make it possible to set (via ctl.conf(5)) and query (via ctladm islist -v)
target iSCSI offload.  Add mechanism to query maximum receive data segment
size supported by chosen hardware offload module, and use it in ctld(8)
to determine the value to advertise to the other side.

MFC after:	1 month
Sponsored by:	The FreeBSD Foundation
2015-02-06 21:03:25 +00:00

1024 lines
18 KiB
Plaintext

%{
/*-
* Copyright (c) 2012 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Edward Tomasz Napierala under sponsorship
* from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/queue.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "ctld.h"
extern FILE *yyin;
extern char *yytext;
extern int lineno;
static struct conf *conf = NULL;
static struct auth_group *auth_group = NULL;
static struct portal_group *portal_group = NULL;
static struct target *target = NULL;
static struct lun *lun = NULL;
extern void yyerror(const char *);
extern int yylex(void);
extern void yyrestart(FILE *);
%}
%token ALIAS AUTH_GROUP AUTH_TYPE BACKEND BLOCKSIZE CHAP CHAP_MUTUAL
%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 OFFLOAD OPENING_BRACKET OPTION
%token PATH PIDFILE PORTAL_GROUP REDIRECT SEMICOLON SERIAL SIZE STR
%token TARGET TIMEOUT
%union
{
char *str;
}
%token <str> STR
%%
statements:
|
statements statement
|
statements statement SEMICOLON
;
statement:
debug
|
timeout
|
maxproc
|
pidfile
|
isns_server
|
isns_period
|
isns_timeout
|
auth_group
|
portal_group
|
lun
|
target
;
debug: DEBUG STR
{
uint64_t tmp;
if (expand_number($2, &tmp) != 0) {
yyerror("invalid numeric value");
free($2);
return (1);
}
conf->conf_debug = tmp;
}
;
timeout: TIMEOUT STR
{
uint64_t tmp;
if (expand_number($2, &tmp) != 0) {
yyerror("invalid numeric value");
free($2);
return (1);
}
conf->conf_timeout = tmp;
}
;
maxproc: MAXPROC STR
{
uint64_t tmp;
if (expand_number($2, &tmp) != 0) {
yyerror("invalid numeric value");
free($2);
return (1);
}
conf->conf_maxproc = tmp;
}
;
pidfile: PIDFILE STR
{
if (conf->conf_pidfile_path != NULL) {
log_warnx("pidfile specified more than once");
free($2);
return (1);
}
conf->conf_pidfile_path = $2;
}
;
isns_server: ISNS_SERVER STR
{
int error;
error = isns_new(conf, $2);
free($2);
if (error != 0)
return (1);
}
;
isns_period: ISNS_PERIOD STR
{
uint64_t tmp;
if (expand_number($2, &tmp) != 0) {
yyerror("invalid numeric value");
free($2);
return (1);
}
conf->conf_isns_period = tmp;
}
;
isns_timeout: ISNS_TIMEOUT STR
{
uint64_t tmp;
if (expand_number($2, &tmp) != 0) {
yyerror("invalid numeric value");
free($2);
return (1);
}
conf->conf_isns_timeout = tmp;
}
;
auth_group: AUTH_GROUP auth_group_name
OPENING_BRACKET auth_group_entries CLOSING_BRACKET
{
auth_group = NULL;
}
;
auth_group_name: STR
{
/*
* Make it possible to redefine default
* auth-group. but only once.
*/
if (strcmp($1, "default") == 0 &&
conf->conf_default_ag_defined == false) {
auth_group = auth_group_find(conf, $1);
conf->conf_default_ag_defined = true;
} else {
auth_group = auth_group_new(conf, $1);
}
free($1);
if (auth_group == NULL)
return (1);
}
;
auth_group_entries:
|
auth_group_entries auth_group_entry
|
auth_group_entries auth_group_entry SEMICOLON
;
auth_group_entry:
auth_group_auth_type
|
auth_group_chap
|
auth_group_chap_mutual
|
auth_group_initiator_name
|
auth_group_initiator_portal
;
auth_group_auth_type: AUTH_TYPE STR
{
int error;
error = auth_group_set_type(auth_group, $2);
free($2);
if (error != 0)
return (1);
}
;
auth_group_chap: CHAP STR STR
{
const struct auth *ca;
ca = auth_new_chap(auth_group, $2, $3);
free($2);
free($3);
if (ca == NULL)
return (1);
}
;
auth_group_chap_mutual: CHAP_MUTUAL STR STR STR STR
{
const struct auth *ca;
ca = auth_new_chap_mutual(auth_group, $2, $3, $4, $5);
free($2);
free($3);
free($4);
free($5);
if (ca == NULL)
return (1);
}
;
auth_group_initiator_name: INITIATOR_NAME STR
{
const struct auth_name *an;
an = auth_name_new(auth_group, $2);
free($2);
if (an == NULL)
return (1);
}
;
auth_group_initiator_portal: INITIATOR_PORTAL STR
{
const struct auth_portal *ap;
ap = auth_portal_new(auth_group, $2);
free($2);
if (ap == NULL)
return (1);
}
;
portal_group: PORTAL_GROUP portal_group_name
OPENING_BRACKET portal_group_entries CLOSING_BRACKET
{
portal_group = NULL;
}
;
portal_group_name: STR
{
/*
* Make it possible to redefine default
* portal-group. but only once.
*/
if (strcmp($1, "default") == 0 &&
conf->conf_default_pg_defined == false) {
portal_group = portal_group_find(conf, $1);
conf->conf_default_pg_defined = true;
} else {
portal_group = portal_group_new(conf, $1);
}
free($1);
if (portal_group == NULL)
return (1);
}
;
portal_group_entries:
|
portal_group_entries portal_group_entry
|
portal_group_entries portal_group_entry SEMICOLON
;
portal_group_entry:
portal_group_discovery_auth_group
|
portal_group_discovery_filter
|
portal_group_listen
|
portal_group_listen_iser
|
portal_group_redirect
;
portal_group_discovery_auth_group: DISCOVERY_AUTH_GROUP STR
{
if (portal_group->pg_discovery_auth_group != NULL) {
log_warnx("discovery-auth-group for portal-group "
"\"%s\" specified more than once",
portal_group->pg_name);
return (1);
}
portal_group->pg_discovery_auth_group =
auth_group_find(conf, $2);
if (portal_group->pg_discovery_auth_group == NULL) {
log_warnx("unknown discovery-auth-group \"%s\" "
"for portal-group \"%s\"",
$2, portal_group->pg_name);
return (1);
}
free($2);
}
;
portal_group_discovery_filter: DISCOVERY_FILTER STR
{
int error;
error = portal_group_set_filter(portal_group, $2);
free($2);
if (error != 0)
return (1);
}
;
portal_group_listen: LISTEN STR
{
int error;
error = portal_group_add_listen(portal_group, $2, false);
free($2);
if (error != 0)
return (1);
}
;
portal_group_listen_iser: LISTEN_ISER STR
{
int error;
error = portal_group_add_listen(portal_group, $2, true);
free($2);
if (error != 0)
return (1);
}
;
portal_group_redirect: REDIRECT STR
{
int error;
error = portal_group_set_redirection(portal_group, $2);
free($2);
if (error != 0)
return (1);
}
;
lun: LUN lun_name
OPENING_BRACKET lun_entries CLOSING_BRACKET
{
lun = NULL;
}
;
lun_name: STR
{
lun = lun_new(conf, $1);
free($1);
if (lun == NULL)
return (1);
}
;
target: TARGET target_name
OPENING_BRACKET target_entries CLOSING_BRACKET
{
target = NULL;
}
;
target_name: STR
{
target = target_new(conf, $1);
free($1);
if (target == NULL)
return (1);
}
;
target_entries:
|
target_entries target_entry
|
target_entries target_entry SEMICOLON
;
target_entry:
target_alias
|
target_auth_group
|
target_auth_type
|
target_chap
|
target_chap_mutual
|
target_initiator_name
|
target_initiator_portal
|
target_offload
|
target_portal_group
|
target_redirect
|
target_lun
|
target_lun_ref
;
target_alias: ALIAS STR
{
if (target->t_alias != NULL) {
log_warnx("alias for target \"%s\" "
"specified more than once", target->t_name);
return (1);
}
target->t_alias = $2;
}
;
target_auth_group: AUTH_GROUP STR
{
if (target->t_auth_group != NULL) {
if (target->t_auth_group->ag_name != NULL)
log_warnx("auth-group for target \"%s\" "
"specified more than once", target->t_name);
else
log_warnx("cannot use both auth-group and explicit "
"authorisations for target \"%s\"",
target->t_name);
return (1);
}
target->t_auth_group = auth_group_find(conf, $2);
if (target->t_auth_group == NULL) {
log_warnx("unknown auth-group \"%s\" for target "
"\"%s\"", $2, target->t_name);
return (1);
}
free($2);
}
;
target_auth_type: AUTH_TYPE STR
{
int error;
if (target->t_auth_group != NULL) {
if (target->t_auth_group->ag_name != NULL) {
log_warnx("cannot use both auth-group and "
"auth-type for target \"%s\"",
target->t_name);
return (1);
}
} else {
target->t_auth_group = auth_group_new(conf, NULL);
if (target->t_auth_group == NULL) {
free($2);
return (1);
}
target->t_auth_group->ag_target = target;
}
error = auth_group_set_type(target->t_auth_group, $2);
free($2);
if (error != 0)
return (1);
}
;
target_chap: CHAP STR STR
{
const struct auth *ca;
if (target->t_auth_group != NULL) {
if (target->t_auth_group->ag_name != NULL) {
log_warnx("cannot use both auth-group and "
"chap for target \"%s\"",
target->t_name);
free($2);
free($3);
return (1);
}
} else {
target->t_auth_group = auth_group_new(conf, NULL);
if (target->t_auth_group == NULL) {
free($2);
free($3);
return (1);
}
target->t_auth_group->ag_target = target;
}
ca = auth_new_chap(target->t_auth_group, $2, $3);
free($2);
free($3);
if (ca == NULL)
return (1);
}
;
target_chap_mutual: CHAP_MUTUAL STR STR STR STR
{
const struct auth *ca;
if (target->t_auth_group != NULL) {
if (target->t_auth_group->ag_name != NULL) {
log_warnx("cannot use both auth-group and "
"chap-mutual for target \"%s\"",
target->t_name);
free($2);
free($3);
free($4);
free($5);
return (1);
}
} else {
target->t_auth_group = auth_group_new(conf, NULL);
if (target->t_auth_group == NULL) {
free($2);
free($3);
free($4);
free($5);
return (1);
}
target->t_auth_group->ag_target = target;
}
ca = auth_new_chap_mutual(target->t_auth_group,
$2, $3, $4, $5);
free($2);
free($3);
free($4);
free($5);
if (ca == NULL)
return (1);
}
;
target_initiator_name: INITIATOR_NAME STR
{
const struct auth_name *an;
if (target->t_auth_group != NULL) {
if (target->t_auth_group->ag_name != NULL) {
log_warnx("cannot use both auth-group and "
"initiator-name for target \"%s\"",
target->t_name);
free($2);
return (1);
}
} else {
target->t_auth_group = auth_group_new(conf, NULL);
if (target->t_auth_group == NULL) {
free($2);
return (1);
}
target->t_auth_group->ag_target = target;
}
an = auth_name_new(target->t_auth_group, $2);
free($2);
if (an == NULL)
return (1);
}
;
target_initiator_portal: INITIATOR_PORTAL STR
{
const struct auth_portal *ap;
if (target->t_auth_group != NULL) {
if (target->t_auth_group->ag_name != NULL) {
log_warnx("cannot use both auth-group and "
"initiator-portal for target \"%s\"",
target->t_name);
free($2);
return (1);
}
} else {
target->t_auth_group = auth_group_new(conf, NULL);
if (target->t_auth_group == NULL) {
free($2);
return (1);
}
target->t_auth_group->ag_target = target;
}
ap = auth_portal_new(target->t_auth_group, $2);
free($2);
if (ap == NULL)
return (1);
}
;
target_offload: OFFLOAD STR
{
int error;
error = target_set_offload(target, $2);
free($2);
if (error != 0)
return (1);
}
;
target_portal_group: PORTAL_GROUP STR STR
{
struct portal_group *tpg;
struct auth_group *tag;
struct port *tp;
tpg = portal_group_find(conf, $2);
if (tpg == NULL) {
log_warnx("unknown portal-group \"%s\" for target "
"\"%s\"", $2, target->t_name);
free($2);
free($3);
return (1);
}
tag = auth_group_find(conf, $3);
if (tag == NULL) {
log_warnx("unknown auth-group \"%s\" for target "
"\"%s\"", $3, target->t_name);
free($2);
free($3);
return (1);
}
tp = port_new(conf, target, tpg);
if (tp == NULL) {
log_warnx("can't link portal-group \"%s\" to target "
"\"%s\"", $2, target->t_name);
free($2);
return (1);
}
tp->p_auth_group = tag;
free($2);
free($3);
}
| PORTAL_GROUP STR
{
struct portal_group *tpg;
struct port *tp;
tpg = portal_group_find(conf, $2);
if (tpg == NULL) {
log_warnx("unknown portal-group \"%s\" for target "
"\"%s\"", $2, target->t_name);
free($2);
return (1);
}
tp = port_new(conf, target, tpg);
if (tp == NULL) {
log_warnx("can't link portal-group \"%s\" to target "
"\"%s\"", $2, target->t_name);
free($2);
return (1);
}
free($2);
}
;
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
{
lun = NULL;
}
;
lun_number: STR
{
uint64_t tmp;
char *name;
if (expand_number($1, &tmp) != 0) {
yyerror("invalid numeric value");
free($1);
return (1);
}
asprintf(&name, "%s,lun,%ju", target->t_name, tmp);
lun = lun_new(conf, name);
if (lun == NULL)
return (1);
lun_set_scsiname(lun, name);
target->t_luns[tmp] = lun;
}
;
target_lun_ref: LUN STR STR
{
uint64_t tmp;
if (expand_number($2, &tmp) != 0) {
yyerror("invalid numeric value");
free($2);
free($3);
return (1);
}
free($2);
lun = lun_find(conf, $3);
free($3);
if (lun == NULL)
return (1);
target->t_luns[tmp] = lun;
}
;
lun_entries:
|
lun_entries lun_entry
|
lun_entries lun_entry SEMICOLON
;
lun_entry:
lun_backend
|
lun_blocksize
|
lun_device_id
|
lun_option
|
lun_path
|
lun_serial
|
lun_size
;
lun_backend: BACKEND STR
{
if (lun->l_backend != NULL) {
log_warnx("backend for lun \"%s\" "
"specified more than once",
lun->l_name);
free($2);
return (1);
}
lun_set_backend(lun, $2);
free($2);
}
;
lun_blocksize: BLOCKSIZE STR
{
uint64_t tmp;
if (expand_number($2, &tmp) != 0) {
yyerror("invalid numeric value");
free($2);
return (1);
}
if (lun->l_blocksize != 0) {
log_warnx("blocksize for lun \"%s\" "
"specified more than once",
lun->l_name);
return (1);
}
lun_set_blocksize(lun, tmp);
}
;
lun_device_id: DEVICE_ID STR
{
if (lun->l_device_id != NULL) {
log_warnx("device_id for lun \"%s\" "
"specified more than once",
lun->l_name);
free($2);
return (1);
}
lun_set_device_id(lun, $2);
free($2);
}
;
lun_option: OPTION STR STR
{
struct lun_option *clo;
clo = lun_option_new(lun, $2, $3);
free($2);
free($3);
if (clo == NULL)
return (1);
}
;
lun_path: PATH STR
{
if (lun->l_path != NULL) {
log_warnx("path for lun \"%s\" "
"specified more than once",
lun->l_name);
free($2);
return (1);
}
lun_set_path(lun, $2);
free($2);
}
;
lun_serial: SERIAL STR
{
if (lun->l_serial != NULL) {
log_warnx("serial for lun \"%s\" "
"specified more than once",
lun->l_name);
free($2);
return (1);
}
lun_set_serial(lun, $2);
free($2);
}
;
lun_size: SIZE STR
{
uint64_t tmp;
if (expand_number($2, &tmp) != 0) {
yyerror("invalid numeric value");
free($2);
return (1);
}
if (lun->l_size != 0) {
log_warnx("size for lun \"%s\" "
"specified more than once",
lun->l_name);
return (1);
}
lun_set_size(lun, tmp);
}
;
%%
void
yyerror(const char *str)
{
log_warnx("error in configuration file at line %d near '%s': %s",
lineno, yytext, str);
}
static void
check_perms(const char *path)
{
struct stat sb;
int error;
error = stat(path, &sb);
if (error != 0) {
log_warn("stat");
return;
}
if (sb.st_mode & S_IWOTH) {
log_warnx("%s is world-writable", path);
} else if (sb.st_mode & S_IROTH) {
log_warnx("%s is world-readable", path);
} else if (sb.st_mode & S_IXOTH) {
/*
* Ok, this one doesn't matter, but still do it,
* just for consistency.
*/
log_warnx("%s is world-executable", path);
}
/*
* XXX: Should we also check for owner != 0?
*/
}
struct conf *
conf_new_from_file(const char *path)
{
struct auth_group *ag;
struct portal_group *pg;
int error;
log_debugx("obtaining configuration from %s", path);
conf = conf_new();
ag = auth_group_new(conf, "default");
assert(ag != NULL);
ag = auth_group_new(conf, "no-authentication");
assert(ag != NULL);
ag->ag_type = AG_TYPE_NO_AUTHENTICATION;
ag = auth_group_new(conf, "no-access");
assert(ag != NULL);
ag->ag_type = AG_TYPE_DENY;
pg = portal_group_new(conf, "default");
assert(pg != NULL);
yyin = fopen(path, "r");
if (yyin == NULL) {
log_warn("unable to open configuration file %s", path);
conf_delete(conf);
return (NULL);
}
check_perms(path);
lineno = 1;
yyrestart(yyin);
error = yyparse();
auth_group = NULL;
portal_group = NULL;
target = NULL;
lun = NULL;
fclose(yyin);
if (error != 0) {
conf_delete(conf);
return (NULL);
}
if (conf->conf_default_ag_defined == false) {
log_debugx("auth-group \"default\" not defined; "
"going with defaults");
ag = auth_group_find(conf, "default");
assert(ag != NULL);
ag->ag_type = AG_TYPE_DENY;
}
if (conf->conf_default_pg_defined == false) {
log_debugx("portal-group \"default\" not defined; "
"going with defaults");
pg = portal_group_find(conf, "default");
assert(pg != NULL);
portal_group_add_listen(pg, "0.0.0.0:3260", false);
portal_group_add_listen(pg, "[::]:3260", false);
}
conf->conf_kernel_port_on = true;
error = conf_verify(conf);
if (error != 0) {
conf_delete(conf);
return (NULL);
}
return (conf);
}